tizen 2.4 release accepted/tizen_2.4_mobile tizen_2.4 accepted/tizen/2.4/mobile/20151029.040634 submit/tizen_2.4/20151028.062857 tizen_2.4_mobile_release
authorjk7744.park <jk7744.park@samsung.com>
Sat, 24 Oct 2015 06:54:41 +0000 (15:54 +0900)
committerjk7744.park <jk7744.park@samsung.com>
Sat, 24 Oct 2015 06:54:41 +0000 (15:54 +0900)
1231 files changed:
.gitignore
Config.in
INSTALL
LICENSE
Makefile
Makefile.custom
Makefile.flags
Makefile.help
README
TODO
applets/.gitignore
applets/Kbuild.src
applets/applet_tables.c
applets/applets.c
applets/busybox.mkll
applets/busybox.mksuid [new file with mode: 0755]
applets/individual.c
applets/install.sh
applets/usage.c
applets/usage_compressed
applets/usage_pod.c
applets_sh/README [new file with mode: 0644]
applets_sh/dos2unix [new file with mode: 0755]
applets_sh/nologin [new file with mode: 0755]
applets_sh/tac [new file with mode: 0755]
applets_sh/unix2dos [new file with mode: 0755]
archival/Config.src
archival/Kbuild.src
archival/ar.c
archival/bbunzip.c
archival/bzip2.c
archival/cpio.c
archival/dpkg.c
archival/dpkg_deb.c
archival/gzip.c
archival/libarchive/Kbuild.src [new file with mode: 0644]
archival/libarchive/bz/LICENSE [moved from archival/bz/LICENSE with 100% similarity]
archival/libarchive/bz/README [moved from archival/bz/README with 100% similarity]
archival/libarchive/bz/blocksort.c [moved from archival/bz/blocksort.c with 99% similarity]
archival/libarchive/bz/bzlib.c [moved from archival/bz/bzlib.c with 98% similarity]
archival/libarchive/bz/bzlib.h [moved from archival/bz/bzlib.h with 100% similarity]
archival/libarchive/bz/bzlib_private.h [moved from archival/bz/bzlib_private.h with 99% similarity]
archival/libarchive/bz/compress.c [moved from archival/bz/compress.c with 95% similarity]
archival/libarchive/bz/huffman.c [moved from archival/bz/huffman.c with 99% similarity]
archival/libarchive/data_align.c [moved from archival/libunarchive/data_align.c with 75% similarity]
archival/libarchive/data_extract_all.c [moved from archival/libunarchive/data_extract_all.c with 86% similarity]
archival/libarchive/data_extract_to_command.c [moved from archival/libunarchive/data_extract_to_command.c with 84% similarity]
archival/libarchive/data_extract_to_stdout.c [moved from archival/libunarchive/data_extract_to_stdout.c with 69% similarity]
archival/libarchive/data_skip.c [moved from archival/libunarchive/data_skip.c with 65% similarity]
archival/libarchive/decompress_bunzip2.c [moved from archival/libunarchive/decompress_bunzip2.c with 66% similarity]
archival/libarchive/decompress_gunzip.c [moved from archival/libunarchive/decompress_unzip.c with 96% similarity]
archival/libarchive/decompress_uncompress.c [moved from archival/libunarchive/decompress_uncompress.c with 85% similarity]
archival/libarchive/decompress_unlzma.c [moved from archival/libunarchive/decompress_unlzma.c with 95% similarity]
archival/libarchive/decompress_unxz.c [moved from archival/libunarchive/decompress_unxz.c with 54% similarity]
archival/libarchive/filter_accept_all.c [moved from archival/libunarchive/filter_accept_all.c with 75% similarity]
archival/libarchive/filter_accept_list.c [moved from archival/libunarchive/filter_accept_list.c with 78% similarity]
archival/libarchive/filter_accept_list_reassign.c [moved from archival/libunarchive/filter_accept_list_reassign.c with 93% similarity]
archival/libarchive/filter_accept_reject_list.c [moved from archival/libunarchive/filter_accept_reject_list.c with 72% similarity]
archival/libarchive/find_list_entry.c [moved from archival/libunarchive/find_list_entry.c with 91% similarity]
archival/libarchive/get_header_ar.c [moved from archival/libunarchive/get_header_ar.c with 75% similarity]
archival/libarchive/get_header_cpio.c [moved from archival/libunarchive/get_header_cpio.c with 98% similarity]
archival/libarchive/get_header_tar.c [moved from archival/libunarchive/get_header_tar.c with 78% similarity]
archival/libarchive/get_header_tar_bz2.c [moved from archival/libunarchive/get_header_tar_bz2.c with 65% similarity]
archival/libarchive/get_header_tar_gz.c [new file with mode: 0644]
archival/libarchive/get_header_tar_lzma.c [moved from archival/libunarchive/get_header_tar_lzma.c with 72% similarity]
archival/libarchive/header_list.c [moved from archival/libunarchive/header_list.c with 66% similarity]
archival/libarchive/header_skip.c [moved from archival/libunarchive/header_skip.c with 55% similarity]
archival/libarchive/header_verbose_list.c [moved from archival/libunarchive/header_verbose_list.c with 93% similarity]
archival/libarchive/init_handle.c [moved from archival/libunarchive/init_handle.c with 82% similarity]
archival/libarchive/liblzo.h [moved from archival/liblzo.h with 100% similarity]
archival/libarchive/lzo1x_1.c [moved from archival/lzo1x_1.c with 100% similarity]
archival/libarchive/lzo1x_1o.c [moved from archival/lzo1x_1o.c with 100% similarity]
archival/libarchive/lzo1x_9x.c [moved from archival/lzo1x_9x.c with 98% similarity]
archival/libarchive/lzo1x_c.c [moved from archival/lzo1x_c.c with 99% similarity]
archival/libarchive/lzo1x_d.c [moved from archival/lzo1x_d.c with 99% similarity]
archival/libarchive/open_transformer.c [new file with mode: 0644]
archival/libarchive/seek_by_jump.c [moved from archival/libunarchive/seek_by_jump.c with 72% similarity]
archival/libarchive/seek_by_read.c [moved from archival/libunarchive/seek_by_read.c with 73% similarity]
archival/libarchive/unpack_ar_archive.c [moved from archival/libunarchive/unpack_ar_archive.c with 79% similarity]
archival/libarchive/unxz/README [moved from archival/libunarchive/unxz/README with 97% similarity]
archival/libarchive/unxz/xz.h [moved from archival/libunarchive/unxz/xz.h with 96% similarity]
archival/libarchive/unxz/xz_config.h [moved from archival/libunarchive/unxz/xz_config.h with 100% similarity]
archival/libarchive/unxz/xz_dec_bcj.c [moved from archival/libunarchive/unxz/xz_dec_bcj.c with 92% similarity]
archival/libarchive/unxz/xz_dec_lzma2.c [moved from archival/libunarchive/unxz/xz_dec_lzma2.c with 99% similarity]
archival/libarchive/unxz/xz_dec_stream.c [moved from archival/libunarchive/unxz/xz_dec_stream.c with 100% similarity]
archival/libarchive/unxz/xz_lzma2.h [moved from archival/libunarchive/unxz/xz_lzma2.h with 100% similarity]
archival/libarchive/unxz/xz_private.h [moved from archival/libunarchive/unxz/xz_private.h with 100% similarity]
archival/libarchive/unxz/xz_stream.h [moved from archival/libunarchive/unxz/xz_stream.h with 72% similarity]
archival/libunarchive/Kbuild.src [deleted file]
archival/libunarchive/get_header_tar_gz.c [deleted file]
archival/libunarchive/open_transformer.c [deleted file]
archival/lzop.c
archival/rpm.c
archival/rpm.h
archival/rpm2cpio.c
archival/tar.c
archival/unzip.c
configs/TEST_nommu_defconfig [moved from TEST_config_nommu with 99% similarity]
configs/TEST_noprintf_defconfig [moved from TEST_config_noprintf with 99% similarity]
configs/TEST_rh9_defconfig [moved from TEST_config_rh9 with 99% similarity]
configs/android2_defconfig [moved from debian/config/pkg/udeb with 57% similarity]
configs/android_defconfig [new file with mode: 0644]
configs/android_ndk_defconfig [moved from packaging/busybox-dahlia.config with 60% similarity]
configs/cygwin_defconfig [new file with mode: 0644]
configs/freebsd_defconfig [moved from debian/config/pkg/deb with 69% similarity]
console-tools/Config.src
console-tools/Kbuild.src
console-tools/chvt.c
console-tools/clear.c
console-tools/deallocvt.c
console-tools/dumpkmap.c
console-tools/fgconsole.c
console-tools/kbd_mode.c
console-tools/loadfont.c
console-tools/loadkmap.c
console-tools/openvt.c
console-tools/reset.c
console-tools/resize.c
console-tools/setconsole.c
console-tools/setkeycodes.c
console-tools/setlogcons.c
console-tools/showkey.c
coreutils/Config.src
coreutils/Kbuild.src
coreutils/basename.c
coreutils/cal.c
coreutils/cat.c
coreutils/catv.c
coreutils/chgrp.c
coreutils/chmod.c
coreutils/chown.c
coreutils/chroot.c
coreutils/cksum.c
coreutils/comm.c
coreutils/cp.c
coreutils/cut.c
coreutils/date.c
coreutils/dd.c
coreutils/df.c
coreutils/dirname.c
coreutils/dos2unix.c
coreutils/du.c
coreutils/echo.c
coreutils/env.c
coreutils/expand.c
coreutils/expr.c
coreutils/false.c
coreutils/fold.c
coreutils/fsync.c
coreutils/head.c
coreutils/hostid.c
coreutils/id.c
coreutils/install.c
coreutils/length.c.disabled [moved from coreutils/length.c with 57% similarity]
coreutils/libcoreutils/Kbuild.src
coreutils/libcoreutils/coreutils.h
coreutils/ln.c
coreutils/logname.c
coreutils/ls.c
coreutils/md5_sha1_sum.c
coreutils/mkdir.c
coreutils/mkfifo.c
coreutils/mknod.c
coreutils/mv.c
coreutils/nice.c
coreutils/nohup.c
coreutils/od.c
coreutils/od_bloaty.c
coreutils/printenv.c
coreutils/printf.c
coreutils/pwd.c
coreutils/readlink.c
coreutils/realpath.c
coreutils/rm.c
coreutils/rmdir.c
coreutils/seq.c
coreutils/sleep.c
coreutils/sort.c
coreutils/split.c
coreutils/stat.c
coreutils/stty.c
coreutils/sum.c
coreutils/sync.c
coreutils/tac.c
coreutils/tail.c
coreutils/tee.c
coreutils/test.c
coreutils/test_ptr_hack.c
coreutils/touch.c
coreutils/tr.c
coreutils/true.c
coreutils/tty.c
coreutils/uname.c
coreutils/uniq.c
coreutils/usleep.c
coreutils/uudecode.c
coreutils/uuencode.c
coreutils/wc.c
coreutils/who.c
coreutils/whoami.c
coreutils/yes.c
debian/bin/genorig.py [deleted file]
debian/busybox-klogd.service [deleted file]
debian/busybox-static.install [deleted file]
debian/busybox-symlinks-adduser.links [deleted file]
debian/busybox-symlinks-adjtimex.links [deleted file]
debian/busybox-symlinks-binutils.links [deleted file]
debian/busybox-symlinks-bridge-utils.links [deleted file]
debian/busybox-symlinks-bsdmainutils.links [deleted file]
debian/busybox-symlinks-busybox.links [deleted file]
debian/busybox-symlinks-bzip2.links [deleted file]
debian/busybox-symlinks-console-tools.links [deleted file]
debian/busybox-symlinks-cpio.links [deleted file]
debian/busybox-symlinks-cpio.postinst [deleted file]
debian/busybox-symlinks-cpio.prerm [deleted file]
debian/busybox-symlinks-cron.links [deleted file]
debian/busybox-symlinks-daemontools.links [deleted file]
debian/busybox-symlinks-dc.links [deleted file]
debian/busybox-symlinks-dnsutils.links [deleted file]
debian/busybox-symlinks-dosfstools.links [deleted file]
debian/busybox-symlinks-ed.links [deleted file]
debian/busybox-symlinks-eject.links [deleted file]
debian/busybox-symlinks-fbset.links [deleted file]
debian/busybox-symlinks-fdflush.links [deleted file]
debian/busybox-symlinks-hdparm.links [deleted file]
debian/busybox-symlinks-ifupdown.dirs [deleted file]
debian/busybox-symlinks-ifupdown.links [deleted file]
debian/busybox-symlinks-initscripts.links [deleted file]
debian/busybox-symlinks-ipcalc.links [deleted file]
debian/busybox-symlinks-iproute.links [deleted file]
debian/busybox-symlinks-ipsvd.links [deleted file]
debian/busybox-symlinks-iputils-arping.links [deleted file]
debian/busybox-symlinks-iputils-ping.links [deleted file]
debian/busybox-symlinks-klogd.links [deleted file]
debian/busybox-symlinks-loadlin.links [deleted file]
debian/busybox-symlinks-lrzsz.links [deleted file]
debian/busybox-symlinks-lzma.links [deleted file]
debian/busybox-symlinks-lzop.links [deleted file]
debian/busybox-symlinks-module-init-tools.links [deleted file]
debian/busybox-symlinks-mtd-utils.links [deleted file]
debian/busybox-symlinks-net-tools.links [deleted file]
debian/busybox-symlinks-openbsd-inetd.links [deleted file]
debian/busybox-symlinks-passwd.links [deleted file]
debian/busybox-symlinks-patch.links [deleted file]
debian/busybox-symlinks-ppp.links [deleted file]
debian/busybox-symlinks-procps.links [deleted file]
debian/busybox-symlinks-psmisc.links [deleted file]
debian/busybox-symlinks-rdate.links [deleted file]
debian/busybox-symlinks-realpath.links [deleted file]
debian/busybox-symlinks-rpm.links [deleted file]
debian/busybox-symlinks-runit.links [deleted file]
debian/busybox-symlinks-sharutils.links [deleted file]
debian/busybox-symlinks-ssmtp.links [deleted file]
debian/busybox-symlinks-sysklogd.links [deleted file]
debian/busybox-symlinks-telnetd.links [deleted file]
debian/busybox-symlinks-tftp.links [deleted file]
debian/busybox-symlinks-time.links [deleted file]
debian/busybox-symlinks-tofrodos.links [deleted file]
debian/busybox-symlinks-udhcpc.links [deleted file]
debian/busybox-symlinks-udhcpd.links [deleted file]
debian/busybox-symlinks-unzip.links [deleted file]
debian/busybox-symlinks-vlan.links [deleted file]
debian/busybox-symlinks-vlock.links [deleted file]
debian/busybox-symlinks-watchdog.links [deleted file]
debian/busybox-symlinks-wget.links [deleted file]
debian/busybox-symlinks-xterm.links [deleted file]
debian/busybox-symlinks-zcip.links [deleted file]
debian/busybox-syslogd.busybox-klogd.init [deleted file]
debian/busybox-syslogd.default [deleted file]
debian/busybox-syslogd.init [deleted file]
debian/busybox-syslogd.service [deleted file]
debian/busybox-systemd-klogd.install [deleted file]
debian/busybox-systemd-klogd.links [deleted file]
debian/busybox-systemd-sysklogd.install [deleted file]
debian/busybox-systemd-sysklogd.links [deleted file]
debian/busybox-udeb.install [deleted file]
debian/busybox.dirs [deleted file]
debian/busybox.install [deleted file]
debian/busybox.links [deleted file]
debian/busybox.postinst [deleted file]
debian/busybox.prerm [deleted file]
debian/changelog [deleted file]
debian/compat [deleted file]
debian/config/config.maemo [deleted file]
debian/config/os/hurd [deleted file]
debian/config/os/kfreebsd [deleted file]
debian/config/os/linux [deleted file]
debian/control [deleted file]
debian/copyright [deleted file]
debian/local/bash [deleted file]
debian/local/faketrue [deleted file]
debian/local/tempfile [deleted file]
debian/patches/05thumb.dpatch [deleted file]
debian/patches/06ls.dpatch [deleted file]
debian/patches/applets-fallback.patch [deleted file]
debian/patches/blockdev.patch [deleted file]
debian/patches/bootchartd-mounting-tmpfs-is-Linux-specific.patch [deleted file]
debian/patches/busybox-zero-ifr.ifr_hwaddr.sa_data.patch [deleted file]
debian/patches/cttyhack-serial-console-detection-is-Linux-specific.patch [deleted file]
debian/patches/debian-changes-1:1.17.1-10 [deleted file]
debian/patches/doc-man-name.patch [deleted file]
debian/patches/init-console-CRTSCTS.patch [deleted file]
debian/patches/init-console.patch [deleted file]
debian/patches/init-halt-portability-improvements.patch [deleted file]
debian/patches/init-loginutils-termios-portability-fixes.patch [deleted file]
debian/patches/init-make-the-initial-TERM-value-configurable.patch [deleted file]
debian/patches/klogd-make-it-work-on-non-linux-systems.patch [deleted file]
debian/patches/less-remove-misguided-dependency-on-PLATFORM_LINUX.patch [deleted file]
debian/patches/libbb-conditionalize-AF_-usage-in-error-reporting.patch [deleted file]
debian/patches/libbb.h-add-device-names-for-Hurd-and-FreeBSD.patch [deleted file]
debian/patches/make_gen_build_files_skip_quilt.patch [deleted file]
debian/patches/make_unicode_printable.patch [deleted file]
debian/patches/mark-Linux-specific-configuration-options.patch [deleted file]
debian/patches/mkdir-fix-p-on-FreeBSD.patch [deleted file]
debian/patches/readlink-use-xmalloc_realpath.patch [deleted file]
debian/patches/series [deleted file]
debian/patches/shell-ash-export-HOME.patch [deleted file]
debian/patches/shell-hist.patch [deleted file]
debian/patches/smack-busybox-1.17.1.patch [deleted file]
debian/patches/smack-conflict-with-selinux.patch [deleted file]
debian/patches/strip.patch [deleted file]
debian/patches/stty-sort-out-preprocessor-conditionals.patch [deleted file]
debian/patches/swaponoff-FreeBSD-support.patch [deleted file]
debian/patches/tcpsvd-udpsvd-conditionalize-usage-of-SO_ORIGINAL_DS.patch [deleted file]
debian/patches/top-display-rss.patch [deleted file]
debian/patches/u-mount-FreeBSD-support.patch [deleted file]
debian/patches/udhcpc-fast-request.patch [deleted file]
debian/patches/update-scripts-kconfig-_shipped.patch [deleted file]
debian/patches/version.patch [deleted file]
debian/patches/vlock-disable-linux-console-calls-on-other-systems.patch [deleted file]
debian/rules [deleted file]
debian/scripts/README [deleted file]
debian/scripts/busybox-notes.txt [deleted file]
debian/scripts/busybox-symlinks-ifupdown.dirs [deleted file]
debian/scripts/busybox.dirs [deleted file]
debian/scripts/check-links.py [deleted file]
debian/scripts/create-control.py [deleted file]
debian/scripts/debian-mappings.txt [deleted file]
debian/sfdisk/Makefile [deleted file]
debian/sfdisk/common.h [deleted file]
debian/sfdisk/defines.h [deleted file]
debian/sfdisk/disksize.c [deleted file]
debian/sfdisk/i386_sys_types.c [deleted file]
debian/sfdisk/nls.h [deleted file]
debian/sfdisk/partname.c [deleted file]
debian/sfdisk/sfdisk.c [deleted file]
debian/sfdisk/xstrncpy.c [deleted file]
debian/sfdisk/xstrncpy.h [deleted file]
debian/source/format [deleted file]
debian/tree/busybox-udeb/usr/share/udhcpc/default.script [deleted file]
debian/tree/udhcpc/usr/share/udhcpc/default.script [deleted file]
debian/tree/udhcpd/etc/default/udhcpd [deleted file]
debian/tree/udhcpd/etc/init.d/udhcpd [deleted file]
debian/tree/udhcpd/etc/udhcpd.conf [deleted file]
debian/tree/udhcpd/usr/share/man/man5/udhcpd.conf.5 [deleted file]
debian/udhcpc.install [deleted file]
debian/udhcpd.install [deleted file]
debianutils/Config.src
debianutils/Kbuild.src
debianutils/mktemp.c
debianutils/pipe_progress.c
debianutils/run_parts.c
debianutils/start_stop_daemon.c
debianutils/which.c
docs/.gitignore
docs/busybox_footer.pod
docs/busybox_header.pod
docs/cgi/cl.html
docs/cgi/env.html
docs/cgi/in.html
docs/cgi/interface.html
docs/cgi/out.html
docs/contributing.txt
docs/ctty.htm
docs/ifupdown_design.txt
docs/keep_data_small.txt
docs/mdev.txt
docs/new-applet-HOWTO.txt
docs/nofork_noexec.txt
docs/posix_conformance.txt
docs/smallint.txt [new file with mode: 0644]
docs/style-guide.txt
docs/syslog.conf.txt [new file with mode: 0644]
docs/tcp.txt [new file with mode: 0644]
docs/unicode.txt
e2fsprogs/Config.src
e2fsprogs/Kbuild.src
e2fsprogs/chattr.c
e2fsprogs/e2fs_lib.c
e2fsprogs/e2fs_lib.h
e2fsprogs/fsck.c
e2fsprogs/lsattr.c
e2fsprogs/old_e2fsprogs/Kbuild.src
e2fsprogs/old_e2fsprogs/blkid/Kbuild.src
e2fsprogs/old_e2fsprogs/blkid/blkidP.h
e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c
e2fsprogs/old_e2fsprogs/blkid/cache.c
e2fsprogs/old_e2fsprogs/blkid/dev.c
e2fsprogs/old_e2fsprogs/blkid/probe.c
e2fsprogs/old_e2fsprogs/blkid/read.c
e2fsprogs/old_e2fsprogs/blkid/tag.c
e2fsprogs/old_e2fsprogs/e2fsck.c
e2fsprogs/old_e2fsprogs/e2fsck.h
e2fsprogs/old_e2fsprogs/e2p/Kbuild.src
e2fsprogs/old_e2fsprogs/e2p/feature.c
e2fsprogs/old_e2fsprogs/e2p/ostype.c
e2fsprogs/old_e2fsprogs/ext2fs/Kbuild.src
e2fsprogs/old_e2fsprogs/ext2fs/alloc.c
e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c
e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c
e2fsprogs/old_e2fsprogs/ext2fs/bitops.c
e2fsprogs/old_e2fsprogs/ext2fs/block.c
e2fsprogs/old_e2fsprogs/ext2fs/bmap.c
e2fsprogs/old_e2fsprogs/ext2fs/bmove.c
e2fsprogs/old_e2fsprogs/ext2fs/brel.h
e2fsprogs/old_e2fsprogs/ext2fs/closefs.c
e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c
e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c
e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c
e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c
e2fsprogs/old_e2fsprogs/ext2fs/e2image.h
e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h
e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h
e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h
e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h
e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c
e2fsprogs/old_e2fsprogs/ext2fs/flushb.c
e2fsprogs/old_e2fsprogs/ext2fs/freefs.c
e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c
e2fsprogs/old_e2fsprogs/ext2fs/getsize.c
e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c
e2fsprogs/old_e2fsprogs/ext2fs/initialize.c
e2fsprogs/old_e2fsprogs/ext2fs/inline.c
e2fsprogs/old_e2fsprogs/ext2fs/inode.c
e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c
e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c
e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h
e2fsprogs/old_e2fsprogs/ext2fs/lookup.c
e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c
e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c
e2fsprogs/old_e2fsprogs/ext2fs/namei.c
e2fsprogs/old_e2fsprogs/ext2fs/newdir.c
e2fsprogs/old_e2fsprogs/ext2fs/read_bb.c
e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c
e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c
e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c
e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c
e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c
e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c
e2fsprogs/old_e2fsprogs/ext2fs/unlink.c
e2fsprogs/old_e2fsprogs/fsck.c
e2fsprogs/old_e2fsprogs/lsattr.c
e2fsprogs/old_e2fsprogs/mke2fs.c
e2fsprogs/old_e2fsprogs/tune2fs.c
e2fsprogs/old_e2fsprogs/util.c
e2fsprogs/old_e2fsprogs/uuid/Kbuild.src
e2fsprogs/tune2fs.c
editors/Config.src
editors/Kbuild.src
editors/awk.c
editors/cmp.c
editors/diff.c
editors/ed.c
editors/patch.c
editors/patch_bbox.c [new file with mode: 0644]
editors/patch_toybox.c
editors/sed.c
editors/vi.c
examples/android-build [new file with mode: 0755]
examples/bootfloppy/display.txt
examples/bootfloppy/etc/fstab
examples/bootfloppy/etc/inittab
examples/bootfloppy/etc/profile
examples/bootfloppy/mkrootfs.sh
examples/depmod
examples/depmod.pl
examples/inittab
examples/mdev.conf
examples/mdev.conf.change_blockdev.sh [new file with mode: 0755]
examples/mdev_fat.conf
examples/udhcp/sample.bound
examples/udhcp/sample.renew
examples/udhcp/simple.script
examples/udhcp/udhcpd.conf
examples/var_service/README [new file with mode: 0644]
examples/zcip.script
findutils/.gitignore [deleted file]
findutils/Kbuild.src
findutils/find.c
findutils/grep.c
findutils/xargs.c
include/.gitignore
include/applet_metadata.h [new file with mode: 0644]
include/applets.src.h
include/ar.h
include/bb_archive.h [new file with mode: 0644]
include/bb_e2fs_defs.h [moved from e2fsprogs/e2fs_defs.h with 87% similarity]
include/busybox.h
include/dump.h
include/fix_u32.h
include/grp_.h
include/libbb.h
include/liblzo_interface.h [moved from archival/liblzo_interface.h with 97% similarity]
include/platform.h
include/pwd_.h
include/rtc_.h
include/shadow_.h
include/unarchive.h [deleted file]
include/unicode.h
include/usage.src.h
include/volume_id.h
include/xatonum.h
include/xregex.h
init/Config.src
init/Kbuild.src
init/bootchartd.c
init/halt.c
init/init.c
init/mesg.c
init/reboot.h [new file with mode: 0644]
libbb/Config.src
libbb/Kbuild.src
libbb/README
libbb/appletlib.c
libbb/ask_confirmation.c
libbb/bb_askpass.c
libbb/bb_basename.c [deleted file]
libbb/bb_bswap_64.c [new file with mode: 0644]
libbb/bb_do_delay.c
libbb/bb_pwd.c
libbb/bb_qsort.c
libbb/bb_strtod.c
libbb/bb_strtonum.c
libbb/chomp.c
libbb/compare_string_array.c
libbb/concat_path_file.c
libbb/concat_subpath_file.c
libbb/copy_file.c
libbb/copyfd.c
libbb/correct_password.c
libbb/crc32.c
libbb/create_icmp6_socket.c [deleted file]
libbb/create_icmp_socket.c [deleted file]
libbb/default_error_retval.c
libbb/device_open.c
libbb/die_if_bad_username.c
libbb/dump.c
libbb/endofname.c [new file with mode: 0644]
libbb/execable.c
libbb/fclose_nonstdin.c
libbb/fflush_stdout_and_exit.c
libbb/fgets_str.c
libbb/find_mount_point.c
libbb/find_pid_by_name.c
libbb/find_root_device.c
libbb/full_write.c
libbb/get_console.c
libbb/get_cpu_count.c [new file with mode: 0644]
libbb/get_last_path_component.c
libbb/get_line_from_file.c
libbb/get_shell_name.c [new file with mode: 0644]
libbb/get_volsize.c
libbb/getopt32.c
libbb/getpty.c
libbb/hash_md5_sha.c [new file with mode: 0644]
libbb/hash_md5prime.c [moved from libbb/md5prime.c with 96% similarity]
libbb/herror_msg.c
libbb/human_readable.c
libbb/in_ether.c [new file with mode: 0644]
libbb/inet_cksum.c [new file with mode: 0644]
libbb/inet_common.c
libbb/info_msg.c
libbb/inode_hash.c
libbb/isdirectory.c
libbb/kernel_version.c
libbb/last_char_is.c
libbb/lineedit.c
libbb/lineedit_ptr_hack.c
libbb/llist.c
libbb/login.c
libbb/loop.c
libbb/make_directory.c
libbb/makedev.c
libbb/match_fstype.c
libbb/md5.c [deleted file]
libbb/messages.c
libbb/missing_syscalls.c [new file with mode: 0644]
libbb/mode_string.c
libbb/mtab.c
libbb/mtab_file.c [deleted file]
libbb/nuke_str.c [new file with mode: 0644]
libbb/obscure.c
libbb/parse_config.c
libbb/parse_mode.c
libbb/percent_decode.c [new file with mode: 0644]
libbb/perror_msg.c
libbb/perror_nomsg.c
libbb/perror_nomsg_and_die.c
libbb/pidfile.c
libbb/platform.c
libbb/print_flags.c
libbb/printable.c
libbb/printable_string.c
libbb/process_escape_sequence.c
libbb/procps.c
libbb/progress.c
libbb/ptr_to_globals.c
libbb/pw_encrypt.c
libbb/pw_encrypt_md5.c
libbb/pw_encrypt_sha.c
libbb/read.c
libbb/read_key.c
libbb/read_printf.c
libbb/recursive_action.c
libbb/remove_file.c
libbb/rtc.c
libbb/safe_gethostname.c
libbb/safe_poll.c
libbb/safe_strncpy.c
libbb/safe_write.c
libbb/selinux_common.c
libbb/setup_environment.c
libbb/sha1.c [deleted file]
libbb/signals.c
libbb/simplify_path.c
libbb/single_argv.c
libbb/skip_whitespace.c
libbb/speed_table.c
libbb/str_tolower.c
libbb/strrstr.c
libbb/systemd_support.c [new file with mode: 0644]
libbb/time.c
libbb/trim.c
libbb/u_signal_names.c
libbb/udp_io.c
libbb/unicode.c
libbb/update_passwd.c
libbb/utmp.c
libbb/uuencode.c
libbb/vdprintf.c
libbb/verror_msg.c
libbb/vfork_daemon_rexec.c
libbb/warn_ignoring_args.c
libbb/wfopen.c
libbb/wfopen_input.c
libbb/write.c
libbb/xatonum.c
libbb/xatonum_template.c
libbb/xconnect.c
libbb/xfunc_die.c
libbb/xfuncs.c
libbb/xfuncs_printf.c
libbb/xgetcwd.c
libbb/xgethostbyname.c
libbb/xreadlink.c
libbb/xrealloc_vector.c
libbb/xregcomp.c
libpwdgrp/Kbuild.src
libpwdgrp/pwd_grp.c
libpwdgrp/pwd_grp_internal.c
libpwdgrp/uidgid_get.c
loginutils/Config.src
loginutils/Kbuild.src
loginutils/README [new file with mode: 0644]
loginutils/add-remove-shell.c [new file with mode: 0644]
loginutils/addgroup.c
loginutils/adduser.c
loginutils/chpasswd.c
loginutils/cryptpw.c
loginutils/deluser.c
loginutils/getty.c
loginutils/login.c
loginutils/passwd.c
loginutils/su.c
loginutils/sulogin.c
loginutils/vlock.c
mailutils/Kbuild.src
mailutils/mail.c
mailutils/mail.h
mailutils/makemime.c [new file with mode: 0644]
mailutils/mime.c [deleted file]
mailutils/popmaildir.c
mailutils/reformime.c [new file with mode: 0644]
mailutils/sendmail.c
miscutils/Config.src
miscutils/Kbuild.src
miscutils/adjtimex.c
miscutils/bbconfig.c
miscutils/beep.c
miscutils/chat.c
miscutils/chrt.c
miscutils/conspy.c
miscutils/crond.c
miscutils/crontab.c
miscutils/dc.c
miscutils/devfsd.c
miscutils/devmem.c
miscutils/eject.c
miscutils/fbsplash.c
miscutils/flash_eraseall.c
miscutils/flash_lock_unlock.c
miscutils/flashcp.c
miscutils/hdparm.c
miscutils/inotifyd.c
miscutils/ionice.c
miscutils/last.c
miscutils/last_fancy.c
miscutils/less.c
miscutils/makedevs.c
miscutils/man.c
miscutils/microcom.c
miscutils/mountpoint.c
miscutils/mt.c
miscutils/nandwrite.c [new file with mode: 0644]
miscutils/raidautorun.c
miscutils/readahead.c
miscutils/rfkill.c
miscutils/runlevel.c
miscutils/rx.c
miscutils/setserial.c [new file with mode: 0644]
miscutils/setsid.c
miscutils/strings.c
miscutils/taskset.c
miscutils/time.c
miscutils/timeout.c
miscutils/ttysize.c
miscutils/ubi_attach_detach.c [deleted file]
miscutils/ubi_tools.c [new file with mode: 0644]
miscutils/volname.c
miscutils/wall.c
miscutils/watchdog.c
modutils/Config.src
modutils/Kbuild.src
modutils/depmod.c
modutils/insmod.c
modutils/lsmod.c
modutils/modinfo.c
modutils/modprobe-small.c
modutils/modprobe.c
modutils/modutils-24.c
modutils/modutils.c
modutils/modutils.h
modutils/rmmod.c
networking/Config.src
networking/Kbuild.src
networking/arp.c
networking/arping.c
networking/brctl.c
networking/dnsd.c
networking/ether-wake.c
networking/ftpd.c
networking/ftpgetput.c
networking/hostname.c
networking/httpd.c
networking/httpd_indexcgi.c
networking/httpd_ssi.c
networking/ifconfig.c
networking/ifenslave.c
networking/ifplugd.c
networking/ifupdown.c
networking/inetd.c
networking/interface.c
networking/ip.c
networking/ipcalc.c
networking/isrv.c
networking/isrv.h
networking/isrv_identd.c
networking/libiproute/Kbuild.src
networking/libiproute/ip_common.h
networking/libiproute/ip_parse_common_args.c
networking/libiproute/ipaddress.c
networking/libiproute/iplink.c
networking/libiproute/iproute.c
networking/libiproute/iprule.c
networking/libiproute/iptunnel.c
networking/libiproute/libnetlink.c
networking/libiproute/libnetlink.h
networking/libiproute/ll_addr.c
networking/libiproute/ll_map.c
networking/libiproute/ll_proto.c
networking/libiproute/ll_types.c
networking/libiproute/rt_names.c
networking/libiproute/rtm_map.c
networking/libiproute/utils.c
networking/libiproute/utils.h
networking/nameif.c
networking/nbd-client.c [new file with mode: 0644]
networking/nc.c
networking/nc_bloaty.c
networking/netstat.c
networking/nslookup.c
networking/ntpd.c
networking/ntpd_simple.c
networking/ping.c
networking/pscan.c
networking/route.c
networking/slattach.c
networking/tc.c
networking/tcpudp.c
networking/tcpudp_perhost.c
networking/tcpudp_perhost.h
networking/telnet.c
networking/telnetd.c
networking/tftp.c
networking/traceroute.c
networking/tunctl.c
networking/udhcp/Config.src
networking/udhcp/Kbuild.src
networking/udhcp/arpping.c
networking/udhcp/common.c
networking/udhcp/common.h
networking/udhcp/d6_common.h [new file with mode: 0644]
networking/udhcp/d6_dhcpc.c [new file with mode: 0644]
networking/udhcp/d6_packet.c [new file with mode: 0644]
networking/udhcp/d6_socket.c [new file with mode: 0644]
networking/udhcp/dhcpc.c
networking/udhcp/dhcpc.h
networking/udhcp/dhcpd.c
networking/udhcp/dhcpd.h
networking/udhcp/dhcprelay.c
networking/udhcp/domain_codec.c
networking/udhcp/dumpleases.c
networking/udhcp/files.c
networking/udhcp/leases.c
networking/udhcp/packet.c
networking/udhcp/socket.c
networking/udhcp/static_leases.c
networking/vconfig.c
networking/wget.c
networking/whois.c [new file with mode: 0644]
networking/zcip.c
packaging/06ls.patch [deleted file]
packaging/applets-fallback.patch [deleted file]
packaging/bin.links [deleted file]
packaging/blockdev.patch [deleted file]
packaging/bootchartd-mounting-tmpfs-is-Linux-specific.patch [deleted file]
packaging/busybox-1.17.1-make.patch [deleted file]
packaging/busybox-1.20.2-fix-resource-h-failure.patch [deleted file]
packaging/busybox-zero-ifr.ifr_hwaddr.sa_data.patch [deleted file]
packaging/busybox.config [moved from debian/config/pkg/slp with 68% similarity]
packaging/busybox.manifest
packaging/busybox.spec [changed mode: 0755->0644]
packaging/busybox.tizen.config [moved from debian/config/pkg/static with 63% similarity]
packaging/cttyhack-serial-console-detection-is-Linux-specific.patch [deleted file]
packaging/debian-changes-1.17.1-10 [deleted file]
packaging/doc-man-name.patch [deleted file]
packaging/init-console-CRTSCTS.patch [deleted file]
packaging/init-console.patch [deleted file]
packaging/init-halt-portability-improvements.patch [deleted file]
packaging/init-loginutils-termios-portability-fixes.patch [deleted file]
packaging/init-make-the-initial-TERM-value-configurable.patch [deleted file]
packaging/klogd-make-it-work-on-non-linux-systems.patch [deleted file]
packaging/klogd.service [deleted file]
packaging/less-remove-misguided-dependency-on-PLATFORM_LINUX.patch [deleted file]
packaging/libbb-conditionalize-AF_-usage-in-error-reporting.patch [deleted file]
packaging/libbb.h-add-device-names-for-Hurd-and-FreeBSD.patch [deleted file]
packaging/make_gen_build_files_skip_quilt.patch [deleted file]
packaging/make_unicode_printable.patch [deleted file]
packaging/mark-Linux-specific-configuration-options.patch [deleted file]
packaging/mkdir-fix-p-on-FreeBSD.patch [deleted file]
packaging/readlink-use-xmalloc_realpath.patch [deleted file]
packaging/sbin.links [deleted file]
packaging/shell-ash-export-HOME.patch [deleted file]
packaging/shell-hist.patch [deleted file]
packaging/smack-busybox-1.17.1.patch [deleted file]
packaging/smack-conflict-with-selinux.patch [deleted file]
packaging/strip.patch [deleted file]
packaging/stty-sort-out-preprocessor-conditionals.patch [deleted file]
packaging/swaponoff-FreeBSD-support.patch [deleted file]
packaging/sysinfo-multiple-define-error-fix.patch [deleted file]
packaging/syslogd-disable-systemd-sa.patch [deleted file]
packaging/syslogd.manifest [deleted file]
packaging/syslogd.service [deleted file]
packaging/tcpsvd-udpsvd-conditionalize-usage-of-SO_ORIGINAL_DS.patch [deleted file]
packaging/tizen.config [deleted file]
packaging/top-display-rss.patch [deleted file]
packaging/u-mount-FreeBSD-support.patch [deleted file]
packaging/udhcpc-fast-request.patch [deleted file]
packaging/update-scripts-kconfig-_shipped.patch [deleted file]
packaging/usrbin.links [deleted file]
packaging/usrsbin.links [deleted file]
packaging/version.patch [deleted file]
packaging/vlock-disable-linux-console-calls-on-other-systems.patch [deleted file]
printutils/Kbuild.src
printutils/lpd.c
printutils/lpr.c
procps/Config.src
procps/Kbuild.src
procps/free.c
procps/fuser.c
procps/iostat.c [new file with mode: 0644]
procps/kill.c
procps/lsof.c [new file with mode: 0644]
procps/mpstat.c [new file with mode: 0644]
procps/nmeter.c
procps/pgrep.c
procps/pidof.c
procps/pmap.c [new file with mode: 0644]
procps/powertop.c [new file with mode: 0644]
procps/ps.c
procps/pstree.c [new file with mode: 0644]
procps/pwdx.c [new file with mode: 0644]
procps/renice.c
procps/smemcap.c
procps/sysctl.c
procps/top.c
procps/uptime.c
procps/watch.c
runit/Kbuild.src
runit/chpst.c
runit/runit_lib.c [deleted file]
runit/runit_lib.h
runit/runsv.c
runit/runsvdir.c
runit/sv.c
runit/svlogd.c
scripts/Makefile.IMA
scripts/Makefile.host
scripts/Makefile.lib
scripts/basic/docproc.c
scripts/basic/fixdep.c
scripts/bloat-o-meter
scripts/cleanup_printf2puts
scripts/echo.c
scripts/find_stray_empty_lines [new file with mode: 0755]
scripts/gen_build_files.sh
scripts/individual [deleted file]
scripts/kconfig/Makefile
scripts/kconfig/check.sh
scripts/kconfig/conf.c
scripts/kconfig/confdata.c
scripts/kconfig/lex.zconf.c_shipped
scripts/kconfig/lxdialog/textbox.c
scripts/kconfig/mconf.c
scripts/kconfig/menu.c
scripts/kconfig/util.c
scripts/kconfig/zconf.hash.c_shipped
scripts/kconfig/zconf.tab.c_shipped
scripts/kconfig/zconf.y
scripts/mkconfigs
scripts/mkdiff_obj
scripts/mkmakefile
scripts/randomtest
scripts/randomtest.loop
scripts/showasm
scripts/trylink
selinux/Config.src
selinux/Kbuild.src
selinux/chcon.c
selinux/getenforce.c
selinux/getsebool.c
selinux/load_policy.c
selinux/matchpathcon.c
selinux/runcon.c
selinux/selinuxenabled.c
selinux/sestatus.c
selinux/setenforce.c
selinux/setfiles.c
selinux/setsebool.c
shell/Config.src
shell/Kbuild.src
shell/ash.c
shell/ash_ptr_hack.c
shell/ash_test/ash-arith/arith.right
shell/ash_test/ash-misc/echo_write_error.right [new file with mode: 0644]
shell/ash_test/ash-misc/echo_write_error.tests [new file with mode: 0644]
shell/ash_test/ash-redir/redir.right
shell/ash_test/ash-redir/redir9.tests [changed mode: 0644->0755]
shell/ash_test/ash-redir/redirA.right [new file with mode: 0644]
shell/ash_test/ash-redir/redirA.tests [new file with mode: 0755]
shell/ash_test/ash-signals/sigint1.right [new file with mode: 0644]
shell/ash_test/ash-signals/sigint1.tests [new file with mode: 0755]
shell/ash_test/ash-signals/signal7.right [new file with mode: 0644]
shell/ash_test/ash-signals/signal7.tests [new file with mode: 0755]
shell/ash_test/ash-signals/signal8.right [new file with mode: 0644]
shell/ash_test/ash-signals/signal8.tests [new file with mode: 0755]
shell/ash_test/ash-signals/signal9.right [new file with mode: 0644]
shell/ash_test/ash-signals/signal9.tests [new file with mode: 0755]
shell/ash_test/ash-vars/var_bash3.right
shell/ash_test/ash-vars/var_bash3.tests
shell/ash_test/ash-vars/var_bash4.right [new file with mode: 0644]
shell/ash_test/ash-vars/var_bash4.tests [new file with mode: 0755]
shell/ash_test/ash-vars/var_bash5.right [new file with mode: 0644]
shell/ash_test/ash-vars/var_bash5.tests [new file with mode: 0755]
shell/ash_test/recho.c
shell/ash_test/zecho.c
shell/bbsh.c [deleted file]
shell/cttyhack.c
shell/hush.c
shell/hush_test/hush-arith/arith.right
shell/hush_test/hush-bugs/export_exp.tests.disabled [moved from shell/hush_test/hush-bugs/export_exp.tests with 80% similarity, mode: 0644]
shell/hush_test/hush-glob/bash_brace1.right [new file with mode: 0644]
shell/hush_test/hush-glob/bash_brace1.tests [new file with mode: 0755]
shell/hush_test/hush-glob/glob2.right [new file with mode: 0644]
shell/hush_test/hush-glob/glob2.tests [new file with mode: 0755]
shell/hush_test/hush-misc/assignment3.right [new file with mode: 0644]
shell/hush_test/hush-misc/assignment3.tests [new file with mode: 0755]
shell/hush_test/hush-misc/assignment4.right [new file with mode: 0644]
shell/hush_test/hush-misc/assignment4.tests [new file with mode: 0755]
shell/hush_test/hush-misc/break1.tests
shell/hush_test/hush-misc/echo_write_error.right [new file with mode: 0644]
shell/hush_test/hush-misc/echo_write_error.tests [new file with mode: 0755]
shell/hush_test/hush-misc/heredoc_backslash1.right [new file with mode: 0644]
shell/hush_test/hush-misc/heredoc_backslash1.tests [new file with mode: 0755]
shell/hush_test/hush-misc/pipefail.right [new file with mode: 0644]
shell/hush_test/hush-misc/pipefail.tests [new file with mode: 0755]
shell/hush_test/hush-misc/return1.right [new file with mode: 0644]
shell/hush_test/hush-misc/return1.tests [new file with mode: 0755]
shell/hush_test/hush-misc/sigint1.right [new file with mode: 0644]
shell/hush_test/hush-misc/sigint1.tests [new file with mode: 0755]
shell/hush_test/hush-misc/source1.right [new file with mode: 0644]
shell/hush_test/hush-misc/source1.tests [new file with mode: 0755]
shell/hush_test/hush-misc/source2.right [new file with mode: 0644]
shell/hush_test/hush-misc/source2.tests [new file with mode: 0755]
shell/hush_test/hush-misc/while3.right [new file with mode: 0644]
shell/hush_test/hush-misc/while3.tests [new file with mode: 0755]
shell/hush_test/hush-misc/while4.right [new file with mode: 0644]
shell/hush_test/hush-misc/while4.tests [new file with mode: 0755]
shell/hush_test/hush-parsing/comment1.right [new file with mode: 0644]
shell/hush_test/hush-parsing/comment1.tests [new file with mode: 0755]
shell/hush_test/hush-parsing/eol1.right [new file with mode: 0644]
shell/hush_test/hush-parsing/eol1.tests [new file with mode: 0755]
shell/hush_test/hush-parsing/starquoted2.right
shell/hush_test/hush-parsing/starquoted2.tests
shell/hush_test/hush-psubst/tick3.right
shell/hush_test/hush-psubst/tick3.tests
shell/hush_test/hush-trap/exit.right
shell/hush_test/hush-trap/exit.tests
shell/hush_test/hush-trap/signal7.right [new file with mode: 0644]
shell/hush_test/hush-trap/signal7.tests [new file with mode: 0755]
shell/hush_test/hush-trap/signal_read1.right [new file with mode: 0644]
shell/hush_test/hush-trap/signal_read1.tests [new file with mode: 0755]
shell/hush_test/hush-trap/signal_read2.right [new file with mode: 0644]
shell/hush_test/hush-trap/signal_read2.tests [new file with mode: 0755]
shell/hush_test/hush-trap/subshell.tests
shell/hush_test/hush-vars/var_bash1.right [new file with mode: 0644]
shell/hush_test/hush-vars/var_bash1.tests [new file with mode: 0755]
shell/hush_test/hush-vars/var_bash2.right [new file with mode: 0644]
shell/hush_test/hush-vars/var_bash2.tests [new file with mode: 0755]
shell/hush_test/hush-vars/var_bash3.right [new file with mode: 0644]
shell/hush_test/hush-vars/var_bash3.tests [new file with mode: 0755]
shell/hush_test/hush-vars/var_bash4.right [new file with mode: 0644]
shell/hush_test/hush-vars/var_bash4.tests [new file with mode: 0755]
shell/hush_test/hush-vars/var_bash5.right [new file with mode: 0644]
shell/hush_test/hush-vars/var_bash5.tests [new file with mode: 0755]
shell/hush_test/hush-vars/var_bash6.right [new file with mode: 0644]
shell/hush_test/hush-vars/var_bash6.tests [new file with mode: 0755]
shell/hush_test/hush-vars/var_expand_on_ifs.right [new file with mode: 0644]
shell/hush_test/hush-vars/var_expand_on_ifs.tests [new file with mode: 0755]
shell/hush_test/hush-vars/var_serial.right [new file with mode: 0644]
shell/hush_test/hush-vars/var_serial.tests [new file with mode: 0755]
shell/hush_test/hush-vars/var_unbackslash.right [new file with mode: 0644]
shell/hush_test/hush-vars/var_unbackslash.tests [new file with mode: 0755]
shell/hush_test/run-all
shell/match.c
shell/match.h
shell/math.c
shell/math.h
shell/random.c
shell/random.h
shell/shell_common.c
shell/shell_common.h
sysklogd/Config.src
sysklogd/Kbuild.src
sysklogd/klogd.c
sysklogd/logger.c
sysklogd/logread.c
sysklogd/syslogd.c
sysklogd/syslogd_and_logger.c
testsuite/all_sourcecode.tests
testsuite/ar.tests
testsuite/ash.tests
testsuite/awk.tests
testsuite/basename/basename-works
testsuite/bunzip2.tests
testsuite/busybox.tests
testsuite/bzcat.tests
testsuite/cal.tests
testsuite/comm.tests
testsuite/cp.tests
testsuite/cpio.tests
testsuite/cut.tests
testsuite/date/date-@-works [new file with mode: 0644]
testsuite/date/date-R-works
testsuite/date/date-works
testsuite/date/date-works-1
testsuite/diff.tests
testsuite/dirname/dirname-works
testsuite/du/du-h-works
testsuite/du/du-k-works
testsuite/du/du-l-works
testsuite/echo/echo-prints-dash [new file with mode: 0644]
testsuite/echo/echo-prints-non-opts [new file with mode: 0644]
testsuite/echo/echo-prints-slash_00041 [new file with mode: 0644]
testsuite/echo/echo-prints-slash_0041 [new file with mode: 0644]
testsuite/echo/echo-prints-slash_041 [new file with mode: 0644]
testsuite/echo/echo-prints-slash_41 [new file with mode: 0644]
testsuite/expand.tests
testsuite/expr/expr-works
testsuite/fold.tests
testsuite/grep.tests
testsuite/gunzip.tests
testsuite/hostid/hostid-works
testsuite/hostname/hostname-d-works
testsuite/ln/ln-preserves-soft-links
testsuite/ls.tests
testsuite/makedevs.tests
testsuite/md5sum.tests [new file with mode: 0755]
testsuite/mdev.tests
testsuite/mkfs.minix.tests
testsuite/mount.testroot
testsuite/mount.tests
testsuite/od.tests
testsuite/parse.tests
testsuite/patch.tests
testsuite/pidof.tests
testsuite/printf.tests
testsuite/readlink.tests
testsuite/runtest
testsuite/rx.tests
testsuite/sed.tests
testsuite/seq.tests
testsuite/sha1sum.tests [new file with mode: 0755]
testsuite/sha256sum.tests [new file with mode: 0755]
testsuite/sha3sum.tests [new file with mode: 0755]
testsuite/sha512sum.tests [new file with mode: 0755]
testsuite/sort.tests
testsuite/start-stop-daemon.tests
testsuite/sum.tests
testsuite/tail.tests
testsuite/tar.tests
testsuite/tar/tar-extracts-all-subdirs
testsuite/taskset.tests
testsuite/test.tests
testsuite/testing.sh
testsuite/tr.tests
testsuite/uncompress.tests [new file with mode: 0755]
testsuite/unexpand.tests
testsuite/uniq.tests
testsuite/unzip.tests
testsuite/uptime/uptime-works
testsuite/uuencode.tests
testsuite/which/which-uses-default-path
testsuite/xargs.tests
util-linux/Config.src
util-linux/Kbuild.src
util-linux/acpid.c
util-linux/blkid.c
util-linux/blockdev.c [new file with mode: 0644]
util-linux/dmesg.c
util-linux/fbset.c
util-linux/fdformat.c
util-linux/fdisk.c
util-linux/fdisk_aix.c
util-linux/fdisk_gpt.c [new file with mode: 0644]
util-linux/fdisk_osf.c
util-linux/fdisk_sgi.c
util-linux/fdisk_sun.c
util-linux/findfs.c
util-linux/flock.c
util-linux/freeramdisk.c
util-linux/fsck_minix.c
util-linux/fstrim.c [new file with mode: 0644]
util-linux/getopt.c
util-linux/hexdump.c
util-linux/hwclock.c
util-linux/ipcrm.c
util-linux/ipcs.c
util-linux/losetup.c
util-linux/lspci.c
util-linux/lsusb.c
util-linux/mdev.c
util-linux/mkfs_ext2.c
util-linux/mkfs_minix.c
util-linux/mkfs_reiser.c
util-linux/mkfs_vfat.c
util-linux/mkswap.c
util-linux/more.c
util-linux/mount.c
util-linux/pivot_root.c
util-linux/rdate.c
util-linux/rdev.c
util-linux/readprofile.c
util-linux/rev.c
util-linux/rtcwake.c
util-linux/script.c
util-linux/scriptreplay.c
util-linux/setarch.c
util-linux/swaponoff.c
util-linux/switch_root.c
util-linux/umount.c
util-linux/volume_id/Config.src [new file with mode: 0644]
util-linux/volume_id/Kbuild.src
util-linux/volume_id/btrfs.c
util-linux/volume_id/cramfs.c
util-linux/volume_id/exfat.c [new file with mode: 0644]
util-linux/volume_id/ext.c
util-linux/volume_id/f2fs.c [new file with mode: 0644]
util-linux/volume_id/fat.c
util-linux/volume_id/get_devname.c
util-linux/volume_id/hfs.c
util-linux/volume_id/iso9660.c
util-linux/volume_id/jfs.c
util-linux/volume_id/linux_raid.c
util-linux/volume_id/linux_swap.c
util-linux/volume_id/luks.c
util-linux/volume_id/nilfs.c [new file with mode: 0644]
util-linux/volume_id/ntfs.c
util-linux/volume_id/ocfs2.c
util-linux/volume_id/reiserfs.c
util-linux/volume_id/romfs.c
util-linux/volume_id/squashfs.c [new file with mode: 0644]
util-linux/volume_id/sysv.c
util-linux/volume_id/udf.c
util-linux/volume_id/unused_highpoint.c
util-linux/volume_id/unused_hpfs.c
util-linux/volume_id/unused_isw_raid.c
util-linux/volume_id/unused_lsi_raid.c
util-linux/volume_id/unused_lvm.c
util-linux/volume_id/unused_mac.c
util-linux/volume_id/unused_minix.c
util-linux/volume_id/unused_msdos.c
util-linux/volume_id/unused_nvidia_raid.c
util-linux/volume_id/unused_promise_raid.c
util-linux/volume_id/unused_silicon_raid.c
util-linux/volume_id/unused_ufs.c
util-linux/volume_id/unused_via_raid.c
util-linux/volume_id/util.c
util-linux/volume_id/volume_id.c
util-linux/volume_id/volume_id_internal.h
util-linux/volume_id/xfs.c

index 7d2cca6..0a0c65b 100644 (file)
@@ -18,6 +18,7 @@ Config.in
 # Normal output
 #
 /busybox
+/busybox_old
 /busybox_unstripped*
 
 #
index fe64f2b..2c4be2e 100644 (file)
--- a/Config.in
+++ b/Config.in
@@ -47,6 +47,17 @@ config USE_PORTABLE_CODE
          compiler other than gcc.
          If you do use gcc, this option may needlessly increase code size.
 
+config PLATFORM_LINUX
+       bool "Enable Linux-specific applets and features"
+       default y
+       help
+         For the most part, busybox requires only POSIX compatibility
+         from the target system, but some applets and features use
+         Linux-specific interfaces.
+
+         Answering 'N' here will disable such applets and hide the
+         corresponding configuration options.
+
 choice
        prompt "Buffer allocation policy"
        default FEATURE_BUFFERS_USE_MALLOC
@@ -72,20 +83,21 @@ config FEATURE_BUFFERS_GO_IN_BSS
 endchoice
 
 config SHOW_USAGE
-       bool "Show terse applet usage messages"
+       bool "Show applet usage messages"
        default y
        help
-         All BusyBox applets will show help messages when invoked with
-         wrong arguments. You can turn off printing these terse usage
-         messages if you say no here.
-         This will save you up to 7k.
+         Enabling this option, BusyBox 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,
+         saving approximately 7k.
 
 config FEATURE_VERBOSE_USAGE
        bool "Show verbose applet usage messages"
        default y
        depends on SHOW_USAGE
        help
-         All BusyBox applets will show more verbose help messages when
+         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.
@@ -95,8 +107,8 @@ config FEATURE_COMPRESS_USAGE
        default y
        depends on SHOW_USAGE
        help
-         Store usage messages in compressed form, uncompress them on-the-fly
-         when <applet> --help is called.
+         Store usage messages in .bz compressed form, uncompress them
+         on-the-fly when <applet> --help is called.
 
          If you have a really tiny busybox with few applets enabled (and
          bunzip2 isn't one of them), the overhead of the decompressor might
@@ -112,6 +124,14 @@ config FEATURE_INSTALLER
          busybox at runtime to create hard links or symlinks for all the
          applets that are compiled into busybox.
 
+config INSTALL_NO_USR
+       bool "Don't use /usr"
+       default n
+       help
+         Disable use of /usr. busybox --install and "make install"
+         will install applets only to /bin and /sbin,
+         never to /usr/bin or /usr/sbin.
+
 config LOCALE_SUPPORT
        bool "Enable locale support (system needs locale for this to work)"
        default n
@@ -141,12 +161,13 @@ config UNICODE_USING_LOCALE
          Internal implementation is smaller.
 
 config FEATURE_CHECK_UNICODE_IN_ENV
-       bool "Check $LANG environment variable"
+       bool "Check $LC_ALL, $LC_CTYPE and $LANG environment variables"
        default n
        depends on UNICODE_SUPPORT && !UNICODE_USING_LOCALE
        help
          With this option on, Unicode support is activated
-         only if LANG variable has the value of the form "xxxx.utf8"
+         only if locale-related variables have the value of the form
+         "xxxx.utf8"
 
          Otherwise, Unicode support will be always enabled and active.
 
@@ -229,8 +250,9 @@ config UNICODE_PRESERVE_BROKEN
        default n
        depends on UNICODE_SUPPORT
        help
-         With this option on, invalid UTF-8 bytes are not substituted
-         with the selected substitution character.
+         With this option on, on line-editing input (such as used by shells)
+         invalid UTF-8 bytes are not substituted with the selected
+         substitution character.
          For example, this means that entering 'l', 's', ' ', 0xff, [Enter]
          at shell prompt will list file named 0xff (single char name
          with char value 255), not file named '?'.
@@ -276,7 +298,7 @@ config FEATURE_UTMP
 config FEATURE_WTMP
        bool "Support wtmp file"
        default y
-       select FEATURE_UTMP
+       depends on FEATURE_UTMP
        help
          The file /var/run/wtmp is used to track when users have logged into
          and logged out of the system.
@@ -289,35 +311,69 @@ config FEATURE_PIDFILE
        default y
        help
          This option makes some applets (e.g. crond, syslogd, inetd) write
-         a pidfile in /var/run. Some applications rely on them.
+         a pidfile at the configured PID_FILE_PATH.  It has no effect
+         on applets which require pidfiles to run.
+
+config PID_FILE_PATH
+       string "Path to directory for pidfile"
+       default "/var/run"
+       depends on FEATURE_PIDFILE
+       help
+         This is the default path where pidfiles are created.  Applets which
+         allow you to set the pidfile path on the command line will override
+         this value.  The option has no effect on applets that require you to
+         specify a pidfile path.
 
 config FEATURE_SUID
        bool "Support for SUID/SGID handling"
        default y
        help
          With this option you can install the busybox binary belonging
-         to root with the suid bit set, and it will automatically drop
-         priviledges for applets that don't need root access.
+         to root with the suid bit set, enabling some applets to perform
+         root-level operations even when run by ordinary users
+         (for example, mounting of user mounts in fstab needs this).
+
+         Busybox will automatically drop privileges for applets
+         that don't need root access.
 
          If you are really paranoid and don't want to do this, 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. The applets currently marked to need the suid bit
-         are:
+         one that needs it.
+
+         The applets which require root rights (need suid bit or
+         to be run by root) and will refuse to execute otherwise:
+         crontab, login, passwd, su, vlock, wall.
 
-         crontab, dnsd, findfs, ipcrm, ipcs, login, passwd, ping, su,
-         traceroute, vlock.
+         The applets which will use root rights if they have them
+         (via suid bit, or because run by root), but would try to work
+         without root right nevertheless:
+         findfs, ping[6], traceroute[6], mount.
+
+         Note that if you DONT select this option, but DO make busybox
+         suid root, ALL applets will run under root, which is a huge
+         security hole (think "cp /some/file /etc/passwd").
 
 config FEATURE_SUID_CONFIG
        bool "Runtime SUID/SGID configuration via /etc/busybox.conf"
-       default y if FEATURE_SUID
+       default y
        depends on FEATURE_SUID
        help
          Allow the SUID / SGID state of an applet to be determined at runtime
          by checking /etc/busybox.conf. (This is sort of a poor man's sudo.)
          The format of this file is as follows:
 
-         <applet> = [Ssx-][Ssx-][x-] (<username>|<uid>).(<groupname>|<gid>)
+         APPLET = [Ssx-][Ssx-][x-] [USER.GROUP]
+
+         s: USER or GROUP is allowed to execute APPLET.
+            APPLET will run under USER or GROUP
+            (reagardless of who's running it).
+         S: USER or GROUP is NOT allowed to execute APPLET.
+            APPLET will run under USER or GROUP.
+            This option is not very sensical.
+         x: USER/GROUP/others are allowed to execute APPLET.
+            No UID/GID change will be done when it is run.
+         -: USER/GROUP/others are not allowed to execute APPLET.
 
          An example might help:
 
@@ -327,7 +383,8 @@ config FEATURE_SUID_CONFIG
          su = ssx        # exactly the same
 
          mount = sx- root.disk # applet mount can be run by root and members
-                               # of group disk and runs with euid=0
+                               # of group disk (but not anyone else)
+                               # and runs with euid=0 (egid is not changed)
 
          cp = --- # disable applet cp for everyone
 
@@ -353,6 +410,7 @@ config FEATURE_SUID_CONFIG_QUIET
 config SELINUX
        bool "Support NSA Security Enhanced Linux"
        default n
+       select PLATFORM_LINUX
        help
          Enable support for SELinux in applets ls, ps, and id. Also provide
          the option of compiling in SELinux applets.
@@ -433,7 +491,10 @@ config PIE
        default n
        depends on !STATIC
        help
-         (TODO: what is it and why/when is it useful?)
+         Hardened code option. PIE binaries are loaded at a different
+         address at each invocation. This has some overhead,
+         particularly on x86-32 which is short on registers.
+
          Most people will leave this set to 'N'.
 
 config NOMMU
@@ -530,7 +591,6 @@ config FEATURE_SHARED_BUSYBOX
 config LFS
        bool "Build with Large File Support (for accessing files > 2 GB)"
        default y
-       select FDISK_SUPPORT_LARGE_DISKS
        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
@@ -552,12 +612,39 @@ config CROSS_COMPILER_PREFIX
 
          Native builds leave this empty.
 
+config SYSROOT
+       string "Path to sysroot"
+       default ""
+       help
+         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
+         Android NDK, platform version 9, for ARM ABI with
+
+         CONFIG_SYSROOT=/opt/android-ndk/platforms/android-9/arch-arm
+
+         Native builds leave this empty.
+
 config EXTRA_CFLAGS
        string "Additional CFLAGS"
        default ""
        help
          Additional CFLAGS to pass to the compiler verbatim.
 
+config EXTRA_LDFLAGS
+       string "Additional LDFLAGS"
+       default ""
+       help
+         Additional LDFLAGS to pass to the linker verbatim.
+
+config EXTRA_LDLIBS
+       string "Additional LDLIBS"
+       default ""
+       help
+         Additional LDLIBS to pass to the linker with -l.
+
 endmenu
 
 menu 'Debugging Options'
@@ -634,25 +721,15 @@ config EFENCE
 
 endchoice
 
-### config PARSE
-###    bool "Uniform config file parser debugging applet: parse"
-
 endmenu
 
-menu 'Installation Options'
-
-config INSTALL_NO_USR
-       bool "Don't use /usr"
-       default n
-       help
-         Disable use of /usr. Don't activate this option if you don't know
-         that you really want this behaviour.
+menu 'Installation Options ("make install" behavior)'
 
 choice
-       prompt "Applets links"
+       prompt "What kind of applet links to install"
        default INSTALL_APPLET_SYMLINKS
        help
-         Choose how you install applets links.
+         Choose what kind of links to applets are created by "make install".
 
 config INSTALL_APPLET_SYMLINKS
        bool "as soft-links"
@@ -674,10 +751,10 @@ config INSTALL_APPLET_SCRIPT_WRAPPERS
 
 config INSTALL_APPLET_DONT
        bool "not installed"
-       depends on FEATURE_INSTALLER || FEATURE_SH_STANDALONE || FEATURE_PREFER_APPLETS
        help
-         Do not install applet links. Useful when using the -install feature
-         or a standalone shell for rescue purposes.
+         Do not install applet links. Useful when you plan to use
+         busybox --install for installing links, or plan to use
+         a standalone shell and thus don't need applet links.
 
 endchoice
 
@@ -701,8 +778,8 @@ config INSTALL_SH_APPLET_HARDLINK
 config INSTALL_SH_APPLET_SCRIPT_WRAPPER
        bool "as script wrapper"
        help
-         Install /bin/sh applet as script wrapper that call the busybox
-         binary.
+         Install /bin/sh applet as script wrapper that calls
+         the busybox binary.
 
 endchoice
 
diff --git a/INSTALL b/INSTALL
index ec2b028..750cfc4 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -47,8 +47,11 @@ the only commands busybox can find are the built-in ones.
 
 Note that the standalone shell requires CONFIG_BUSYBOX_EXEC_PATH
 to be set appropriately, depending on whether or not /proc/self/exe is
-available or not. If you do not have /proc, then point that config option
+available. If you do not have /proc, then point that config option
 to the location of your busybox binary, usually /bin/busybox.
+Another solution is to patch the kernel (see
+examples/linux-*_proc_self_exe.patch) to make exec("/proc/self/exe")
+always work.
 
 Configuring Busybox:
 ====================
@@ -70,7 +73,9 @@ create a known starting point.
 Other starting configurations (mostly used for testing purposes) include
 "make allbareconfig" (enables all applets but disables all optional features),
 "make allyesconfig" (enables absolutely everything including debug features),
-and "make randconfig" (produce a random configuration).
+and "make randconfig" (produce a random configuration).  The configs/ directory
+contains a number of additional configuration files ending in _defconfig which
+are useful in specific cases.  "make help" will list them.
 
 Configuring BusyBox produces a file ".config", which can be saved for future
 use.  Run "make oldconfig" to bring a .config file from an older version of
@@ -97,7 +102,7 @@ first argument to determine which applet to behave as, for example
 "./busybox cat LICENSE".  (Running the busybox applet with no arguments gives
 a list of all enabled applets.) The standalone shell can also call busybox
 applets without links to busybox under other names in the filesystem.  You can
-also configure a standaone install capability into the busybox base applet,
+also configure a standalone install capability into the busybox base applet,
 and then install such links at runtime with one of "busybox --install" (for
 hardlinks) or "busybox --install -s" (for symlinks).
 
diff --git a/LICENSE b/LICENSE
index 0e32274..6f50a71 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -346,515 +346,3 @@ proprietary programs.  If your program is a subroutine library, you may
 consider it more useful to permit linking proprietary applications with the
 library.  If this is what you want to do, use the GNU Library General
 Public License instead of this License.
-
-
-
-                  GNU LESSER GENERAL PUBLIC LICENSE
-                       Version 2.1, February 1999
-
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
-       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-[This is the first released version of the Lesser GPL.  It also counts
- as the successor of the GNU Library Public License, version 2, hence
- the version number 2.1.]
-
-                            Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
-
-  This license, the Lesser General Public License, applies to some
-specially designated software packages--typically libraries--of the
-Free Software Foundation and other authors who decide to use it.  You
-can use it too, but we suggest you first think carefully about whether
-this license or the ordinary General Public License is the better
-strategy to use in any particular case, based on the explanations
-below.
-
-  When we speak of free software, we are referring to freedom of use,
-not price.  Our General Public Licenses are designed to make sure that
-you have the freedom to distribute copies of free software (and charge
-for this service if you wish); that you receive source code or can get
-it if you want it; that you can change the software and use pieces of
-it in new free programs; and that you are informed that you can do
-these things.
-
-  To protect your rights, we need to make restrictions that forbid
-distributors to deny you these rights or to ask you to surrender these
-rights.  These restrictions translate to certain responsibilities for
-you if you distribute copies of the library or if you modify it.
-
-  For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you.  You must make sure that they, too, receive or can get the source
-code.  If you link other code with the library, you must provide
-complete object files to the recipients, so that they can relink them
-with the library after making changes to the library and recompiling
-it.  And you must show them these terms so they know their rights.
-
-  We protect your rights with a two-step method: (1) we copyright the
-library, and (2) we offer you this license, which gives you legal
-permission to copy, distribute and/or modify the library.
-
-  To protect each distributor, we want to make it very clear that
-there is no warranty for the free library.  Also, if the library is
-modified by someone else and passed on, the recipients should know
-that what they have is not the original version, so that the original
-author's reputation will not be affected by problems that might be
-introduced by others.
-\f
-  Finally, software patents pose a constant threat to the existence of
-any free program.  We wish to make sure that a company cannot
-effectively restrict the users of a free program by obtaining a
-restrictive license from a patent holder.  Therefore, we insist that
-any patent license obtained for a version of the library must be
-consistent with the full freedom of use specified in this license.
-
-  Most GNU software, including some libraries, is covered by the
-ordinary GNU General Public License.  This license, the GNU Lesser
-General Public License, applies to certain designated libraries, and
-is quite different from the ordinary General Public License.  We use
-this license for certain libraries in order to permit linking those
-libraries into non-free programs.
-
-  When a program is linked with a library, whether statically or using
-a shared library, the combination of the two is legally speaking a
-combined work, a derivative of the original library.  The ordinary
-General Public License therefore permits such linking only if the
-entire combination fits its criteria of freedom.  The Lesser General
-Public License permits more lax criteria for linking other code with
-the library.
-
-  We call this license the "Lesser" General Public License because it
-does Less to protect the user's freedom than the ordinary General
-Public License.  It also provides other free software developers Less
-of an advantage over competing non-free programs.  These disadvantages
-are the reason we use the ordinary General Public License for many
-libraries.  However, the Lesser license provides advantages in certain
-special circumstances.
-
-  For example, on rare occasions, there may be a special need to
-encourage the widest possible use of a certain library, so that it
-becomes a de-facto standard.  To achieve this, non-free programs must
-be allowed to use the library.  A more frequent case is that a free
-library does the same job as widely used non-free libraries.  In this
-case, there is little to gain by limiting the free library to free
-software only, so we use the Lesser General Public License.
-
-  In other cases, permission to use a particular library in non-free
-programs enables a greater number of people to use a large body of
-free software.  For example, permission to use the GNU C Library in
-non-free programs enables many more people to use the whole GNU
-operating system, as well as its variant, the GNU/Linux operating
-system.
-
-  Although the Lesser General Public License is Less protective of the
-users' freedom, it does ensure that the user of a program that is
-linked with the Library has the freedom and the wherewithal to run
-that program using a modified version of the Library.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.  Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library".  The
-former contains code derived from the library, whereas the latter must
-be combined with the library in order to run.
-\f
-                  GNU LESSER GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License Agreement applies to any software library or other
-program which contains a notice placed by the copyright holder or
-other authorized party saying it may be distributed under the terms of
-this Lesser General Public License (also called "this License").
-Each licensee is addressed as "you".
-
-  A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
-
-  The "Library", below, refers to any such software library or work
-which has been distributed under these terms.  A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language.  (Hereinafter, translation is
-included without limitation in the term "modification".)
-
-  "Source code" for a work means the preferred form of the work for
-making modifications to it.  For a library, complete source code means
-all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control
-compilation and installation of the library.
-
-  Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running a program using the Library is not restricted, and output from
-such a program is covered only if its contents constitute a work based
-on the Library (independent of the use of the Library in a tool for
-writing it).  Whether that is true depends on what the Library does
-and what the program that uses the Library does.
-
-  1. You may copy and distribute verbatim copies of the Library's
-complete source code as you receive it, in any medium, provided that
-you conspicuously and appropriately publish on each copy an
-appropriate copyright notice and disclaimer of warranty; keep intact
-all the notices that refer to this License and to the absence of any
-warranty; and distribute a copy of this License along with the
-Library.
-
-  You may charge a fee for the physical act of transferring a copy,
-and you may at your option offer warranty protection in exchange for a
-fee.
-\f
-  2. You may modify your copy or copies of the Library or any portion
-of it, thus forming a work based on the Library, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) The modified work must itself be a software library.
-
-    b) You must cause the files modified to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    c) You must cause the whole of the work to be licensed at no
-    charge to all third parties under the terms of this License.
-
-    d) If a facility in the modified Library refers to a function or a
-    table of data to be supplied by an application program that uses
-    the facility, other than as an argument passed when the facility
-    is invoked, then you must make a good faith effort to ensure that,
-    in the event an application does not supply such function or
-    table, the facility still operates, and performs whatever part of
-    its purpose remains meaningful.
-
-    (For example, a function in a library to compute square roots has
-    a purpose that is entirely well-defined independent of the
-    application.  Therefore, Subsection 2d requires that any
-    application-supplied function or table used by this function must
-    be optional: if the application does not supply it, the square
-    root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Library,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Library, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote
-it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Library.
-
-In addition, mere aggregation of another work not based on the Library
-with the Library (or with a work based on the Library) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may opt to apply the terms of the ordinary GNU General Public
-License instead of this License to a given copy of the Library.  To do
-this, you must alter all the notices that refer to this License, so
-that they refer to the ordinary GNU General Public License, version 2,
-instead of to this License.  (If a newer version than version 2 of the
-ordinary GNU General Public License has appeared, then you can specify
-that version instead if you wish.)  Do not make any other change in
-these notices.
-\f
-  Once this change is made in a given copy, it is irreversible for
-that copy, so the ordinary GNU General Public License applies to all
-subsequent copies and derivative works made from that copy.
-
-  This option is useful when you wish to copy part of the code of
-the Library into a program that is not a library.
-
-  4. You may copy and distribute the Library (or a portion or
-derivative of it, under Section 2) in object code or executable form
-under the terms of Sections 1 and 2 above provided that you accompany
-it with the complete corresponding machine-readable source code, which
-must be distributed under the terms of Sections 1 and 2 above on a
-medium customarily used for software interchange.
-
-  If distribution of object code is made by offering access to copy
-from a designated place, then offering equivalent access to copy the
-source code from the same place satisfies the requirement to
-distribute the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  5. A program that contains no derivative of any portion of the
-Library, but is designed to work with the Library by being compiled or
-linked with it, is called a "work that uses the Library".  Such a
-work, in isolation, is not a derivative work of the Library, and
-therefore falls outside the scope of this License.
-
-  However, linking a "work that uses the Library" with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a "work that uses the
-library".  The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
-
-  When a "work that uses the Library" uses material from a header file
-that is part of the Library, the object code for the work may be a
-derivative work of the Library even though the source code is not.
-Whether this is true is especially significant if the work can be
-linked without the Library, or if the work is itself a library.  The
-threshold for this to be true is not precisely defined by law.
-
-  If such an object file uses only numerical parameters, data
-structure layouts and accessors, and small macros and small inline
-functions (ten lines or less in length), then the use of the object
-file is unrestricted, regardless of whether it is legally a derivative
-work.  (Executables containing this object code plus portions of the
-Library will still fall under Section 6.)
-
-  Otherwise, if the work is a derivative of the Library, you may
-distribute the object code for the work under the terms of Section 6.
-Any executables containing that work also fall under Section 6,
-whether or not they are linked directly with the Library itself.
-\f
-  6. As an exception to the Sections above, you may also combine or
-link a "work that uses the Library" with the Library to produce a
-work containing portions of the Library, and distribute that work
-under terms of your choice, provided that the terms permit
-modification of the work for the customer's own use and reverse
-engineering for debugging such modifications.
-
-  You must give prominent notice with each copy of the work that the
-Library is used in it and that the Library and its use are covered by
-this License.  You must supply a copy of this License.  If the work
-during execution displays copyright notices, you must include the
-copyright notice for the Library among them, as well as a reference
-directing the user to the copy of this License.  Also, you must do one
-of these things:
-
-    a) Accompany the work with the complete corresponding
-    machine-readable source code for the Library including whatever
-    changes were used in the work (which must be distributed under
-    Sections 1 and 2 above); and, if the work is an executable linked
-    with the Library, with the complete machine-readable "work that
-    uses the Library", as object code and/or source code, so that the
-    user can modify the Library and then relink to produce a modified
-    executable containing the modified Library.  (It is understood
-    that the user who changes the contents of definitions files in the
-    Library will not necessarily be able to recompile the application
-    to use the modified definitions.)
-
-    b) Use a suitable shared library mechanism for linking with the
-    Library.  A suitable mechanism is one that (1) uses at run time a
-    copy of the library already present on the user's computer system,
-    rather than copying library functions into the executable, and (2)
-    will operate properly with a modified version of the library, if
-    the user installs one, as long as the modified version is
-    interface-compatible with the version that the work was made with.
-
-    c) Accompany the work with a written offer, valid for at least
-    three years, to give the same user the materials specified in
-    Subsection 6a, above, for a charge no more than the cost of
-    performing this distribution.
-
-    d) If distribution of the work is made by offering access to copy
-    from a designated place, offer equivalent access to copy the above
-    specified materials from the same place.
-
-    e) Verify that the user has already received a copy of these
-    materials or that you have already sent this user a copy.
-
-  For an executable, the required form of the "work that uses the
-Library" must include any data and utility programs needed for
-reproducing the executable from it.  However, as a special exception,
-the materials to be distributed need not include anything that is
-normally distributed (in either source or binary form) with the major
-components (compiler, kernel, and so on) of the operating system on
-which the executable runs, unless that component itself accompanies
-the executable.
-
-  It may happen that this requirement contradicts the license
-restrictions of other proprietary libraries that do not normally
-accompany the operating system.  Such a contradiction means you cannot
-use both them and the Library together in an executable that you
-distribute.
-\f
-  7. You may place library facilities that are a work based on the
-Library side-by-side in a single library together with other library
-facilities not covered by this License, and distribute such a combined
-library, provided that the separate distribution of the work based on
-the Library and of the other library facilities is otherwise
-permitted, and provided that you do these two things:
-
-    a) Accompany the combined library with a copy of the same work
-    based on the Library, uncombined with any other library
-    facilities.  This must be distributed under the terms of the
-    Sections above.
-
-    b) Give prominent notice with the combined library of the fact
-    that part of it is a work based on the Library, and explaining
-    where to find the accompanying uncombined form of the same work.
-
-  8. You may not copy, modify, sublicense, link with, or distribute
-the Library except as expressly provided under this License.  Any
-attempt otherwise to copy, modify, sublicense, link with, or
-distribute the Library is void, and will automatically terminate your
-rights under this License.  However, parties who have received copies,
-or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
-
-  9. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Library or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
-
-  10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the
-original licensor to copy, distribute, link with or modify the Library
-subject to these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties with
-this License.
-\f
-  11. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Library at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Library by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Library.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply, and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  12. If the distribution and/or use of the Library is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library under this License
-may add an explicit geographical distribution limitation excluding those
-countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  13. The Free Software Foundation may publish revised and/or new
-versions of the Lesser General Public License from time to time.
-Such new versions will be similar in spirit to the present version,
-but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Library
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation.  If the Library does not specify a
-license version number, you may choose any version ever published by
-the Free Software Foundation.
-\f
-  14. If you wish to incorporate parts of the Library into other free
-programs whose distribution conditions are incompatible with these,
-write to the author to ask for permission.  For software which is
-copyrighted by the Free Software Foundation, write to the Free
-Software Foundation; we sometimes make exceptions for this.  Our
-decision will be guided by the two goals of preserving the free status
-of all derivatives of our free software and of promoting the sharing
-and reuse of software generally.
-
-                            NO WARRANTY
-
-  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
-LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
-RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
-FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
-
-                     END OF TERMS AND CONDITIONS
-\f
-           How to Apply These Terms to Your New Libraries
-
-  If you develop a new library, and you want it to be of the greatest
-possible use to the public, we recommend making it free software that
-everyone can redistribute and change.  You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms
-of the ordinary General Public License).
-
-  To apply these terms, attach the following notices to the library.
-It is safest to attach them to the start of each source file to most
-effectively convey the exclusion of warranty; and each file should
-have at least the "copyright" line and a pointer to where the full
-notice is found.
-
-
-    <one line to give the library's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or
-your school, if any, to sign a "copyright disclaimer" for the library,
-if necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the
-  library `Frob' (a library for tweaking knobs) written by James
-  Random Hacker.
-
-  <signature of Ty Coon>, 1 April 1990
-  Ty Coon, President of Vice
-
-That's all there is to it!
-
-
index 03e9885..33d59e3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 VERSION = 1
-PATCHLEVEL = 17
+PATCHLEVEL = 22
 SUBLEVEL = 1
 EXTRAVERSION =
 NAME = Unnamed
@@ -297,6 +297,7 @@ NM          = $(CROSS_COMPILE)nm
 STRIP          = $(CROSS_COMPILE)strip
 OBJCOPY                = $(CROSS_COMPILE)objcopy
 OBJDUMP                = $(CROSS_COMPILE)objdump
+PKG_CONFIG     ?= $(CROSS_COMPILE)pkg-config
 AWK            = awk
 GENKSYMS       = scripts/genksyms/genksyms
 DEPMOD         = /sbin/depmod
@@ -433,7 +434,12 @@ ifeq ($(config-targets),1)
 -include $(srctree)/arch/$(ARCH)/Makefile
 export KBUILD_DEFCONFIG
 
-config %config: scripts_basic outputmakefile gen_build_files FORCE
+config: scripts_basic outputmakefile gen_build_files FORCE
+       $(Q)mkdir -p include
+       $(Q)$(MAKE) $(build)=scripts/kconfig $@
+       $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= .kernelrelease
+
+%config: scripts_basic outputmakefile gen_build_files FORCE
        $(Q)mkdir -p include
        $(Q)$(MAKE) $(build)=scripts/kconfig $@
        $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= .kernelrelease
@@ -459,7 +465,7 @@ core-y              := \
 
 libs-y         := \
                archival/ \
-               archival/libunarchive/ \
+               archival/libarchive/ \
                console-tools/ \
                coreutils/ \
                coreutils/libcoreutils/ \
@@ -837,7 +843,7 @@ export CPPFLAGS_busybox.lds += -P -C -U$(ARCH)
 
 #      Split autoconf.h into include/linux/config/*
 quiet_cmd_gen_bbconfigopts = GEN     include/bbconfigopts.h
-      cmd_gen_bbconfigopts = $(srctree)/scripts/mkconfigs > include/bbconfigopts.h
+      cmd_gen_bbconfigopts = $(srctree)/scripts/mkconfigs include/bbconfigopts.h include/bbconfigopts_bz2.h
 quiet_cmd_split_autoconf   = SPLIT   include/autoconf.h -> include/config/*
       cmd_split_autoconf   = scripts/basic/split-include include/autoconf.h include/config
 #bbox# piggybacked generation of few .h files
@@ -958,10 +964,14 @@ CLEAN_FILES +=    busybox busybox_unstripped* busybox.links \
 # Directories & files removed with 'make mrproper'
 MRPROPER_DIRS  += include/config include2
 MRPROPER_FILES += .config .config.old include/asm .version .old_version \
+                 include/NUM_APPLETS.h \
                  include/autoconf.h \
                  include/bbconfigopts.h \
+                 include/bbconfigopts_bz2.h \
                  include/usage_compressed.h \
                  include/applet_tables.h \
+                 include/applets.h \
+                 include/usage.h \
                  applets/usage \
                  .kernelrelease Module.symvers tags TAGS cscope* \
                  busybox_old
@@ -986,7 +996,7 @@ clean: archclean $(clean-dirs)
 
 PHONY += doc-clean
 doc-clean: rm-files := docs/busybox.pod \
-                 docs/BusyBox.html docs/BusyBox.1 docs/BusyBox.txt
+                 docs/BusyBox.html docs/busybox.1 docs/BusyBox.txt
 doc-clean:
        $(call cmd,rmfiles)
 
@@ -1003,8 +1013,8 @@ $(mrproper-dirs):
 mrproper: clean archmrproper $(mrproper-dirs)
        $(call cmd,rmdirs)
        $(call cmd,rmfiles)
-       @find -name Config.src | sed 's/.src$$/.in/' | xargs -r rm -f
-       @find -name Kbuild.src | sed 's/.src$$//' | xargs -r rm -f
+       @find -name Config.src | sed 's/.src$$/.in/' | xargs -r rm -f
+       @find -name Kbuild.src | sed 's/.src$$//' | xargs -r rm -f
 
 # distclean
 #
@@ -1033,7 +1043,7 @@ rpm: FORCE
 # Brief documentation of the typical targets used
 # ---------------------------------------------------------------------------
 
-boards := $(wildcard $(srctree)/arch/$(ARCH)/configs/*_defconfig)
+boards := $(wildcard $(srctree)/configs/*_defconfig)
 boards := $(notdir $(boards))
 
 -include $(srctree)/Makefile.help
@@ -1122,15 +1132,6 @@ clean: $(clean-dirs)
                -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \
                -type f -print | xargs rm -f
 
-help:
-       @echo  '  Building external modules.'
-       @echo  '  Syntax: make -C path/to/kernel/src M=$$PWD target'
-       @echo  ''
-       @echo  '  modules         - default target, build the module(s)'
-       @echo  '  modules_install - install the module'
-       @echo  '  clean           - remove generated files in module directory only'
-       @echo  ''
-
 # Dummies...
 PHONY += prepare scripts
 prepare: ;
@@ -1285,9 +1286,13 @@ endif
        $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
 
 # Modules
-/ %/: prepare scripts FORCE
+%/: prepare scripts FORCE
+       $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \
+       $(build)=$(build-dir)
+/: prepare scripts FORCE
        $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \
        $(build)=$(build-dir)
+
 %.ko: prepare scripts FORCE
        $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1)   \
        $(build)=$(build-dir) $(@:.ko=.o)
index 01d69dd..8c95ef2 100644 (file)
@@ -3,7 +3,12 @@
 # ==========================================================================
 
 busybox.links: $(srctree)/applets/busybox.mkll $(objtree)/include/autoconf.h include/applets.h
-       $(Q)-$(SHELL) $^ >$@
+       $(Q)-$(SHELL) $^ > $@
+
+busybox.cfg.suid: $(srctree)/applets/busybox.mksuid $(objtree)/include/autoconf.h include/applets.h
+       $(Q)-SUID="yes" $(SHELL) $^ > $@
+busybox.cfg.nosuid: $(srctree)/applets/busybox.mksuid $(objtree)/include/autoconf.h include/applets.h
+       $(Q)-SUID="DROP" $(SHELL) $^ > $@
 
 .PHONY: install
 ifeq ($(CONFIG_INSTALL_APPLET_SYMLINKS),y)
@@ -69,6 +74,10 @@ release: distclean
                -print \
                -exec rm -r -f {} \; ; \
        find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type f \
+               -name .gitignore \
+               -print \
+               -exec rm -f {} \; ; \
+       find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type f \
                -name .\#* \
                -print \
                -exec rm -f {} \; ; \
@@ -107,7 +116,7 @@ bigdata: busybox_unstripped
 
 # Documentation Targets
 .PHONY: doc
-doc: docs/busybox.pod docs/BusyBox.txt docs/BusyBox.1 docs/BusyBox.html
+doc: docs/busybox.pod docs/BusyBox.txt docs/busybox.1 docs/BusyBox.html
 
 # FIXME: Doesn't belong here
        cmd_doc =
@@ -125,6 +134,7 @@ docs/busybox.pod: $(srctree)/docs/busybox_header.pod \
        $(Q)-mkdir -p docs
        $(Q)-( \
            cat $(srctree)/docs/busybox_header.pod; \
+           echo; \
            applets/usage_pod | sed 's/^[A-Za-z][A-Za-z ]*[a-z]:$$/&\n/'; \
            cat $(srctree)/docs/busybox_footer.pod; \
            ) > docs/busybox.pod
@@ -134,10 +144,10 @@ docs/BusyBox.txt: docs/busybox.pod
        $(Q)-mkdir -p docs
        $(Q)-pod2text $< > $@
 
-docs/BusyBox.1: docs/busybox.pod
+docs/busybox.1: docs/busybox.pod
        $(disp_doc)
        $(Q)-mkdir -p docs
-       $(Q)-pod2man --center=BusyBox --release="version $(KERNELVERSION)" $< > $@
+       $(Q)-pod2man --center=busybox --release="version $(KERNELVERSION)" $< > $@
 
 docs/BusyBox.html: docs/busybox.net/BusyBox.html
        $(disp_doc)
index 1ffa738..307afa7 100644 (file)
@@ -4,7 +4,7 @@
 
 BB_VER = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 export BB_VER
-SKIP_STRIP = n
+SKIP_STRIP ?= n
 
 # -std=gnu99 needed for [U]LLONG_MAX on some systems
 CPPFLAGS += $(call cc-option,-std=gnu99,)
@@ -25,6 +25,7 @@ CFLAGS += $(call cc-option,-Wstrict-prototypes,)
 CFLAGS += $(call cc-option,-Wunused -Wunused-parameter,)
 CFLAGS += $(call cc-option,-Wunused-function -Wunused-value,)
 CFLAGS += $(call cc-option,-Wmissing-prototypes -Wmissing-declarations,)
+CFLAGS += $(call cc-option,-Wno-format-security,)
 # warn about C99 declaration after statement
 CFLAGS += $(call cc-option,-Wdeclaration-after-statement,)
 # If you want to add more -Wsomething above, make sure that it is
@@ -52,20 +53,23 @@ CFLAGS += $(call cc-option,-fno-builtin-strlen -finline-limit=0 -fomit-frame-poi
 CFLAGS += $(call cc-option,-fno-guess-branch-probability,)
 CFLAGS += $(call cc-option,-funsigned-char -static-libgcc,)
 CFLAGS += $(call cc-option,-falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1,)
+# Defeat .eh_frame bloat (gcc 4.6.3 x86-32 defconfig: 20% smaller busybox binary):
+CFLAGS += $(call cc-option,-fno-unwind-tables,)
+CFLAGS += $(call cc-option,-fno-asynchronous-unwind-tables,)
 
 # FIXME: These warnings are at least partially to be concerned about and should
 # be fixed..
 #CFLAGS += $(call cc-option,-Wconversion,)
 
 ifneq ($(CONFIG_DEBUG),y)
-CFLAGS += $(call cc-option,-Os,)
+CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,))
 else
 CFLAGS += $(call cc-option,-g,)
 #CFLAGS += "-D_FORTIFY_SOURCE=2"
 ifeq ($(CONFIG_DEBUG_PESSIMIZE),y)
 CFLAGS += $(call cc-option,-O0,)
 else
-CFLAGS += $(call cc-option,-Os,)
+CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,))
 endif
 endif
 
@@ -74,6 +78,12 @@ ARCH_FPIC ?= -fpic
 ARCH_FPIE ?= -fpie
 ARCH_PIE ?= -pie
 
+# Usage: $(eval $(call pkg_check_modules,VARIABLE-PREFIX,MODULES))
+define pkg_check_modules
+$(1)_CFLAGS := $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --cflags $(2))
+$(1)_LIBS := $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --libs $(2))
+endef
+
 ifeq ($(CONFIG_BUILD_LIBBUSYBOX),y)
 # on i386: 14% smaller libbusybox.so
 # (code itself is 9% bigger, we save on relocs/PLT/GOT)
@@ -85,6 +95,7 @@ endif
 
 ifeq ($(CONFIG_STATIC),y)
 CFLAGS_busybox += -static
+PKG_CONFIG_FLAGS += --static
 endif
 
 ifeq ($(CONFIG_PIE),y)
@@ -97,14 +108,40 @@ CFLAGS += $(strip $(subst ",,$(CONFIG_EXTRA_CFLAGS)))
 #"))
 endif
 
+# Note: both "" (string consisting of two quote chars) and empty string
+# are possible, and should be skipped below.
+ifneq ($(subst "",,$(CONFIG_SYSROOT)),)
+CFLAGS += --sysroot=$(CONFIG_SYSROOT)
+export SYSROOT=$(CONFIG_SYSROOT)
+endif
+
+# Android has no separate crypt library
+# gcc-4.2.1 fails if we try to feed C source on stdin:
+#  echo 'int main(void){return 0;}' | $(CC) $(CFLAGS) -lcrypt -o /dev/null -xc -
+# fall back to using a temp file:
+CRYPT_AVAILABLE := $(shell echo 'int main(void){return 0;}' >crypttest.c; $(CC) $(CFLAGS) -lcrypt -o /dev/null crypttest.c >/dev/null 2>&1 && echo "y"; rm crypttest.c)
+ifeq ($(CRYPT_AVAILABLE),y)
 LDLIBS += m crypt
+else
+LDLIBS += m
+endif
 
 ifeq ($(CONFIG_PAM),y)
-LDLIBS += pam pam_misc
+# libpam uses libpthread, so for static builds busybox must be linked to
+# libpthread. On some platforms that requires an explicit -lpthread, so
+# it should be in LDLIBS. For non-static builds, scripts/trylink will
+# take care of removing -lpthread if possible. (Not bothering to check
+# CONFIG_STATIC because even in a non-static build it could be that the
+# only libpam available is libpam.a, so -lpthread could still be
+# needed.)
+LDLIBS += pam pam_misc pthread
 endif
 
 ifeq ($(CONFIG_SELINUX),y)
-LDLIBS += selinux sepol
+SELINUX_PC_MODULES = libselinux libsepol
+$(eval $(call pkg_check_modules,SELINUX,$(SELINUX_PC_MODULES)))
+CPPFLAGS += $(SELINUX_CFLAGS)
+LDLIBS += $(if $(SELINUX_LIBS),$(SELINUX_LIBS:-l%=%),$(SELINUX_PC_MODULES:lib%=%))
 endif
 
 ifeq ($(CONFIG_EFENCE),y)
@@ -115,10 +152,6 @@ ifeq ($(CONFIG_DMALLOC),y)
 LDLIBS += dmalloc
 endif
 
-ifeq ($(CONFIG_SYSLOGD),y)
-LDLIBS += systemd-daemon
-endif
-
 # If a flat binary should be built, CFLAGS_busybox="-elf2flt"
 # env var should be set for make invocation.
 # Here we check whether CFLAGS_busybox indeed contains that flag.
@@ -129,6 +162,16 @@ ifneq (,$(findstring $(W_ELF2FLT),$(LDFLAGS) $(CFLAGS_busybox)))
 SKIP_STRIP = y
 endif
 
+ifneq ($(CONFIG_EXTRA_LDFLAGS),)
+EXTRA_LDFLAGS += $(strip $(subst ",,$(CONFIG_EXTRA_LDFLAGS)))
+#"))
+endif
+
+ifneq ($(CONFIG_EXTRA_LDLIBS),)
+LDLIBS += $(strip $(subst ",,$(CONFIG_EXTRA_LDLIBS)))
+#"))
+endif
+
 # Busybox is a stack-fatty so make sure we increase default size
 # TODO: use "make stksizes" to find & fix big stack users
 # (we stole scripts/checkstack.pl from the kernel... thanks guys!)
index 999d029..119dd6f 100644 (file)
@@ -25,6 +25,10 @@ help:
        @echo '                           You can use these commands if the commands on the host'
        @echo '                           is unusable. Afterwards use it like:'
        @echo '                           make SED="$(objtree)/sed"'
+       @$(if $(boards), \
+               $(foreach b, $(boards), \
+               printf "  %-21s - Build for %s\\n" $(b) $(subst _defconfig,,$(b));) \
+               echo '')
        @echo
        @echo 'Installation:'
        @echo '  install                - install busybox into CONFIG_PREFIX'
diff --git a/README b/README
index 3a9d849..b940e35 100644 (file)
--- a/README
+++ b/README
@@ -44,7 +44,7 @@ Using busybox:
   run (I.E. "./busybox ls -l /proc").
 
   The "standalone shell" mode is an easy way to try out busybox; this is a
-  command shell that calls the builtin applets without needing them to be
+  command shell that calls the built-in applets without needing them to be
   installed in the path.  (Note that this requires /proc to be mounted, if
   testing from a boot floppy or in a chroot environment.)
 
@@ -80,7 +80,7 @@ Downloading the current source code:
   The developers also have a bug and patch tracking system
   (https://bugs.busybox.net) although posting a bug/patch to the mailing list
   is generally a faster way of getting it fixed, and the complete archive of
-  what happened is the subversion changelog.
+  what happened is the git changelog.
 
   Note: if you want to compile busybox in a busybox environment you must
   select CONFIG_DESKTOP.
@@ -169,7 +169,7 @@ Portability:
   MacOS X, Solaris, Cygwin, or the BSD Fork Du Jour).  This generally involves
   a different kernel and a different C library at the same time.  While it
   should be possible to port the majority of the code to work in one of
-  these environments, don't be suprised if it doesn't work out of the box.  If
+  these environments, don't be surprised if it doesn't work out of the box.  If
   you're into that sort of thing, start small (selecting just a few applets)
   and work your way up.
 
diff --git a/TODO b/TODO
index 6f8cd8a..dcf48c2 100644 (file)
--- a/TODO
+++ b/TODO
@@ -2,6 +2,8 @@ Busybox TODO
 
 Harvest patches from
 http://git.openembedded.org/cgit.cgi/openembedded/tree/recipes/busybox/
+https://dev.openwrt.org/browser/trunk/package/busybox/patches/
+
 
 Stuff that needs to be done.  This is organized by who plans to get around to
 doing it eventually, but that doesn't mean they "own" the item.  If you want to
@@ -82,7 +84,7 @@ Rob Landley suggested this:
 
   initramfs
     Busybox should have a sample initramfs build script.  This depends on
-    bbsh, mdev, and switch_root.
+    shell, mdev, and switch_root.
 
   mkdep
     Write a mkdep that doesn't segfault if there's a directory it doesn't
@@ -125,20 +127,6 @@ patch
 
   And while we're at it, a new patch filename quoting format is apparently
   coming soon:  http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
----
-stty / catv
-  stty's visible() function and catv's guts are identical. Merge them into
-  an appropriate libbb function.
----
-struct suffix_mult
-  Several duplicate users of: grep -r "1024\*1024" * -B2 -A1
-  Merge to a single size_suffixes[] in libbb.
-  Users: head tail od_bloaty hexdump and (partially as it wouldn't hurt) svlogd
----
-tail
-  ./busybox tail -f foo.c~ TODO
-  should not print fmt=header_fmt for subsequent date >> TODO; i.e. only
-  fmt+ if another (not the current) file did change
 
 Architectural issues:
 
@@ -232,8 +220,6 @@ Minor stuff:
   See grep -r strtod
   Alot of duplication that wants cleanup.
 ---
-  in_ether duplicated in network/{interface,ifconfig}.c
----
   unify progress_meter. wget, flash_eraseall, pipe_progress, fbsplash, setfiles.
 ---
   support start-stop-daemon -d <chdir-path>
index cc932fc..459938d 100644 (file)
@@ -1,2 +1,3 @@
 /applet_tables
 /usage
+/usage_pod
index 31fee8d..b612399 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 obj-y :=
 obj-y += applets.o
@@ -42,3 +42,6 @@ quiet_cmd_gen_applet_tables = GEN     include/applet_tables.h
 
 include/applet_tables.h: applets/applet_tables
        $(call cmd,gen_applet_tables)
+
+include/NUM_APPLETS.h: applets/applet_tables
+       $(call cmd,gen_applet_tables)
index 338dc20..94b974e 100644 (file)
@@ -5,15 +5,21 @@
  *
  * Copyright (C) 2007 Denys Vlasenko <vda.linux@googlemail.com>
  *
- * Licensed under GPLv2, see file License in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
-
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
+#include <unistd.h>
+
+#undef ARRAY_SIZE
+#define ARRAY_SIZE(x) ((unsigned)(sizeof(x) / sizeof((x)[0])))
 
 #include "../include/autoconf.h"
-#include "../include/busybox.h"
+#include "../include/applet_metadata.h"
 
 struct bb_applet {
        const char *name;
@@ -47,7 +53,7 @@ int main(int argc, char **argv)
 {
        int i;
        int ofs;
-       unsigned MAX_APPLET_NAME_LEN = 1;
+//     unsigned MAX_APPLET_NAME_LEN = 1;
 
        qsort(applets, NUM_APPLETS, sizeof(applets[0]), cmp_name);
 
@@ -75,7 +81,7 @@ int main(int argc, char **argv)
        printf("#define NUM_APPLETS %u\n", NUM_APPLETS);
        if (NUM_APPLETS == 1) {
                printf("#define SINGLE_APPLET_STR \"%s\"\n", applets[0].name);
-               printf("#define SINGLE_APPLET_MAIN %s_main\n", applets[0].name);
+               printf("#define SINGLE_APPLET_MAIN %s_main\n", applets[0].main);
        }
        printf("\n");
 
@@ -83,8 +89,8 @@ int main(int argc, char **argv)
        printf("const char applet_names[] ALIGN1 = \"\"\n");
        for (i = 0; i < NUM_APPLETS; i++) {
                printf("\"%s\" \"\\0\"\n", applets[i].name);
-               if (MAX_APPLET_NAME_LEN < strlen(applets[i].name))
-                       MAX_APPLET_NAME_LEN = strlen(applets[i].name);
+//             if (MAX_APPLET_NAME_LEN < strlen(applets[i].name))
+//                     MAX_APPLET_NAME_LEN = strlen(applets[i].name);
        }
        printf(";\n\n");
 
@@ -124,8 +130,8 @@ int main(int argc, char **argv)
        printf("};\n");
 #endif
        //printf("#endif /* SKIP_definitions */\n");
-       printf("\n");
-       printf("#define MAX_APPLET_NAME_LEN %u\n", MAX_APPLET_NAME_LEN);
+//     printf("\n");
+//     printf("#define MAX_APPLET_NAME_LEN %u\n", MAX_APPLET_NAME_LEN);
 
        if (argv[2]) {
                char line_old[80];
index 6a39962..98c2b44 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2007 Denys Vlasenko <vda.linux@googlemail.com>
  *
- * Licensed under GPLv2, see file License in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "busybox.h"
 
index 6d61f7e..68dbf21 100755 (executable)
@@ -14,7 +14,7 @@ CONFIG_H=${1:-include/autoconf.h}
 APPLETS_H=${2:-include/applets.h}
 $HOSTCC -E -DMAKE_LINKS -include $CONFIG_H $APPLETS_H |
   awk '/^[ \t]*LINK/{
-       dir=substr($2,8)
+       dir=substr($2,7)
        gsub("_","/",dir)
        if(dir=="/ROOT") dir=""
        file=$3
diff --git a/applets/busybox.mksuid b/applets/busybox.mksuid
new file mode 100755 (executable)
index 0000000..6492c07
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/sh
+# Make list of configuration variables regarding suid handling
+
+# input $1: full path to autoconf.h
+# input $2: full path to applets.h
+# input $3: full path to .config
+# output (stdout): list of CONFIG_ that do or may require suid
+
+# If the environment variable SUID is not set or set to DROP,
+# lists all config options that do not require suid permissions.
+# Otherwise, lists all config options for applets that DO or MAY require
+# suid permissions.
+
+# Maintainer: Bernhard Reutner-Fischer
+
+export LC_ALL=POSIX
+export LC_CTYPE=POSIX
+
+CONFIG_H=${1:-include/autoconf.h}
+APPLETS_H=${2:-include/applets.h}
+DOT_CONFIG=${3:-.config}
+
+case ${SUID:-DROP} in
+[dD][rR][oO][pP]) USE="DROP" ;;
+*) USE="suid" ;;
+esac
+
+$HOSTCC -E -DMAKE_SUID -include $CONFIG_H $APPLETS_H |
+  awk -v USE=${USE} '
+    /^SUID[ \t]/{
+      if (USE == "DROP") {
+        if ($2 != "BB_SUID_DROP") next
+      } else {
+        if ($2 == "BB_SUID_DROP") next
+      }
+      cfg = $NF
+      gsub("\"", "", cfg)
+      cfg = substr(cfg, 8)
+      s[i++] = "CONFIG_" cfg
+      s[i++] = "CONFIG_FEATURE_" cfg "_.*"
+    }
+    END{
+      while (getline < ARGV[2]) {
+        for (j in s) {
+          if ($0 ~ "^" s[j] "=y$") {
+            sub(/=.*/, "")
+            print
+            if (s[j] !~ /\*$/) delete s[j] # can drop this applet now
+          }
+        }
+      }
+    }
+' - $DOT_CONFIG
+
index 341f4d1..4c468df 100644 (file)
@@ -2,7 +2,7 @@
  *
  * Copyright 2005 Rob Landley <rob@landley.net
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 const char *applet_name;
@@ -14,7 +14,7 @@ const char *applet_name;
 int main(int argc, char **argv)
 {
        applet_name = argv[0];
-       return APPLET_main(argc,argv);
+       return APPLET_main(argc, argv);
 }
 
 void bb_show_usage(void)
index 32049b1..95b4719 100755 (executable)
@@ -3,12 +3,15 @@
 export LC_ALL=POSIX
 export LC_CTYPE=POSIX
 
-prefix=${1}
+prefix=$1
 if [ -z "$prefix" ]; then
        echo "usage: applets/install.sh DESTINATION [--symlinks/--hardlinks/--scriptwrapper]"
-       exit 1;
+       exit 1
 fi
+
 h=`sort busybox.links | uniq`
+
+linkopts=""
 scriptwrapper="n"
 cleanup="0"
 noclobber="0"
@@ -33,12 +36,12 @@ if [ -n "$DO_INSTALL_LIBS" ] && [ "$DO_INSTALL_LIBS" != "n" ]; then
                libdir=/lib
        fi
 
-       mkdir -p $prefix/$libdir || exit 1
+       mkdir -p "$prefix/$libdir" || exit 1
        for i in $DO_INSTALL_LIBS; do
-               rm -f $prefix/$libdir/$i || exit 1
-               if [ -f $i ]; then
-                       cp -pPR $i $prefix/$libdir/ || exit 1
-                       chmod 0644 $prefix/$libdir/$i || exit 1
+               rm -f "$prefix/$libdir/$i" || exit 1
+               if [ -f "$i" ]; then
+                       cp -pPR "$i" "$prefix/$libdir/" || exit 1
+                       chmod 0644 "$prefix/$libdir/$i" || exit 1
                fi
        done
 fi
@@ -46,35 +49,35 @@ fi
 if [ "$cleanup" = "1" ] && [ -e "$prefix/bin/busybox" ]; then
        inode=`ls -i "$prefix/bin/busybox" | awk '{print $1}'`
        sub_shell_it=`
-       cd "$prefix"
-       for d in usr/sbin usr/bin sbin bin; do
-               pd=$PWD
-               if [ -d "$d" ]; then
-                       cd $d
-                       ls -iL . | grep "^ *$inode" | awk '{print $2}' | env -i xargs rm -f
-               fi
-               cd "$pd"
-       done
-       `
+               cd "$prefix"
+               for d in usr/sbin usr/bin sbin bin; do
+                       pd=$PWD
+                       if [ -d "$d" ]; then
+                               cd "$d"
+                               ls -iL . | grep "^ *$inode" | awk '{print $2}' | env -i xargs rm -f
+                       fi
+                       cd "$pd"
+               done
+               `
        exit 0
 fi
 
-rm -f $prefix/bin/busybox || exit 1
-mkdir -p $prefix/bin || exit 1
-install -m 755 busybox $prefix/bin/busybox || exit 1
+rm -f "$prefix/bin/busybox" || exit 1
+mkdir -p "$prefix/bin" || exit 1
+install -m 755 busybox "$prefix/bin/busybox" || exit 1
 
 for i in $h; do
-       appdir=`dirname $i`
-       mkdir -p $prefix/$appdir || exit 1
+       appdir=`dirname "$i"`
+       mkdir -p "$prefix/$appdir" || exit 1
        if [ "$scriptwrapper" = "y" ]; then
                if [ "$swrapall" != "y" ] && [ "$i" = "/bin/sh" ]; then
-                       ln $linkopts busybox $prefix$i || exit 1
+                       ln $linkopts busybox "$prefix/$i" || exit 1
                else
-                       rm -f $prefix$i
-                       echo "#!/bin/busybox" > $prefix$i
-                       chmod +x $prefix/$i
+                       rm -f "$prefix/$i"
+                       echo "#!/bin/busybox" >"$prefix/$i"
+                       chmod +x "$prefix/$i"
                fi
-               echo "  $prefix$i"
+               echo "  $prefix/$i"
        else
                if [ "$2" = "--hardlinks" ]; then
                        bb_path="$prefix/bin/busybox"
@@ -89,20 +92,20 @@ for i in $h; do
                        /sbin)
                                bb_path="../bin/busybox"
                        ;;
-                       /usr/bin|/usr/sbin)
+                       /usr/bin | /usr/sbin)
                                bb_path="../../bin/busybox"
                        ;;
                        *)
-                       echo "Unknown installation directory: $appdir"
-                       exit 1
+                               echo "Unknown installation directory: $appdir"
+                               exit 1
                        ;;
                        esac
                fi
-               if [ "$noclobber" = "0" ] || [ ! -e "$prefix$i" ]; then
-                       echo "  $prefix$i -> $bb_path"
-                       ln $linkopts $bb_path $prefix$i || exit 1
+               if [ "$noclobber" = "0" ] || [ ! -e "$prefix/$i" ]; then
+                       echo "  $prefix/$i -> $bb_path"
+                       ln $linkopts "$bb_path" "$prefix/$i" || exit 1
                else
-                       echo "  $prefix$i already exists"
+                       echo "  $prefix/$i already exists"
                fi
        fi
 done
index 46adbf4..94520ff 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2008 Denys Vlasenko.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include <unistd.h>
 #include <stdlib.h>
index e1fd0d9..fb6e1c2 100755 (executable)
@@ -9,14 +9,21 @@ test -x "$loc/usage" || exit 1
 test "$SED" || SED=sed
 test "$DD" || DD=dd
 
+# Some people were bitten by their system lacking a (proper) od
+od -v -b </dev/null >/dev/null
+if test $? != 0; then
+       echo 'od tool is not installed or cannot accept "-v -b" options'
+       exit 1
+fi
+
 exec >"$target.$$"
 
 echo '#define UNPACKED_USAGE "" \'
-"$loc/usage" | od -v -t x1 \
+"$loc/usage" | od -v -b \
 | $SED -e 's/^[^ ]*//' \
        -e 's/ //g' \
        -e '/^$/d' \
-       -e 's/\(..\)/\\x\1/g' \
+       -e 's/\(...\)/\\\1/g' \
        -e 's/^/"/' \
        -e 's/$/" \\/'
 echo ''
@@ -32,11 +39,11 @@ echo '#define PACKED_USAGE \'
 ##     -e '/^$/d' \
 ##     -e 's/\(..\)\(..\)/0x\2,0x\1,/g'
 ##     -e 's/$/ \\/'
-"$loc/usage" | bzip2 -1 | $DD bs=2 skip=1 2>/dev/null | od -v -t x1 \
+"$loc/usage" | bzip2 -1 | $DD bs=2 skip=1 2>/dev/null | od -v -b \
 | $SED -e 's/^[^ ]*//' \
        -e 's/ //g' \
        -e '/^$/d' \
-       -e 's/\(..\)/0x\1,/g' \
+       -e 's/\(...\)/0\1,/g' \
        -e 's/$/ \\/'
 echo ''
 
index 85a2a8e..0b1c4aa 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2009 Denys Vlasenko.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include <unistd.h>
 #include <stdint.h>
@@ -31,8 +31,8 @@
 #include "usage.h"
 #define MAKE_USAGE(aname, usage) { aname, usage },
 static struct usage_data {
-        const char *aname;
-        const char *usage;
+       const char *aname;
+       const char *usage;
 } usage_array[] = {
 #include "applets.h"
 };
diff --git a/applets_sh/README b/applets_sh/README
new file mode 100644 (file)
index 0000000..9dcd38a
--- /dev/null
@@ -0,0 +1,5 @@
+This directory contains examples of applets implemented as shell scripts.
+
+So far these scripts are not hooked to the build system and are not
+installed by "make install". If you want to use them,
+you need to install them by hand.
diff --git a/applets_sh/dos2unix b/applets_sh/dos2unix
new file mode 100755 (executable)
index 0000000..0fd5206
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+# TODO: use getopt to avoid parsing options as filenames,
+# and to support -- and --help
+[ $# -ne 0 ] && DASH_I=-i
+sed $DASH_I -e 's/\r$//' "$@"
diff --git a/applets_sh/nologin b/applets_sh/nologin
new file mode 100755 (executable)
index 0000000..3768eaa
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+cat /etc/nologin.txt 2>/dev/null || echo "This account is not available"
+sleep 5
+exit 1
diff --git a/applets_sh/tac b/applets_sh/tac
new file mode 100755 (executable)
index 0000000..c5a8e39
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/sh
+# TODO: use getopt to avoid parsing options as filenames,
+# and to support -- and --help
+for i in "$@"
+do
+sed -e '1!G;h;$!d' "$i"
+done
diff --git a/applets_sh/unix2dos b/applets_sh/unix2dos
new file mode 100755 (executable)
index 0000000..70e0429
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+# TODO: use getopt to avoid parsing options as filenames,
+# and to support -- and --help
+[ $# -ne 0 ] && DASH_I=-i
+sed $DASH_I -e 's/$/\r/' "$@"
index 9a84fd6..76635ba 100644 (file)
@@ -5,8 +5,6 @@
 
 menu "Archival Utilities"
 
-INSERT
-
 config FEATURE_SEAMLESS_XZ
        bool "Make tar, rpm, modprobe etc understand .xz data"
        default y
@@ -32,348 +30,11 @@ config FEATURE_SEAMLESS_GZ
          Make tar, rpm, modprobe etc understand .gz data.
 
 config FEATURE_SEAMLESS_Z
-       bool "Make tar and gunzip understand .Z data"
-       default n
-       help
-         Make tar and gunzip understand .Z data.
-
-config AR
-       bool "ar"
-       default n  # needs to be improved to be able to replace binutils ar
-       help
-         ar is an archival utility program used to create, modify, and
-         extract contents from archives. An archive is a single file holding
-         a collection of other files in a structure that makes it possible to
-         retrieve the original individual files (called archive members).
-         The original files' contents, mode (permissions), timestamp, owner,
-         and group are preserved in the archive, and can be restored on
-         extraction.
-
-         The stored filename is limited to 15 characters. (for more information
-         see long filename support).
-         ar has 60 bytes of overheads for every stored file.
-
-         This implementation of ar can extract archives, it cannot create or
-         modify them.
-         On an x86 system, the ar applet adds about 1K.
-
-         Unless you have a specific application which requires ar, you should
-         probably say N here.
-
-config FEATURE_AR_LONG_FILENAMES
-       bool "Support for long filenames (not needed for debs)"
-       default y
-       depends on AR
-       help
-         By default the ar format can only store the first 15 characters
-         of the filename, this option removes that limitation.
-         It supports the GNU ar long filename method which moves multiple long
-         filenames into a the data section of a new ar entry.
-
-config FEATURE_AR_CREATE
-       bool "Support archive creation"
-       default y
-       depends on AR
-       help
-         This enables archive creation (-c and -r) with busybox ar.
-
-config BUNZIP2
-       bool "bunzip2"
-       default y
-       help
-         bunzip2 is a compression utility using the Burrows-Wheeler block
-         sorting text compression algorithm, and Huffman coding. Compression
-         is generally considerably better than that achieved by more
-         conventional LZ77/LZ78-based compressors, and approaches the
-         performance of the PPM family of statistical compressors.
-
-         Unless you have a specific application which requires bunzip2, you
-         should probably say N here.
-
-config BZIP2
-       bool "bzip2"
-       default y
-       help
-         bzip2 is a compression utility using the Burrows-Wheeler block
-         sorting text compression algorithm, and Huffman coding. Compression
-         is generally considerably better than that achieved by more
-         conventional LZ77/LZ78-based compressors, and approaches the
-         performance of the PPM family of statistical compressors.
-
-         Unless you have a specific application which requires bzip2, you
-         should probably say N here.
-
-config CPIO
-       bool "cpio"
-       default y
-       help
-         cpio is an archival utility program used to create, modify, and
-         extract contents from archives.
-         cpio has 110 bytes of overheads for every stored file.
-
-         This implementation of cpio can extract cpio archives created in the
-         "newc" or "crc" format, it cannot create or modify them.
-
-         Unless you have a specific application which requires cpio, you
-         should probably say N here.
-
-config FEATURE_CPIO_O
-       bool "Support for archive creation"
-       default y
-       depends on CPIO
-       help
-         This implementation of cpio can create cpio archives in the "newc"
-         format only.
-
-config FEATURE_CPIO_P
-       bool "Support for passthrough mode"
-       default y
-       depends on FEATURE_CPIO_O
-       help
-         Passthrough mode. Rarely used.
-
-config DPKG
-       bool "dpkg"
-       default n
-       select FEATURE_SEAMLESS_GZ
-       help
-         dpkg is a medium-level tool to install, build, remove and manage
-         Debian packages.
-
-         This implementation of dpkg has a number of limitations,
-         you should use the official dpkg if possible.
-
-config DPKG_DEB
-       bool "dpkg_deb"
-       default n
-       select FEATURE_SEAMLESS_GZ
-       help
-         dpkg-deb unpacks and provides information about Debian archives.
-
-         This implementation of dpkg-deb cannot pack archives.
-
-         Unless you have a specific application which requires dpkg-deb,
-         say N here.
-
-config FEATURE_DPKG_DEB_EXTRACT_ONLY
-       bool "Extract only (-x)"
-       default n
-       depends on DPKG_DEB
-       help
-         This reduces dpkg-deb to the equivalent of
-         "ar -p <deb> data.tar.gz | tar -zx". However it saves space as none
-         of the extra dpkg-deb, ar or tar options are needed, they are linked
-         to internally.
-
-config GUNZIP
-       bool "gunzip"
-       default y
-       help
-         gunzip is used to decompress archives created by gzip.
-         You can use the `-t' option to test the integrity of
-         an archive, without decompressing it.
-
-config GZIP
-       bool "gzip"
-       default y
-       help
-         gzip is used to compress files.
-         It's probably the most widely used UNIX compression program.
-
-config FEATURE_GZIP_LONG_OPTIONS
-       bool "Enable long options"
-       default y
-       depends on GZIP && LONG_OPTS
-       help
-         Enable use of long options, increases size by about 106 Bytes
-
-config LZOP
-       bool "lzop"
-       default y
-       help
-         Lzop compression/decompresion.
-
-config LZOP_COMPR_HIGH
-       bool "lzop complession levels 7,8,9 (not very useful)"
-       default n
-       depends on LZOP
-       help
-         High levels (7,8,9) of lzop compression. These levels
-         are actually slower than gzip at equivalent compression ratios
-         and take up 3.2K of code.
-
-config RPM2CPIO
-       bool "rpm2cpio"
-       default y
-       help
-         Converts an RPM file into a CPIO archive.
-
-config RPM
-       bool "rpm"
-       default y
-       help
-         Mini RPM applet - queries and extracts RPM packages.
-
-config TAR
-       bool "tar"
-       default y
-       help
-         tar is an archiving program. It's commonly used with gzip to
-         create compressed archives. It's probably the most widely used
-         UNIX archive program.
-
-config FEATURE_TAR_CREATE
-       bool "Enable archive creation"
-       default y
-       depends on TAR
-       help
-         If you enable this option you'll be able to create
-         tar archives using the `-c' option.
-
-config FEATURE_TAR_AUTODETECT
-       bool "Autodetect compressed tarballs"
-       default y
-       depends on TAR && (FEATURE_SEAMLESS_Z || FEATURE_SEAMLESS_GZ || FEATURE_SEAMLESS_BZ2 || FEATURE_SEAMLESS_LZMA || FEATURE_SEAMLESS_XZ)
-       help
-         With this option tar can automatically detect compressed
-         tarballs. Currently it works only on files (not pipes etc).
-
-config FEATURE_TAR_FROM
-       bool "Enable -X (exclude from) and -T (include from) options)"
-       default y
-       depends on TAR
-       help
-         If you enable this option you'll be able to specify
-         a list of files to include or exclude from an archive.
-
-config FEATURE_TAR_OLDGNU_COMPATIBILITY
-       bool "Support for old tar header format"
-       default y
-       depends on TAR || DPKG
-       help
-         This option is required to unpack archives created in
-         the old GNU format; help to kill this old format by
-         repacking your ancient archives with the new format.
-
-config FEATURE_TAR_OLDSUN_COMPATIBILITY
-       bool "Enable untarring of tarballs with checksums produced by buggy Sun tar"
-       default y
-       depends on TAR || DPKG
-       help
-         This option is required to unpack archives created by some old
-         version of Sun's tar (it was calculating checksum using signed
-         arithmetic). It is said to be fixed in newer Sun tar, but "old"
-         tarballs still exist.
-
-config FEATURE_TAR_GNU_EXTENSIONS
-       bool "Support for GNU tar extensions (long filenames)"
-       default y
-       depends on TAR || DPKG
-       help
-         With this option busybox supports GNU long filenames and
-         linknames.
-
-config FEATURE_TAR_LONG_OPTIONS
-       bool "Enable long options"
-       default y
-       depends on TAR && LONG_OPTS
-       help
-         Enable use of long options, increases size by about 400 Bytes
-
-config FEATURE_TAR_TO_COMMAND
-       bool "Support for writing to an external program"
-       default y
-       depends on TAR && FEATURE_TAR_LONG_OPTIONS
-       help
-         If you enable this option you'll be able to instruct tar to send
-         the contents of each extracted file to the standard input of an
-         external program.
-
-config FEATURE_TAR_UNAME_GNAME
-       bool "Enable use of user and group names"
-       default y
-       depends on TAR
-       help
-         Enables use of user and group names in tar. This affects contents
-         listings (-t) and preserving permissions when unpacking (-p).
-         +200 bytes.
-
-config FEATURE_TAR_NOPRESERVE_TIME
-       bool "Enable -m (do not preserve time) option"
-       default y
-       depends on TAR
-       help
-         With this option busybox supports GNU tar -m
-         (do not preserve time) option.
-
-config FEATURE_TAR_SELINUX
-       bool "Support for extracting SELinux labels"
+       bool "tar, rpm, modprobe etc understand .Z data"
        default n
-       depends on TAR && SELINUX
        help
-         With this option busybox supports restoring SELinux labels
-         when extracting files from tar archives.
-
-config UNCOMPRESS
-       bool "uncompress"
-       default n
-       help
-         uncompress is used to decompress archives created by compress.
-         Not much used anymore, replaced by gzip/gunzip.
-
-config UNLZMA
-       bool "unlzma"
-       default y
-       help
-         unlzma is a compression utility using the Lempel-Ziv-Markov chain
-         compression algorithm, and range coding. Compression
-         is generally considerably better than that achieved by the bzip2
-         compressors.
-
-         The BusyBox unlzma applet is limited to de-compression only.
-         On an x86 system, this applet adds about 4K.
-
-         Unless you have a specific application which requires unlzma, you
-         should probably say N here.
+         Make tar, rpm, modprobe etc understand .Z data.
 
-config FEATURE_LZMA_FAST
-       bool "Optimize unlzma for speed"
-       default y
-       depends on UNLZMA
-       help
-         This option reduces decompression time by about 25% at the cost of
-         a 1K bigger binary.
-
-config LZMA
-       bool "Provide lzma alias which supports only unpacking"
-       default y
-       depends on UNLZMA
-       help
-         Enable this option if you want commands like "lzma -d" to work.
-         IOW: you'll get lzma applet, but it will always require -d option.
-
-config UNXZ
-       bool "unxz"
-       default y
-       help
-         unxz is a unlzma successor.
-
-config XZ
-       bool "Provide xz alias which supports only unpacking"
-       default y
-       depends on UNXZ
-       help
-         Enable this option if you want commands like "xz -d" to work.
-         IOW: you'll get xz applet, but it will always require -d option.
-
-config UNZIP
-       bool "unzip"
-       default y
-       help
-         unzip will list or extract files from a ZIP archive,
-         commonly found on DOS/WIN systems. The default behavior
-         (with no options) is to extract the archive into the
-         current directory. Use the `-d' option to extract to a
-         directory of your choice.
+INSERT
 
 endmenu
index 076e582..a6fd2ea 100644 (file)
@@ -2,30 +2,10 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
-libs-y                         += libunarchive/
+libs-y                         += libarchive/
 
 lib-y:=
 
 INSERT
-
-lib-$(CONFIG_AR)               += ar.o
-lib-$(CONFIG_CPIO)             += cpio.o
-lib-$(CONFIG_DPKG)             += dpkg.o
-lib-$(CONFIG_DPKG_DEB)         += dpkg_deb.o
-lib-$(CONFIG_RPM2CPIO)         += rpm2cpio.o
-lib-$(CONFIG_RPM)              += rpm.o
-lib-$(CONFIG_TAR)              += tar.o
-lib-$(CONFIG_UNZIP)            += unzip.o
-
-lib-$(CONFIG_LZOP)             += lzop.o lzo1x_1.o lzo1x_1o.o lzo1x_d.o bbunzip.o
-lib-$(CONFIG_LZOP_COMPR_HIGH)  += lzo1x_9x.o
-lib-$(CONFIG_GZIP)             += gzip.o bbunzip.o
-lib-$(CONFIG_BZIP2)            += bzip2.o bbunzip.o
-
-lib-$(CONFIG_UNXZ)             += bbunzip.o
-lib-$(CONFIG_UNLZMA)           += bbunzip.o
-lib-$(CONFIG_BUNZIP2)          += bbunzip.o
-lib-$(CONFIG_GUNZIP)           += bbunzip.o
-lib-$(CONFIG_UNCOMPRESS)       += bbunzip.o
index 1b7b66a..f86c52d 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Archive creation support:
  * Copyright (C) 2010 Nokia Corporation. All rights reserved.
  * http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html
  */
 
+//config:config AR
+//config:      bool "ar"
+//config:      default n  # needs to be improved to be able to replace binutils ar
+//config:      help
+//config:        ar is an archival utility program used to create, modify, and
+//config:        extract contents from archives. An archive is a single file holding
+//config:        a collection of other files in a structure that makes it possible to
+//config:        retrieve the original individual files (called archive members).
+//config:        The original files' contents, mode (permissions), timestamp, owner,
+//config:        and group are preserved in the archive, and can be restored on
+//config:        extraction.
+//config:
+//config:        The stored filename is limited to 15 characters. (for more information
+//config:        see long filename support).
+//config:        ar has 60 bytes of overheads for every stored file.
+//config:
+//config:        This implementation of ar can extract archives, it cannot create or
+//config:        modify them.
+//config:        On an x86 system, the ar applet adds about 1K.
+//config:
+//config:        Unless you have a specific application which requires ar, you should
+//config:        probably say N here.
+//config:
+//config:config FEATURE_AR_LONG_FILENAMES
+//config:      bool "Support for long filenames (not needed for debs)"
+//config:      default y
+//config:      depends on AR
+//config:      help
+//config:        By default the ar format can only store the first 15 characters
+//config:        of the filename, this option removes that limitation.
+//config:        It supports the GNU ar long filename method which moves multiple long
+//config:        filenames into a the data section of a new ar entry.
+//config:
+//config:config FEATURE_AR_CREATE
+//config:      bool "Support archive creation"
+//config:      default y
+//config:      depends on AR
+//config:      help
+//config:        This enables archive creation (-c and -r) with busybox ar.
+
+//applet:IF_AR(APPLET(ar, BB_DIR_USR_BIN, BB_SUID_DROP))
+//kbuild:lib-$(CONFIG_AR) += ar.o
+
+//usage:#define ar_trivial_usage
+//usage:       "[-o] [-v] [-p] [-t] [-x] ARCHIVE FILES"
+//usage:#define ar_full_usage "\n\n"
+//usage:       "Extract or list FILES from an ar archive\n"
+//usage:     "\n       -o      Preserve original dates"
+//usage:     "\n       -p      Extract to stdout"
+//usage:     "\n       -t      List"
+//usage:     "\n       -x      Extract"
+//usage:     "\n       -v      Verbose"
+
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 #include "ar.h"
 
 #if ENABLE_FEATURE_AR_CREATE
@@ -71,7 +124,7 @@ static void output_ar_header(archive_handle_t *handle)
 }
 
 /*
- * when replacing files in an existing archive, copy from the the
+ * when replacing files in an existing archive, copy from the
  * original archive those files that are to be left intact
  */
 static void FAST_FUNC copy_data(archive_handle_t *handle)
@@ -123,8 +176,7 @@ static int write_ar_archive(archive_handle_t *handle)
        struct stat st;
        archive_handle_t *out_handle;
 
-       if (fstat(handle->src_fd, &st) == -1)
-               bb_simple_perror_msg_and_die(handle->ar__name);
+       xfstat(handle->src_fd, &st, handle->ar__name);
 
        /* if archive exists, create a new handle for output.
         * we create it in place of the old one.
@@ -180,17 +232,17 @@ static void FAST_FUNC header_verbose_list_ar(const file_header_t *file_header)
        );
 }
 
-#define AR_OPT_VERBOSE         (1 << 0)
-#define AR_OPT_PRESERVE_DATE   (1 << 1)
+#define AR_OPT_VERBOSE          (1 << 0)
+#define AR_OPT_PRESERVE_DATE    (1 << 1)
 /* "ar r" implies create, but warns about it. c suppresses warning.
  * bbox accepts but ignores it: */
-#define AR_OPT_CREATE          (1 << 2)
+#define AR_OPT_CREATE           (1 << 2)
 
-#define AR_CMD_PRINT           (1 << 3)
-#define FIRST_CMD AR_CMD_PRINT
-#define AR_CMD_LIST            (1 << 4)
-#define AR_CMD_EXTRACT         (1 << 5)
-#define AR_CMD_INSERT          (1 << 6)
+#define AR_CMD_PRINT            (1 << 3)
+#define FIRST_CMD               AR_CMD_PRINT
+#define AR_CMD_LIST             (1 << 4)
+#define AR_CMD_EXTRACT          (1 << 5)
+#define AR_CMD_INSERT           (1 << 6)
 
 int ar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int ar_main(int argc UNUSED_PARAM, char **argv)
index c1259ac..b3fb90f 100644 (file)
@@ -1,19 +1,25 @@
 /* vi: set sw=4 ts=4: */
 /*
- *  Common code for gunzip-like applets
+ * Common code for gunzip-like applets
  *
- *  Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
+/* lzop_main() uses bbunpack(), need this: */
+//kbuild:lib-$(CONFIG_LZOP) += bbunzip.o
+
+/* Note: must be kept in sync with archival/lzop.c */
 enum {
        OPT_STDOUT     = 1 << 0,
        OPT_FORCE      = 1 << 1,
        /* only some decompressors: */
        OPT_VERBOSE    = 1 << 2,
-       OPT_DECOMPRESS = 1 << 3,
-       OPT_TEST       = 1 << 4,
+       OPT_QUIET      = 1 << 3,
+       OPT_DECOMPRESS = 1 << 4,
+       OPT_TEST       = 1 << 5,
+       SEAMLESS_MAGIC = (1 << 31) * SEAMLESS_COMPRESSION,
 };
 
 static
@@ -33,16 +39,16 @@ char* FAST_FUNC append_ext(char *filename, const char *expected_ext)
 }
 
 int FAST_FUNC bbunpack(char **argv,
-       IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(unpack_info_t *info),
+       IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(transformer_aux_data_t *aux),
        char* FAST_FUNC (*make_new_name)(char *filename, const char *expected_ext),
        const char *expected_ext
 )
 {
        struct stat stat_buf;
-       IF_DESKTOP(long long) int status;
+       IF_DESKTOP(long long) int status = 0;
        char *filename, *new_name;
        smallint exitcode = 0;
-       unpack_info_t info;
+       transformer_aux_data_t aux;
 
        do {
                /* NB: new_name is *maybe* malloc'ed! */
@@ -54,13 +60,27 @@ int FAST_FUNC bbunpack(char **argv,
 
                /* Open src */
                if (filename) {
-                       if (stat(filename, &stat_buf) != 0) {
-                               bb_simple_perror_msg(filename);
+                       if (!(option_mask32 & SEAMLESS_MAGIC)) {
+                               if (stat(filename, &stat_buf) != 0) {
+ err_name:
+                                       bb_simple_perror_msg(filename);
  err:
-                               exitcode = 1;
-                               goto free_name;
+                                       exitcode = 1;
+                                       goto free_name;
+                               }
+                               if (open_to_or_warn(STDIN_FILENO, filename, O_RDONLY, 0))
+                                       goto err;
+                       } else {
+                               /* "clever zcat" with FILE */
+                               int fd = open_zipped(filename);
+                               if (fd < 0)
+                                       goto err_name;
+                               xmove_fd(fd, STDIN_FILENO);
                        }
-                       if (open_to_or_warn(STDIN_FILENO, filename, O_RDONLY, 0))
+               } else
+               if (option_mask32 & SEAMLESS_MAGIC) {
+                       /* "clever zcat" on stdin */
+                       if (setup_unzip_on_fd(STDIN_FILENO, /*fail_if_not_detected*/ 0))
                                goto err;
                }
 
@@ -68,7 +88,7 @@ int FAST_FUNC bbunpack(char **argv,
                if (option_mask32 & (OPT_STDOUT|OPT_TEST)) {
                        if (option_mask32 & OPT_TEST)
                                if (open_to_or_warn(STDOUT_FILENO, bb_dev_null, O_WRONLY, 0))
-                                       goto err;
+                                       xfunc_die();
                        filename = NULL;
                }
 
@@ -93,26 +113,37 @@ int FAST_FUNC bbunpack(char **argv,
                }
 
                /* Check that the input is sane */
-               if (isatty(STDIN_FILENO) && (option_mask32 & OPT_FORCE) == 0) {
+               if (!(option_mask32 & OPT_FORCE) && isatty(STDIN_FILENO)) {
                        bb_error_msg_and_die("compressed data not read from terminal, "
                                        "use -f to force it");
                }
 
-               /* memset(&info, 0, sizeof(info)); */
-               info.mtime = 0; /* so far it has one member only */
-               status = unpacker(&info);
-               if (status < 0)
-                       exitcode = 1;
-               xclose(STDOUT_FILENO); /* with error check! */
+               if (!(option_mask32 & SEAMLESS_MAGIC)) {
+                       init_transformer_aux_data(&aux);
+                       aux.check_signature = 1;
+                       status = unpacker(&aux);
+                       if (status < 0)
+                               exitcode = 1;
+               } else {
+                       if (bb_copyfd_eof(STDIN_FILENO, STDOUT_FILENO) < 0)
+                               /* Disk full, tty closed, etc. No point in continuing */
+                               xfunc_die();
+               }
+
+               if (!(option_mask32 & OPT_STDOUT))
+                       xclose(STDOUT_FILENO); /* with error check! */
 
                if (filename) {
                        char *del = new_name;
+
                        if (status >= 0) {
+                               unsigned new_name_len;
+
                                /* TODO: restore other things? */
-                               if (info.mtime) {
+                               if (aux.mtime != 0) {
                                        struct timeval times[2];
 
-                                       times[1].tv_sec = times[0].tv_sec = info.mtime;
+                                       times[1].tv_sec = times[0].tv_sec = aux.mtime;
                                        times[1].tv_usec = times[0].tv_usec = 0;
                                        /* Note: we closed it first.
                                         * On some systems calling utimes
@@ -121,28 +152,38 @@ int FAST_FUNC bbunpack(char **argv,
                                        utimes(new_name, times); /* ignoring errors */
                                }
 
-                               /* Delete _compressed_ file */
+                               if (ENABLE_DESKTOP)
+                                       new_name_len = strlen(new_name);
+                               /* Restore source filename (unless tgz -> tar case) */
+                               if (new_name == filename) {
+                                       new_name_len = strlen(filename);
+                                       filename[new_name_len] = '.';
+                               }
+                               /* Extreme bloat for gunzip compat */
+                               /* Some users do want this info... */
+                               if (ENABLE_DESKTOP && (option_mask32 & OPT_VERBOSE)) {
+                                       unsigned percent = status
+                                               ? ((uoff_t)stat_buf.st_size * 100u / (unsigned long long)status)
+                                               : 0;
+                                       fprintf(stderr, "%s: %u%% - replaced with %.*s\n",
+                                               filename,
+                                               100u - percent,
+                                               new_name_len, new_name
+                                       );
+                               }
+                               /* Delete _source_ file */
                                del = filename;
-                               /* restore extension (unless tgz -> tar case) */
-                               if (new_name == filename)
-                                       filename[strlen(filename)] = '.';
                        }
                        xunlink(del);
-
-#if 0 /* Currently buggy - wrong name: "a.gz: 261% - replaced with a.gz" */
-                       /* Extreme bloat for gunzip compat */
-                       if (ENABLE_DESKTOP && (option_mask32 & OPT_VERBOSE) && status >= 0) {
-                               fprintf(stderr, "%s: %u%% - replaced with %s\n",
-                                       filename, (unsigned)(stat_buf.st_size*100 / (status+1)), new_name);
-                       }
-#endif
-
  free_name:
                        if (new_name != filename)
                                free(new_name);
                }
        } while (*argv && *++argv);
 
+       if (option_mask32 & OPT_STDOUT)
+               xclose(STDOUT_FILENO); /* with error check! */
+
        return exitcode;
 }
 
@@ -165,20 +206,29 @@ char* FAST_FUNC make_new_name_generic(char *filename, const char *expected_ext)
 /*
  * Uncompress applet for busybox (c) 2002 Glenn McGrath
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+//usage:#define uncompress_trivial_usage
+//usage:       "[-cf] [FILE]..."
+//usage:#define uncompress_full_usage "\n\n"
+//usage:       "Decompress .Z file[s]\n"
+//usage:     "\n       -c      Write to stdout"
+//usage:     "\n       -f      Overwrite"
+
+//config:config UNCOMPRESS
+//config:      bool "uncompress"
+//config:      default n
+//config:      help
+//config:        uncompress is used to decompress archives created by compress.
+//config:        Not much used anymore, replaced by gzip/gunzip.
+
+//applet:IF_UNCOMPRESS(APPLET(uncompress, BB_DIR_BIN, BB_SUID_DROP))
+//kbuild:lib-$(CONFIG_UNCOMPRESS) += bbunzip.o
 #if ENABLE_UNCOMPRESS
 static
-IF_DESKTOP(long long) int FAST_FUNC unpack_uncompress(unpack_info_t *info UNUSED_PARAM)
+IF_DESKTOP(long long) int FAST_FUNC unpack_uncompress(transformer_aux_data_t *aux)
 {
-       IF_DESKTOP(long long) int status = -1;
-
-       if ((xread_char(STDIN_FILENO) != 0x1f) || (xread_char(STDIN_FILENO) != 0x9d)) {
-               bb_error_msg("invalid magic");
-       } else {
-               status = unpack_Z_stream(STDIN_FILENO, STDOUT_FILENO);
-       }
-       return status;
+       return unpack_Z_stream(aux, STDIN_FILENO, STDOUT_FILENO);
 }
 int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int uncompress_main(int argc UNUSED_PARAM, char **argv)
@@ -206,7 +256,7 @@ int uncompress_main(int argc UNUSED_PARAM, char **argv)
  * General cleanup to better adhere to the style guide and make use of standard
  * busybox functions by Glenn McGrath
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
  * Copyright (C) 1992-1993 Jean-loup Gailly
@@ -214,10 +264,39 @@ int uncompress_main(int argc UNUSED_PARAM, char **argv)
  * Portions of the lzw code are derived from the public domain 'compress'
  * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
  * Ken Turkowski, Dave Mack and Peter Jannesen.
- *
- * See the license_msg below and the file COPYING for the software license.
- * See the file algorithm.doc for the compression algorithms and file formats.
  */
+//usage:#define gunzip_trivial_usage
+//usage:       "[-cft] [FILE]..."
+//usage:#define gunzip_full_usage "\n\n"
+//usage:       "Decompress FILEs (or stdin)\n"
+//usage:     "\n       -c      Write to stdout"
+//usage:     "\n       -f      Force"
+//usage:     "\n       -t      Test file integrity"
+//usage:
+//usage:#define gunzip_example_usage
+//usage:       "$ ls -la /tmp/BusyBox*\n"
+//usage:       "-rw-rw-r--    1 andersen andersen   557009 Apr 11 10:55 /tmp/BusyBox-0.43.tar.gz\n"
+//usage:       "$ gunzip /tmp/BusyBox-0.43.tar.gz\n"
+//usage:       "$ ls -la /tmp/BusyBox*\n"
+//usage:       "-rw-rw-r--    1 andersen andersen  1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar\n"
+//usage:
+//usage:#define zcat_trivial_usage
+//usage:       "[FILE]..."
+//usage:#define zcat_full_usage "\n\n"
+//usage:       "Decompress to stdout"
+
+//config:config GUNZIP
+//config:      bool "gunzip"
+//config:      default y
+//config:      help
+//config:        gunzip is used to decompress archives created by gzip.
+//config:        You can use the `-t' option to test the integrity of
+//config:        an archive, without decompressing it.
+
+//applet:IF_GUNZIP(APPLET(gunzip, BB_DIR_BIN, BB_SUID_DROP))
+//applet:IF_GUNZIP(APPLET_ODDNAME(zcat, gunzip, BB_DIR_BIN, BB_SUID_DROP, zcat))
+//kbuild:lib-$(CONFIG_GZIP) += bbunzip.o
+//kbuild:lib-$(CONFIG_GUNZIP) += bbunzip.o
 #if ENABLE_GUNZIP
 static
 char* FAST_FUNC make_new_name_gunzip(char *filename, const char *expected_ext UNUSED_PARAM)
@@ -245,31 +324,9 @@ char* FAST_FUNC make_new_name_gunzip(char *filename, const char *expected_ext UN
        return filename;
 }
 static
-IF_DESKTOP(long long) int FAST_FUNC unpack_gunzip(unpack_info_t *info)
+IF_DESKTOP(long long) int FAST_FUNC unpack_gunzip(transformer_aux_data_t *aux)
 {
-       IF_DESKTOP(long long) int status = -1;
-
-       /* do the decompression, and cleanup */
-       if (xread_char(STDIN_FILENO) == 0x1f) {
-               unsigned char magic2;
-
-               magic2 = xread_char(STDIN_FILENO);
-               if (ENABLE_FEATURE_SEAMLESS_Z && magic2 == 0x9d) {
-                       status = unpack_Z_stream(STDIN_FILENO, STDOUT_FILENO);
-               } else if (magic2 == 0x8b) {
-                       status = unpack_gz_stream_with_info(STDIN_FILENO, STDOUT_FILENO, info);
-               } else {
-                       goto bad_magic;
-               }
-               if (status < 0) {
-                       bb_error_msg("error inflating");
-               }
-       } else {
- bad_magic:
-               bb_error_msg("invalid magic");
-               /* status is still == -1 */
-       }
-       return status;
+       return unpack_gz_stream(aux, STDIN_FILENO, STDOUT_FILENO);
 }
 /*
  * Linux kernel build uses gzip -d -n. We accept and ignore it.
@@ -287,11 +344,15 @@ IF_DESKTOP(long long) int FAST_FUNC unpack_gunzip(unpack_info_t *info)
 int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int gunzip_main(int argc UNUSED_PARAM, char **argv)
 {
-       getopt32(argv, "cfvdtn");
+       getopt32(argv, "cfvqdtn");
        argv += optind;
-       /* if called as zcat */
+
+       /* If called as zcat...
+        * Normally, "zcat" is just "gunzip -c".
+        * But if seamless magic is enabled, then we are much more clever.
+        */
        if (applet_name[1] == 'c')
-               option_mask32 |= OPT_STDOUT;
+               option_mask32 |= OPT_STDOUT | SEAMLESS_MAGIC;
 
        return bbunpack(argv, unpack_gunzip, make_new_name_gunzip, /*unused:*/ NULL);
 }
@@ -302,31 +363,46 @@ int gunzip_main(int argc UNUSED_PARAM, char **argv)
  * Modified for busybox by Glenn McGrath
  * Added support output to stdout by Thomas Lundquist <thomasez@zelow.no>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 //usage:#define bunzip2_trivial_usage
-//usage:       "[OPTIONS] [FILE]..."
+//usage:       "[-cf] [FILE]..."
 //usage:#define bunzip2_full_usage "\n\n"
 //usage:       "Decompress FILEs (or stdin)\n"
-//usage:     "\nOptions:"
 //usage:     "\n       -c      Write to stdout"
 //usage:     "\n       -f      Force"
 //usage:#define bzcat_trivial_usage
-//usage:       "FILE"
+//usage:       "[FILE]..."
 //usage:#define bzcat_full_usage "\n\n"
 //usage:       "Decompress to stdout"
-//applet:IF_BUNZIP2(APPLET(bunzip2, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-//applet:IF_BUNZIP2(APPLET_ODDNAME(bzcat, bunzip2, _BB_DIR_USR_BIN, _BB_SUID_DROP, bzcat))
+
+//config:config BUNZIP2
+//config:      bool "bunzip2"
+//config:      default y
+//config:      help
+//config:        bunzip2 is a compression utility using the Burrows-Wheeler block
+//config:        sorting text compression algorithm, and Huffman coding. Compression
+//config:        is generally considerably better than that achieved by more
+//config:        conventional LZ77/LZ78-based compressors, and approaches the
+//config:        performance of the PPM family of statistical compressors.
+//config:
+//config:        Unless you have a specific application which requires bunzip2, you
+//config:        should probably say N here.
+
+//applet:IF_BUNZIP2(APPLET(bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP))
+//applet:IF_BUNZIP2(APPLET_ODDNAME(bzcat, bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP, bzcat))
+//kbuild:lib-$(CONFIG_BZIP2) += bbunzip.o
+//kbuild:lib-$(CONFIG_BUNZIP2) += bbunzip.o
 #if ENABLE_BUNZIP2
 static
-IF_DESKTOP(long long) int FAST_FUNC unpack_bunzip2(unpack_info_t *info UNUSED_PARAM)
+IF_DESKTOP(long long) int FAST_FUNC unpack_bunzip2(transformer_aux_data_t *aux)
 {
-       return unpack_bz2_stream_prime(STDIN_FILENO, STDOUT_FILENO);
+       return unpack_bz2_stream(aux, STDIN_FILENO, STDOUT_FILENO);
 }
 int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int bunzip2_main(int argc UNUSED_PARAM, char **argv)
 {
-       getopt32(argv, "cfvdt");
+       getopt32(argv, "cfvqdt");
        argv += optind;
        if (applet_name[2] == 'c') /* bzcat */
                option_mask32 |= OPT_STDOUT;
@@ -342,18 +418,90 @@ int bunzip2_main(int argc UNUSED_PARAM, char **argv)
  *
  * Based on bunzip.c from busybox
  *
- * Licensed under GPL v2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+//usage:#define unlzma_trivial_usage
+//usage:       "[-cf] [FILE]..."
+//usage:#define unlzma_full_usage "\n\n"
+//usage:       "Decompress FILE (or stdin)\n"
+//usage:     "\n       -c      Write to stdout"
+//usage:     "\n       -f      Force"
+//usage:
+//usage:#define lzma_trivial_usage
+//usage:       "-d [-cf] [FILE]..."
+//usage:#define lzma_full_usage "\n\n"
+//usage:       "Decompress FILE (or stdin)\n"
+//usage:     "\n       -d      Decompress"
+//usage:     "\n       -c      Write to stdout"
+//usage:     "\n       -f      Force"
+//usage:
+//usage:#define lzcat_trivial_usage
+//usage:       "[FILE]..."
+//usage:#define lzcat_full_usage "\n\n"
+//usage:       "Decompress to stdout"
+//usage:
+//usage:#define unxz_trivial_usage
+//usage:       "[-cf] [FILE]..."
+//usage:#define unxz_full_usage "\n\n"
+//usage:       "Decompress FILE (or stdin)\n"
+//usage:     "\n       -c      Write to stdout"
+//usage:     "\n       -f      Force"
+//usage:
+//usage:#define xz_trivial_usage
+//usage:       "-d [-cf] [FILE]..."
+//usage:#define xz_full_usage "\n\n"
+//usage:       "Decompress FILE (or stdin)\n"
+//usage:     "\n       -d      Decompress"
+//usage:     "\n       -c      Write to stdout"
+//usage:     "\n       -f      Force"
+//usage:
+//usage:#define xzcat_trivial_usage
+//usage:       "[FILE]..."
+//usage:#define xzcat_full_usage "\n\n"
+//usage:       "Decompress to stdout"
+
+//config:config UNLZMA
+//config:      bool "unlzma"
+//config:      default y
+//config:      help
+//config:        unlzma is a compression utility using the Lempel-Ziv-Markov chain
+//config:        compression algorithm, and range coding. Compression
+//config:        is generally considerably better than that achieved by the bzip2
+//config:        compressors.
+//config:
+//config:        The BusyBox unlzma applet is limited to decompression only.
+//config:        On an x86 system, this applet adds about 4K.
+//config:
+//config:config FEATURE_LZMA_FAST
+//config:      bool "Optimize unlzma for speed"
+//config:      default n
+//config:      depends on UNLZMA
+//config:      help
+//config:        This option reduces decompression time by about 25% at the cost of
+//config:        a 1K bigger binary.
+//config:
+//config:config LZMA
+//config:      bool "Provide lzma alias which supports only unpacking"
+//config:      default y
+//config:      depends on UNLZMA
+//config:      help
+//config:        Enable this option if you want commands like "lzma -d" to work.
+//config:        IOW: you'll get lzma applet, but it will always require -d option.
+
+//applet:IF_UNLZMA(APPLET(unlzma, BB_DIR_USR_BIN, BB_SUID_DROP))
+//applet:IF_UNLZMA(APPLET_ODDNAME(lzcat, unlzma, BB_DIR_USR_BIN, BB_SUID_DROP, lzcat))
+//applet:IF_LZMA(APPLET_ODDNAME(lzma, unlzma, BB_DIR_USR_BIN, BB_SUID_DROP, lzma))
+//kbuild:lib-$(CONFIG_UNLZMA) += bbunzip.o
 #if ENABLE_UNLZMA
 static
-IF_DESKTOP(long long) int FAST_FUNC unpack_unlzma(unpack_info_t *info UNUSED_PARAM)
+IF_DESKTOP(long long) int FAST_FUNC unpack_unlzma(transformer_aux_data_t *aux)
 {
-       return unpack_lzma_stream(STDIN_FILENO, STDOUT_FILENO);
+       return unpack_lzma_stream(aux, STDIN_FILENO, STDOUT_FILENO);
 }
 int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int unlzma_main(int argc UNUSED_PARAM, char **argv)
 {
-       IF_LZMA(int opts =) getopt32(argv, "cfvdt");
+       IF_LZMA(int opts =) getopt32(argv, "cfvqdt");
 # if ENABLE_LZMA
        /* lzma without -d or -t? */
        if (applet_name[2] == 'm' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
@@ -369,25 +517,34 @@ int unlzma_main(int argc UNUSED_PARAM, char **argv)
 #endif
 
 
+//config:config UNXZ
+//config:      bool "unxz"
+//config:      default y
+//config:      help
+//config:        unxz is a unlzma successor.
+//config:
+//config:config XZ
+//config:      bool "Provide xz alias which supports only unpacking"
+//config:      default y
+//config:      depends on UNXZ
+//config:      help
+//config:        Enable this option if you want commands like "xz -d" to work.
+//config:        IOW: you'll get xz applet, but it will always require -d option.
+
+//applet:IF_UNXZ(APPLET(unxz, BB_DIR_USR_BIN, BB_SUID_DROP))
+//applet:IF_UNXZ(APPLET_ODDNAME(xzcat, unxz, BB_DIR_USR_BIN, BB_SUID_DROP, xzcat))
+//applet:IF_XZ(APPLET_ODDNAME(xz, unxz, BB_DIR_USR_BIN, BB_SUID_DROP, xz))
+//kbuild:lib-$(CONFIG_UNXZ) += bbunzip.o
 #if ENABLE_UNXZ
 static
-IF_DESKTOP(long long) int FAST_FUNC unpack_unxz(unpack_info_t *info UNUSED_PARAM)
+IF_DESKTOP(long long) int FAST_FUNC unpack_unxz(transformer_aux_data_t *aux)
 {
-       struct {
-               uint32_t v1;
-               uint16_t v2;
-       } magic;
-       xread(STDIN_FILENO, &magic, 6);
-       if (magic.v1 != XZ_MAGIC1a || magic.v2 != XZ_MAGIC2a) {
-               bb_error_msg("invalid magic");
-               return -1;
-       }
-       return unpack_xz_stream(STDIN_FILENO, STDOUT_FILENO);
+       return unpack_xz_stream(aux, STDIN_FILENO, STDOUT_FILENO);
 }
 int unxz_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int unxz_main(int argc UNUSED_PARAM, char **argv)
 {
-       IF_XZ(int opts =) getopt32(argv, "cfvdt");
+       IF_XZ(int opts =) getopt32(argv, "cfvqdt");
 # if ENABLE_XZ
        /* xz without -d or -t? */
        if (applet_name[2] == '\0' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
index fdb8b93..f7718b4 100644 (file)
@@ -7,10 +7,35 @@
  * about bzip2 library code.
  */
 
+//config:config BZIP2
+//config:      bool "bzip2"
+//config:      default y
+//config:      help
+//config:        bzip2 is a compression utility using the Burrows-Wheeler block
+//config:        sorting text compression algorithm, and Huffman coding. Compression
+//config:        is generally considerably better than that achieved by more
+//config:        conventional LZ77/LZ78-based compressors, and approaches the
+//config:        performance of the PPM family of statistical compressors.
+//config:
+//config:        Unless you have a specific application which requires bzip2, you
+//config:        should probably say N here.
+
+//applet:IF_BZIP2(APPLET(bzip2, BB_DIR_USR_BIN, BB_SUID_DROP))
+//kbuild:lib-$(CONFIG_BZIP2) += bzip2.o
+
+//usage:#define bzip2_trivial_usage
+//usage:       "[OPTIONS] [FILE]..."
+//usage:#define bzip2_full_usage "\n\n"
+//usage:       "Compress FILEs (or stdin) with bzip2 algorithm\n"
+//usage:     "\n       -1..9   Compression level"
+//usage:     "\n       -d      Decompress"
+//usage:     "\n       -c      Write to stdout"
+//usage:     "\n       -f      Force"
+
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
-#define CONFIG_BZIP2_FEATURE_SPEED 1
+#define CONFIG_BZIP2_FAST 1
 
 /* Speed test:
  * Compiled with gcc 4.2.1, run on Athlon 64 1800 MHz (512K L2 cache).
@@ -18,7 +43,7 @@
  * (time to compress gcc-4.2.1.tar is 126.4% compared to bbox).
  * At SPEED 5 difference is 32.7%.
  *
- * Test run of all CONFIG_BZIP2_FEATURE_SPEED values on a 11Mb text file:
+ * Test run of all CONFIG_BZIP2_FAST values on a 11Mb text file:
  *     Size   Time (3 runs)
  * 0:  10828  4.145 4.146 4.148
  * 1:  11097  3.845 3.860 3.861
 /* Takes ~300 bytes, detects corruption caused by bad RAM etc */
 #define BZ_LIGHT_DEBUG 0
 
-#include "bz/bzlib.h"
+#include "libarchive/bz/bzlib.h"
 
-#include "bz/bzlib_private.h"
+#include "libarchive/bz/bzlib_private.h"
 
-#include "bz/blocksort.c"
-#include "bz/bzlib.c"
-#include "bz/compress.c"
-#include "bz/huffman.c"
+#include "libarchive/bz/blocksort.c"
+#include "libarchive/bz/bzlib.c"
+#include "libarchive/bz/compress.c"
+#include "libarchive/bz/huffman.c"
 
 /* No point in being shy and having very small buffer here.
  * bzip2 internal buffers are much bigger anyway, hundreds of kbytes.
@@ -102,7 +127,7 @@ IF_DESKTOP(long long) int bz_write(bz_stream *strm, void* rbuf, ssize_t rlen, vo
 }
 
 static
-IF_DESKTOP(long long) int FAST_FUNC compressStream(unpack_info_t *info UNUSED_PARAM)
+IF_DESKTOP(long long) int FAST_FUNC compressStream(transformer_aux_data_t *aux UNUSED_PARAM)
 {
        IF_DESKTOP(long long) int total;
        ssize_t count;
@@ -128,10 +153,12 @@ IF_DESKTOP(long long) int FAST_FUNC compressStream(unpack_info_t *info UNUSED_PA
                        break;
        }
 
-#if ENABLE_FEATURE_CLEAN_UP
+       /* Can't be conditional on ENABLE_FEATURE_CLEAN_UP -
+        * we are called repeatedly
+        */
        BZ2_bzCompressEnd(strm);
        free(iobuf);
-#endif
+
        return total;
 }
 
index e0ca7fa..1cce7c8 100644 (file)
@@ -4,15 +4,75 @@
  *
  * Copyright (C) 2001 by Glenn McGrath
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Limitations:
  * Doesn't check CRC's
  * Only supports new ASCII and CRC formats
- *
  */
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
+
+//config:config CPIO
+//config:      bool "cpio"
+//config:      default y
+//config:      help
+//config:        cpio is an archival utility program used to create, modify, and
+//config:        extract contents from archives.
+//config:        cpio has 110 bytes of overheads for every stored file.
+//config:
+//config:        This implementation of cpio can extract cpio archives created in the
+//config:        "newc" or "crc" format, it cannot create or modify them.
+//config:
+//config:        Unless you have a specific application which requires cpio, you
+//config:        should probably say N here.
+//config:
+//config:config FEATURE_CPIO_O
+//config:      bool "Support for archive creation"
+//config:      default y
+//config:      depends on CPIO
+//config:      help
+//config:        This implementation of cpio can create cpio archives in the "newc"
+//config:        format only.
+//config:
+//config:config FEATURE_CPIO_P
+//config:      bool "Support for passthrough mode"
+//config:      default y
+//config:      depends on FEATURE_CPIO_O
+//config:      help
+//config:        Passthrough mode. Rarely used.
+
+//applet:IF_CPIO(APPLET(cpio, BB_DIR_BIN, BB_SUID_DROP))
+//kbuild:lib-$(CONFIG_CPIO) += cpio.o
+
+//usage:#define cpio_trivial_usage
+//usage:       "[-dmvu] [-F FILE]" IF_FEATURE_CPIO_O(" [-H newc]")
+//usage:       " [-ti"IF_FEATURE_CPIO_O("o")"]" IF_FEATURE_CPIO_P(" [-p DIR]")
+//usage:       " [EXTR_FILE]..."
+//usage:#define cpio_full_usage "\n\n"
+//usage:       "Extract or list files from a cpio archive"
+//usage:       IF_FEATURE_CPIO_O(", or"
+//usage:     "\ncreate an archive" IF_FEATURE_CPIO_P(" (-o) or copy files (-p)")
+//usage:               " using file list on stdin"
+//usage:       )
+//usage:     "\n"
+//usage:     "\nMain operation mode:"
+//usage:     "\n       -t      List"
+//usage:     "\n       -i      Extract EXTR_FILEs (or all)"
+//usage:       IF_FEATURE_CPIO_O(
+//usage:     "\n       -o      Create (requires -H newc)"
+//usage:       )
+//usage:       IF_FEATURE_CPIO_P(
+//usage:     "\n       -p DIR  Copy files to DIR"
+//usage:       )
+//usage:     "\n       -d      Make leading directories"
+//usage:     "\n       -m      Preserve mtime"
+//usage:     "\n       -v      Verbose"
+//usage:     "\n       -u      Overwrite"
+//usage:     "\n       -F FILE Input (-t,-i,-p) or output (-o) file"
+//usage:       IF_FEATURE_CPIO_O(
+//usage:     "\n       -H newc Archive format"
+//usage:       )
 
 /* GNU cpio 2.9 --help (abridged):
 
@@ -20,7 +80,7 @@
   -t, --list                 List the archive
   -i, --extract              Extract files from an archive
   -o, --create               Create the archive
-  -p, --pass-through         Copy-pass mode [was ist das?!]
+  -p, --pass-through         Copy-pass mode
 
  Options valid in any mode:
       --block-size=SIZE      I/O block size = SIZE * 512 bytes
       --sparse               Write files with blocks of zeros as sparse files
   -u, --unconditional        Replace all files unconditionally
  */
+
 enum {
-       CPIO_OPT_EXTRACT            = (1 << 0),
-       CPIO_OPT_TEST               = (1 << 1),
-       CPIO_OPT_NUL_TERMINATED     = (1 << 2),
-       CPIO_OPT_UNCONDITIONAL      = (1 << 3),
-       CPIO_OPT_VERBOSE            = (1 << 4),
-       CPIO_OPT_CREATE_LEADING_DIR = (1 << 5),
-       CPIO_OPT_PRESERVE_MTIME     = (1 << 6),
-       CPIO_OPT_DEREF              = (1 << 7),
-       CPIO_OPT_FILE               = (1 << 8),
+       OPT_EXTRACT            = (1 << 0),
+       OPT_TEST               = (1 << 1),
+       OPT_NUL_TERMINATED     = (1 << 2),
+       OPT_UNCONDITIONAL      = (1 << 3),
+       OPT_VERBOSE            = (1 << 4),
+       OPT_CREATE_LEADING_DIR = (1 << 5),
+       OPT_PRESERVE_MTIME     = (1 << 6),
+       OPT_DEREF              = (1 << 7),
+       OPT_FILE               = (1 << 8),
        OPTBIT_FILE = 8,
        IF_FEATURE_CPIO_O(OPTBIT_CREATE     ,)
        IF_FEATURE_CPIO_O(OPTBIT_FORMAT     ,)
        IF_FEATURE_CPIO_P(OPTBIT_PASSTHROUGH,)
        IF_LONG_OPTS(     OPTBIT_QUIET      ,)
        IF_LONG_OPTS(     OPTBIT_2STDOUT    ,)
-       CPIO_OPT_CREATE             = IF_FEATURE_CPIO_O((1 << OPTBIT_CREATE     )) + 0,
-       CPIO_OPT_FORMAT             = IF_FEATURE_CPIO_O((1 << OPTBIT_FORMAT     )) + 0,
-       CPIO_OPT_PASSTHROUGH        = IF_FEATURE_CPIO_P((1 << OPTBIT_PASSTHROUGH)) + 0,
-       CPIO_OPT_QUIET              = IF_LONG_OPTS(     (1 << OPTBIT_QUIET      )) + 0,
-       CPIO_OPT_2STDOUT            = IF_LONG_OPTS(     (1 << OPTBIT_2STDOUT    )) + 0,
+       OPT_CREATE             = IF_FEATURE_CPIO_O((1 << OPTBIT_CREATE     )) + 0,
+       OPT_FORMAT             = IF_FEATURE_CPIO_O((1 << OPTBIT_FORMAT     )) + 0,
+       OPT_PASSTHROUGH        = IF_FEATURE_CPIO_P((1 << OPTBIT_PASSTHROUGH)) + 0,
+       OPT_QUIET              = IF_LONG_OPTS(     (1 << OPTBIT_QUIET      )) + 0,
+       OPT_2STDOUT            = IF_LONG_OPTS(     (1 << OPTBIT_2STDOUT    )) + 0,
 };
 
 #define OPTION_STR "it0uvdmLF:"
@@ -138,7 +199,7 @@ static NOINLINE int cpio_o(void)
                char *line;
                struct stat st;
 
-               line = (option_mask32 & CPIO_OPT_NUL_TERMINATED)
+               line = (option_mask32 & OPT_NUL_TERMINATED)
                                ? bb_get_chunk_from_file(stdin, NULL)
                                : xmalloc_fgetline(stdin);
 
@@ -153,7 +214,7 @@ static NOINLINE int cpio_o(void)
                                free(line);
                                continue;
                        }
-                       if ((option_mask32 & CPIO_OPT_DEREF)
+                       if ((option_mask32 & OPT_DEREF)
                                        ? stat(name, &st)
                                        : lstat(name, &st)
                        ) {
@@ -223,24 +284,24 @@ static NOINLINE int cpio_o(void)
                }
 
                bytes += printf("070701"
-                               "%08X%08X%08X%08X%08X%08X%08X"
-                               "%08X%08X%08X%08X" /* GNU cpio uses uppercase hex */
+                               "%08X%08X%08X%08X%08X%08X%08X"
+                               "%08X%08X%08X%08X" /* GNU cpio uses uppercase hex */
                                /* strlen+1: */ "%08X"
                                /* chksum: */   "00000000" /* (only for "070702" files) */
                                /* name,NUL: */ "%s%c",
-                               (unsigned)(uint32_t) st.st_ino,
-                               (unsigned)(uint32_t) st.st_mode,
-                               (unsigned)(uint32_t) st.st_uid,
-                               (unsigned)(uint32_t) st.st_gid,
-                               (unsigned)(uint32_t) st.st_nlink,
-                               (unsigned)(uint32_t) st.st_mtime,
-                               (unsigned)(uint32_t) st.st_size,
-                               (unsigned)(uint32_t) major(st.st_dev),
-                               (unsigned)(uint32_t) minor(st.st_dev),
-                               (unsigned)(uint32_t) major(st.st_rdev),
-                               (unsigned)(uint32_t) minor(st.st_rdev),
-                               (unsigned)(strlen(name) + 1),
-                               name, '\0');
+                               (unsigned)(uint32_t) st.st_ino,
+                               (unsigned)(uint32_t) st.st_mode,
+                               (unsigned)(uint32_t) st.st_uid,
+                               (unsigned)(uint32_t) st.st_gid,
+                               (unsigned)(uint32_t) st.st_nlink,
+                               (unsigned)(uint32_t) st.st_mtime,
+                               (unsigned)(uint32_t) st.st_size,
+                               (unsigned)(uint32_t) major(st.st_dev),
+                               (unsigned)(uint32_t) minor(st.st_dev),
+                               (unsigned)(uint32_t) major(st.st_rdev),
+                               (unsigned)(uint32_t) minor(st.st_rdev),
+                               (unsigned)(strlen(name) + 1),
+                               name, '\0');
                bytes = cpio_pad4(bytes);
 
                if (st.st_size) {
@@ -308,28 +369,24 @@ int cpio_main(int argc UNUSED_PARAM, char **argv)
        /* -L makes sense only with -o or -p */
 
 #if !ENABLE_FEATURE_CPIO_O
-       /* no parameters */
-       opt_complementary = "=0";
        opt = getopt32(argv, OPTION_STR, &cpio_filename);
        argv += optind;
-       if (opt & CPIO_OPT_FILE) { /* -F */
+       if (opt & OPT_FILE) { /* -F */
                xmove_fd(xopen(cpio_filename, O_RDONLY), STDIN_FILENO);
        }
 #else
-       /* _exactly_ one parameter for -p, thus <= 1 param if -p is allowed */
-       opt_complementary = ENABLE_FEATURE_CPIO_P ? "?1" : "=0";
        opt = getopt32(argv, OPTION_STR "oH:" IF_FEATURE_CPIO_P("p"), &cpio_filename, &cpio_fmt);
        argv += optind;
-       if ((opt & (CPIO_OPT_FILE|CPIO_OPT_CREATE)) == CPIO_OPT_FILE) { /* -F without -o */
+       if ((opt & (OPT_FILE|OPT_CREATE)) == OPT_FILE) { /* -F without -o */
                xmove_fd(xopen(cpio_filename, O_RDONLY), STDIN_FILENO);
        }
-       if (opt & CPIO_OPT_PASSTHROUGH) {
+       if (opt & OPT_PASSTHROUGH) {
                pid_t pid;
                struct fd_pair pp;
 
                if (argv[0] == NULL)
                        bb_show_usage();
-               if (opt & CPIO_OPT_CREATE_LEADING_DIR)
+               if (opt & OPT_CREATE_LEADING_DIR)
                        mkdir(argv[0], 0777);
                /* Crude existence check:
                 * close(xopen(argv[0], O_RDONLY | O_DIRECTORY));
@@ -358,19 +415,20 @@ int cpio_main(int argc UNUSED_PARAM, char **argv)
                        goto dump;
                }
                /* parent */
+               USE_FOR_NOMMU(argv[-optind][0] &= 0x7f); /* undo fork_or_rexec() damage */
                xchdir(*argv++);
                close(pp.wr);
                xmove_fd(pp.rd, STDIN_FILENO);
-               //opt &= ~CPIO_OPT_PASSTHROUGH;
-               opt |= CPIO_OPT_EXTRACT;
+               //opt &= ~OPT_PASSTHROUGH;
+               opt |= OPT_EXTRACT;
                goto skip;
        }
        /* -o */
-       if (opt & CPIO_OPT_CREATE) {
+       if (opt & OPT_CREATE) {
                if (cpio_fmt[0] != 'n') /* we _require_ "-H newc" */
                        bb_show_usage();
-               if (opt & CPIO_OPT_FILE) {
-                       xmove_fd(xopen3(cpio_filename, O_WRONLY | O_CREAT | O_TRUNC, 0666), STDOUT_FILENO);
+               if (opt & OPT_FILE) {
+                       xmove_fd(xopen(cpio_filename, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO);
                }
  dump:
                return cpio_o();
@@ -379,35 +437,35 @@ int cpio_main(int argc UNUSED_PARAM, char **argv)
 #endif
 
        /* One of either extract or test options must be given */
-       if ((opt & (CPIO_OPT_TEST | CPIO_OPT_EXTRACT)) == 0) {
+       if ((opt & (OPT_TEST | OPT_EXTRACT)) == 0) {
                bb_show_usage();
        }
 
-       if (opt & CPIO_OPT_TEST) {
+       if (opt & OPT_TEST) {
                /* if both extract and test options are given, ignore extract option */
-               opt &= ~CPIO_OPT_EXTRACT;
+               opt &= ~OPT_EXTRACT;
                archive_handle->action_header = header_list;
        }
-       if (opt & CPIO_OPT_EXTRACT) {
+       if (opt & OPT_EXTRACT) {
                archive_handle->action_data = data_extract_all;
-               if (opt & CPIO_OPT_2STDOUT)
+               if (opt & OPT_2STDOUT)
                        archive_handle->action_data = data_extract_to_stdout;
        }
-       if (opt & CPIO_OPT_UNCONDITIONAL) {
+       if (opt & OPT_UNCONDITIONAL) {
                archive_handle->ah_flags |= ARCHIVE_UNLINK_OLD;
                archive_handle->ah_flags &= ~ARCHIVE_EXTRACT_NEWER;
        }
-       if (opt & CPIO_OPT_VERBOSE) {
+       if (opt & OPT_VERBOSE) {
                if (archive_handle->action_header == header_list) {
                        archive_handle->action_header = header_verbose_list;
                } else {
                        archive_handle->action_header = header_list;
                }
        }
-       if (opt & CPIO_OPT_CREATE_LEADING_DIR) {
+       if (opt & OPT_CREATE_LEADING_DIR) {
                archive_handle->ah_flags |= ARCHIVE_CREATE_LEADING_DIRS;
        }
-       if (opt & CPIO_OPT_PRESERVE_MTIME) {
+       if (opt & OPT_PRESERVE_MTIME) {
                archive_handle->ah_flags |= ARCHIVE_RESTORE_DATE;
        }
 
@@ -423,7 +481,7 @@ int cpio_main(int argc UNUSED_PARAM, char **argv)
                continue;
 
        if (archive_handle->cpio__blocks != (off_t)-1
-        && !(opt & CPIO_OPT_QUIET)
+        && !(opt & OPT_QUIET)
        ) {
                fprintf(stderr, "%"OFF_FMT"u blocks\n", archive_handle->cpio__blocks);
        }
index 219512b..2893cfc 100644 (file)
@@ -12,9 +12,8 @@
  *
  *  started life as a busybox implementation of udpkg
  *
- * licensed under gplv2 or later, see file license in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
-
 /*
  * known difference between busybox dpkg and the official dpkg that i don't
  * consider important, its worth keeping a note of differences anyway, just to
  *
  * bugs that need to be fixed
  *  - (unknown, please let me know when you find any)
- *
  */
 
+//config:config DPKG
+//config:      bool "dpkg"
+//config:      default n
+//config:      select FEATURE_SEAMLESS_GZ
+//config:      help
+//config:        dpkg is a medium-level tool to install, build, remove and manage
+//config:        Debian packages.
+//config:
+//config:        This implementation of dpkg has a number of limitations,
+//config:        you should use the official dpkg if possible.
+
+//applet:IF_DPKG(APPLET(dpkg, BB_DIR_USR_BIN, BB_SUID_DROP))
+//kbuild:lib-$(CONFIG_DPKG) += dpkg.o
+
+//usage:#define dpkg_trivial_usage
+//usage:       "[-ilCPru] [-F OPT] PACKAGE"
+//usage:#define dpkg_full_usage "\n\n"
+//usage:       "Install, remove and manage Debian packages\n"
+//usage:       IF_LONG_OPTS(
+//usage:     "\n       -i,--install    Install the package"
+//usage:     "\n       -l,--list       List of installed packages"
+//usage:     "\n       --configure     Configure an unpackaged package"
+//usage:     "\n       -P,--purge      Purge all files of a package"
+//usage:     "\n       -r,--remove     Remove all but the configuration files for a package"
+//usage:     "\n       --unpack        Unpack a package, but don't configure it"
+//usage:     "\n       --force-depends Ignore dependency problems"
+//usage:     "\n       --force-confnew Overwrite existing config files when installing"
+//usage:     "\n       --force-confold Keep old config files when installing"
+//usage:       )
+//usage:       IF_NOT_LONG_OPTS(
+//usage:     "\n       -i              Install the package"
+//usage:     "\n       -l              List of installed packages"
+//usage:     "\n       -C              Configure an unpackaged package"
+//usage:     "\n       -P              Purge all files of a package"
+//usage:     "\n       -r              Remove all but the configuration files for a package"
+//usage:     "\n       -u              Unpack a package, but don't configure it"
+//usage:     "\n       -F depends      Ignore dependency problems"
+//usage:     "\n       -F confnew      Overwrite existing config files when installing"
+//usage:     "\n       -F confold      Keep old config files when installing"
+//usage:       )
+
 #include "libbb.h"
 #include <fnmatch.h>
-#include "unarchive.h"
+#include "bb_archive.h"
 
 /* note: if you vary hash_prime sizes be aware,
  * 1) tweaking these will have a big effect on how much memory this program uses.
@@ -674,28 +713,21 @@ static unsigned get_status(const unsigned status_node, const int num)
 
 static void set_status(const unsigned status_node_num, const char *new_value, const int position)
 {
-       const unsigned new_value_len = strlen(new_value);
        const unsigned new_value_num = search_name_hashtable(new_value);
        unsigned want = get_status(status_node_num, 1);
        unsigned flag = get_status(status_node_num, 2);
        unsigned status = get_status(status_node_num, 3);
-       int want_len = strlen(name_hashtable[want]);
-       int flag_len = strlen(name_hashtable[flag]);
-       int status_len = strlen(name_hashtable[status]);
        char *new_status;
 
        switch (position) {
                case 1:
                        want = new_value_num;
-                       want_len = new_value_len;
                        break;
                case 2:
                        flag = new_value_num;
-                       flag_len = new_value_len;
                        break;
                case 3:
                        status = new_value_num;
-                       status_len = new_value_len;
                        break;
                default:
                        bb_error_msg_and_die("DEBUG ONLY: this shouldnt happen");
@@ -866,7 +898,7 @@ static void write_status_file(deb_file_t **deb_file)
                                        write_flag = TRUE;
                                        fputs("\n", new_status_file);
                                }
-                               else if (strcmp("config-files", name_hashtable[state_status]) == 0) {
+                               else if (strcmp("config-files", name_hashtable[state_status]) == 0) {
                                        /* only change the status line */
                                        while (1) {
                                                char *field_name;
@@ -939,8 +971,8 @@ static int package_satisfies_dependency(int package, int depend_type)
                return 0;
 
        switch (depend_type) {
-       case EDGE_PRE_DEPENDS:  return get_status(status_num, 3) == search_name_hashtable("installed");
-       case EDGE_DEPENDS:      return get_status(status_num, 1) == search_name_hashtable("install");
+       case EDGE_PRE_DEPENDS: return get_status(status_num, 3) == search_name_hashtable("installed");
+       case EDGE_DEPENDS:     return get_status(status_num, 1) == search_name_hashtable("install");
        }
        return 0;
 }
@@ -967,7 +999,7 @@ static int check_deps(deb_file_t **deb_file, int deb_start /*, int dep_max_count
                conflicts[conflicts_num] = package_num;
                conflicts_num++;
                /* add provides to conflicts list */
-               for (j = 0; j < package_hashtable[package_num]->num_of_edges; j++) {
+               for (j = 0; j < package_hashtable[package_num]->num_of_edges; j++) {
                        if (package_hashtable[package_num]->edge[j]->type == EDGE_PROVIDES) {
                                const int conflicts_package_num = search_package_hashtable(
                                        package_hashtable[package_num]->edge[j]->name,
@@ -1006,8 +1038,8 @@ static int check_deps(deb_file_t **deb_file, int deb_start /*, int dep_max_count
                        if (package_edge->type == EDGE_CONFLICTS) {
                                const unsigned package_num =
                                        search_package_hashtable(package_edge->name,
-                                                                package_edge->version,
-                                                                package_edge->operator);
+                                                               package_edge->version,
+                                                               package_edge->operator);
                                int result = 0;
                                if (package_hashtable[package_num] != NULL) {
                                        status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]);
@@ -1067,12 +1099,13 @@ static int check_deps(deb_file_t **deb_file, int deb_start /*, int dep_max_count
 
                        if (package_edge->type == EDGE_OR_PRE_DEPENDS
                         || package_edge->type == EDGE_OR_DEPENDS
-                       ) {     /* start an EDGE_OR_ list */
+                       ) {
+                               /* start an EDGE_OR_ list */
                                number_of_alternatives = package_edge->version;
                                root_of_alternatives = package_edge;
                                continue;
                        }
-                       if (number_of_alternatives == 0) {      /* not in the middle of an EDGE_OR_ list */
+                       if (number_of_alternatives == 0) {  /* not in the middle of an EDGE_OR_ list */
                                number_of_alternatives = 1;
                                root_of_alternatives = NULL;
                        }
@@ -1093,7 +1126,7 @@ static int check_deps(deb_file_t **deb_file, int deb_start /*, int dep_max_count
                                 */
                                if (root_of_alternatives && package_edge->type != root_of_alternatives->type - 1)
                                        bb_error_msg_and_die("fatal error, package dependencies corrupt: %d != %d - 1",
-                                                            package_edge->type, root_of_alternatives->type);
+                                                       package_edge->type, root_of_alternatives->type);
 
                                if (package_hashtable[package_num] != NULL)
                                        result = !package_satisfies_dependency(package_num, package_edge->type);
@@ -1524,8 +1557,8 @@ static char FAST_FUNC filter_rename_config(archive_handle_t *archive_handle)
                buf = xmalloc(4096);
                md5_begin(&md5);
                while ((count = safe_read(fd, buf, 4096)) > 0)
-                       md5_hash(buf, count, &md5);
-               md5_end(buf, &md5); /* using buf as result storage */
+                       md5_hash(&md5, buf, count);
+               md5_end(&md5, buf); /* using buf as result storage */
                close(fd);
 
                md5line = xmalloc(16 * 2 + 2 + strlen(name_ptr) + 1);
@@ -1644,20 +1677,25 @@ static void unpack_package(deb_file_t *deb_file)
        archive_handle = init_archive_deb_ar(deb_file->filename);
        init_archive_deb_data(archive_handle);
        archive_handle->dpkg__sub_archive->accept = conffile_list;
+       /* Why ARCHIVE_REMEMBER_NAMES?
+        * We want names collected in ->passed list even if conffile_list
+        * is NULL (otherwise get_header_tar may optimize name saving out):
+        */
+       archive_handle->dpkg__sub_archive->ah_flags |= ARCHIVE_REMEMBER_NAMES | ARCHIVE_UNLINK_OLD;
        archive_handle->dpkg__sub_archive->filter = filter_rename_config;
        archive_handle->dpkg__sub_archive->action_data = data_extract_all_prefix;
        archive_handle->dpkg__sub_archive->dpkg__buffer = (char*)"/"; /* huh? */
-       archive_handle->dpkg__sub_archive->ah_flags |= ARCHIVE_UNLINK_OLD;
        unpack_ar_archive(archive_handle);
 
        /* Create the list file */
        list_filename = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, "list");
        out_stream = xfopen_for_write(list_filename);
+       archive_handle->dpkg__sub_archive->passed = llist_rev(archive_handle->dpkg__sub_archive->passed);
        while (archive_handle->dpkg__sub_archive->passed) {
+               char *filename = llist_pop(&archive_handle->dpkg__sub_archive->passed);
                /* the leading . has been stripped by data_extract_all_prefix already */
-               fputs(archive_handle->dpkg__sub_archive->passed->data, out_stream);
-               fputc('\n', out_stream);
-               archive_handle->dpkg__sub_archive->passed = archive_handle->dpkg__sub_archive->passed->link;
+               fprintf(out_stream, "%s\n", filename);
+               free(filename);
        }
        fclose(out_stream);
 
index 45a791b..13f9db9 100644 (file)
@@ -2,16 +2,55 @@
 /*
  * dpkg-deb packs, unpacks and provides information about Debian archives.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//config:config DPKG_DEB
+//config:      bool "dpkg_deb"
+//config:      default n
+//config:      select FEATURE_SEAMLESS_GZ
+//config:      help
+//config:        dpkg-deb unpacks and provides information about Debian archives.
+//config:
+//config:        This implementation of dpkg-deb cannot pack archives.
+//config:
+//config:        Unless you have a specific application which requires dpkg-deb,
+//config:        say N here.
+//config:
+//config:config FEATURE_DPKG_DEB_EXTRACT_ONLY
+//config:      bool "Extract only (-x)"
+//config:      default n
+//config:      depends on DPKG_DEB
+//config:      help
+//config:        This reduces dpkg-deb to the equivalent of
+//config:        "ar -p <deb> data.tar.gz | tar -zx". However it saves space as none
+//config:        of the extra dpkg-deb, ar or tar options are needed, they are linked
+//config:        to internally.
+
+//applet:IF_DPKG_DEB(APPLET_ODDNAME(dpkg-deb, dpkg_deb, BB_DIR_USR_BIN, BB_SUID_DROP, dpkg_deb))
+//kbuild:lib-$(CONFIG_DPKG_DEB) += dpkg_deb.o
+
+//usage:#define dpkg_deb_trivial_usage
+//usage:       "[-cefxX] FILE [argument]"
+//usage:#define dpkg_deb_full_usage "\n\n"
+//usage:       "Perform actions on Debian packages (.debs)\n"
+//usage:     "\n       -c      List contents of filesystem tree"
+//usage:     "\n       -e      Extract control files to [argument] directory"
+//usage:     "\n       -f      Display control field name starting with [argument]"
+//usage:     "\n       -x      Extract packages filesystem tree to directory"
+//usage:     "\n       -X      Verbose extract"
+//usage:
+//usage:#define dpkg_deb_example_usage
+//usage:       "$ dpkg-deb -X ./busybox_0.48-1_i386.deb /tmp\n"
+
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
-#define DPKG_DEB_OPT_CONTENTS  1
-#define DPKG_DEB_OPT_CONTROL   2
-#define DPKG_DEB_OPT_FIELD     4
-#define DPKG_DEB_OPT_EXTRACT   8
-#define DPKG_DEB_OPT_EXTRACT_VERBOSE   16
+#define DPKG_DEB_OPT_CONTENTS         1
+#define DPKG_DEB_OPT_CONTROL          2
+#define DPKG_DEB_OPT_FIELD            4
+#define DPKG_DEB_OPT_EXTRACT          8
+#define DPKG_DEB_OPT_EXTRACT_VERBOSE 16
 
 int dpkg_deb_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int dpkg_deb_main(int argc, char **argv)
index 620897b..1e779c9 100644 (file)
@@ -13,9 +13,8 @@
  * files as well as stdin/stdout, and to generally behave itself wrt
  * command line handling.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
-
 /* big objects in bss:
  * 00000020 b bl_count
  * 00000074 b base_length
@@ -31,7 +30,6 @@
  * 00000480 b static_ltree
  * 000008f4 b dyn_ltree
  */
-
 /* TODO: full support for -v for DESKTOP
  * "/usr/bin/gzip -v a bogus aa" should say:
 a:       85.1% -- replaced with a.gz
@@ -39,8 +37,52 @@ gzip: bogus: No such file or directory
 aa:      85.1% -- replaced with aa.gz
 */
 
+//config:config GZIP
+//config:      bool "gzip"
+//config:      default y
+//config:      help
+//config:        gzip is used to compress files.
+//config:        It's probably the most widely used UNIX compression program.
+//config:
+//config:config FEATURE_GZIP_LONG_OPTIONS
+//config:      bool "Enable long options"
+//config:      default y
+//config:      depends on GZIP && LONG_OPTS
+//config:      help
+//config:        Enable use of long options, increases size by about 106 Bytes
+//config:
+//config:config GZIP_FAST
+//config:      int "Trade memory for gzip speed (0:small,slow - 2:fast,big)"
+//config:      default 0
+//config:      range 0 2
+//config:      depends on GZIP
+//config:      help
+//config:        Enable big memory options for gzip.
+//config:        0: small buffers, small hash-tables
+//config:        1: larger buffers, larger hash-tables
+//config:        2: larger buffers, largest hash-tables
+//config:        Larger models may give slightly better compression
+
+//applet:IF_GZIP(APPLET(gzip, BB_DIR_BIN, BB_SUID_DROP))
+//kbuild:lib-$(CONFIG_GZIP) += gzip.o
+
+//usage:#define gzip_trivial_usage
+//usage:       "[-cfd] [FILE]..."
+//usage:#define gzip_full_usage "\n\n"
+//usage:       "Compress FILEs (or stdin)\n"
+//usage:     "\n       -d      Decompress"
+//usage:     "\n       -c      Write to stdout"
+//usage:     "\n       -f      Force"
+//usage:
+//usage:#define gzip_example_usage
+//usage:       "$ ls -la /tmp/busybox*\n"
+//usage:       "-rw-rw-r--    1 andersen andersen  1761280 Apr 14 17:47 /tmp/busybox.tar\n"
+//usage:       "$ gzip /tmp/busybox.tar\n"
+//usage:       "$ ls -la /tmp/busybox*\n"
+//usage:       "-rw-rw-r--    1 andersen andersen   554058 Apr 14 17:49 /tmp/busybox.tar.gz\n"
+
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 
 /* ===========================================================================
@@ -66,9 +108,17 @@ aa:      85.1% -- replaced with aa.gz
 
 /* ===========================================================================
  */
-#define SMALL_MEM
+#if   CONFIG_GZIP_FAST == 0
+# define SMALL_MEM
+#elif CONFIG_GZIP_FAST == 1
+# define MEDIUM_MEM
+#elif CONFIG_GZIP_FAST == 2
+# define BIG_MEM
+#else
+# error "Invalid CONFIG_GZIP_FAST value"
+#endif
 
-#ifndef        INBUFSIZ
+#ifndef INBUFSIZ
 #  ifdef SMALL_MEM
 #    define INBUFSIZ  0x2000   /* input buffer size */
 #  else
@@ -76,7 +126,7 @@ aa:      85.1% -- replaced with aa.gz
 #  endif
 #endif
 
-#ifndef        OUTBUFSIZ
+#ifndef OUTBUFSIZ
 #  ifdef SMALL_MEM
 #    define OUTBUFSIZ   8192   /* output buffer size */
 #  else
@@ -340,7 +390,7 @@ struct globals {
        ulg bits_sent;                  /* bit length of the compressed data */
 #endif
 
-       uint32_t *crc_32_tab;
+       /*uint32_t *crc_32_tab;*/
        uint32_t crc;   /* shift register contents */
 };
 
@@ -393,15 +443,9 @@ static void put_32bit(ulg n)
  * pointer, then initialize the crc shift register contents instead.
  * Return the current crc in either case.
  */
-static uint32_t updcrc(uch * s, unsigned n)
+static void updcrc(uch * s, unsigned n)
 {
-       uint32_t c = G1.crc;
-       while (n) {
-               c = G1.crc_32_tab[(uch)(c ^ *s++)] ^ (c >> 8);
-               n--;
-       }
-       G1.crc = c;
-       return c;
+       G1.crc = crc32_block_endian0(G1.crc, s, n, global_crc32_table /*G1.crc_32_tab*/);
 }
 
 
@@ -1162,7 +1206,7 @@ static void gen_codes(ct_data * tree, int max_code)
         * must be all ones.
         */
        Assert(code + G2.bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1,
-                  "inconsistent bit counts");
+                       "inconsistent bit counts");
        Tracev((stderr, "\ngen_codes: max_code %d ", max_code));
 
        for (n = 0; n <= max_code; n++) {
@@ -1510,9 +1554,9 @@ static int ct_tally(int dist, int lc)
                }
                out_length >>= 3;
                Trace((stderr,
-                          "\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ",
-                          G2.last_lit, G2.last_dist, in_length, out_length,
-                          100L - out_length * 100L / in_length));
+                               "\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ",
+                               G2.last_lit, G2.last_dist, in_length, out_length,
+                               100L - out_length * 100L / in_length));
                if (G2.last_dist < G2.last_lit / 2 && out_length < in_length / 2)
                        return 1;
        }
@@ -1604,9 +1648,9 @@ static ulg flush_block(char *buf, ulg stored_len, int eof)
        static_lenb = (G2.static_len + 3 + 7) >> 3;
 
        Trace((stderr,
-                  "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ",
-                  opt_lenb, G2.opt_len, static_lenb, G2.static_len, stored_len,
-                  G2.last_lit, G2.last_dist));
+                       "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ",
+                       opt_lenb, G2.opt_len, static_lenb, G2.static_len, stored_len,
+                       G2.last_lit, G2.last_dist));
 
        if (static_lenb <= opt_lenb)
                opt_lenb = static_lenb;
@@ -1644,7 +1688,7 @@ static ulg flush_block(char *buf, ulg stored_len, int eof)
        } else {
                send_bits((DYN_TREES << 1) + eof, 3);
                send_all_trees(G2.l_desc.max_code + 1, G2.d_desc.max_code + 1,
-                                          max_blindex + 1);
+                                       max_blindex + 1);
                compress_block((ct_data *) G2.dyn_ltree, (ct_data *) G2.dyn_dtree);
                G2.compressed_len += 3 + G2.opt_len;
        }
@@ -1664,7 +1708,7 @@ static ulg flush_block(char *buf, ulg stored_len, int eof)
 
 /* ===========================================================================
  * Update a hash value with the given input byte
- * IN  assertion: all calls to to UPDATE_HASH are made with consecutive
+ * IN  assertion: all calls to UPDATE_HASH are made with consecutive
  *    input characters, so that a running hash key can be computed from the
  *    previous key instead of complete recalculation each time.
  */
@@ -1695,7 +1739,7 @@ static ulg flush_block(char *buf, ulg stored_len, int eof)
 /* Insert string s in the dictionary and set match_head to the previous head
  * of the hash chain (the most recent string with same hash key). Return
  * the previous length of the hash chain.
- * IN  assertion: all calls to to INSERT_STRING are made with consecutive
+ * IN  assertion: all calls to INSERT_STRING are made with consecutive
  *    input characters and the first MIN_MATCH bytes of s are valid
  *    (except for the last MIN_MATCH-1 bytes of the input file). */
 #define INSERT_STRING(s, match_head) \
@@ -1998,7 +2042,7 @@ static void zip(ulg time_stamp)
 
 /* ======================================================================== */
 static
-IF_DESKTOP(long long) int FAST_FUNC pack_gzip(unpack_info_t *info UNUSED_PARAM)
+IF_DESKTOP(long long) int FAST_FUNC pack_gzip(transformer_aux_data_t *aux UNUSED_PARAM)
 {
        struct stat s;
 
@@ -2104,8 +2148,8 @@ int gzip_main(int argc UNUSED_PARAM, char **argv)
        ALLOC(uch, G1.window, 2L * WSIZE);
        ALLOC(ush, G1.prev, 1L << BITS);
 
-       /* Initialise the CRC32 table */
-       G1.crc_32_tab = crc32_filltable(NULL, 0);
+       /* Initialize the CRC32 table */
+       global_crc32_table = crc32_filltable(NULL, 0);
 
        return bbunpack(argv, pack_gzip, append_ext, "gz");
 }
diff --git a/archival/libarchive/Kbuild.src b/archival/libarchive/Kbuild.src
new file mode 100644 (file)
index 0000000..968fdf8
--- /dev/null
@@ -0,0 +1,83 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2 or later, see file LICENSE in this source tree.
+
+lib-y:=
+
+COMMON_FILES:= \
+\
+       data_skip.o \
+       data_extract_all.o \
+       data_extract_to_stdout.o \
+\
+       filter_accept_all.o \
+       filter_accept_list.o \
+       filter_accept_reject_list.o \
+\
+       header_skip.o \
+       header_list.o \
+       header_verbose_list.o \
+\
+       seek_by_read.o \
+       seek_by_jump.o \
+\
+       data_align.o \
+       find_list_entry.o \
+       init_handle.o
+
+DPKG_FILES:= \
+       unpack_ar_archive.o \
+       filter_accept_list_reassign.o \
+       get_header_ar.o \
+       get_header_tar.o \
+       get_header_tar_gz.o \
+       get_header_tar_bz2.o \
+       get_header_tar_lzma.o \
+
+INSERT
+
+lib-$(CONFIG_DPKG)                      += $(DPKG_FILES)
+lib-$(CONFIG_DPKG_DEB)                  += $(DPKG_FILES)
+
+lib-$(CONFIG_AR)                        += get_header_ar.o unpack_ar_archive.o
+lib-$(CONFIG_CPIO)                      += get_header_cpio.o
+lib-$(CONFIG_TAR)                       += get_header_tar.o
+lib-$(CONFIG_FEATURE_TAR_TO_COMMAND)    += data_extract_to_command.o
+lib-$(CONFIG_LZOP)                      += lzo1x_1.o lzo1x_1o.o lzo1x_d.o
+lib-$(CONFIG_LZOP_COMPR_HIGH)           += lzo1x_9x.o
+lib-$(CONFIG_BUNZIP2)                   += open_transformer.o decompress_bunzip2.o
+lib-$(CONFIG_UNLZMA)                    += open_transformer.o decompress_unlzma.o
+lib-$(CONFIG_UNXZ)                      += open_transformer.o decompress_unxz.o
+lib-$(CONFIG_GUNZIP)                    += open_transformer.o decompress_gunzip.o
+lib-$(CONFIG_UNCOMPRESS)                += open_transformer.o decompress_uncompress.o
+lib-$(CONFIG_UNZIP)                     += open_transformer.o decompress_gunzip.o
+lib-$(CONFIG_RPM2CPIO)                  += open_transformer.o decompress_gunzip.o get_header_cpio.o
+lib-$(CONFIG_RPM)                       += open_transformer.o decompress_gunzip.o get_header_cpio.o
+
+lib-$(CONFIG_GZIP)                      += open_transformer.o
+lib-$(CONFIG_BZIP2)                     += open_transformer.o
+lib-$(CONFIG_LZOP)                      += open_transformer.o
+lib-$(CONFIG_MAN)                       += open_transformer.o
+lib-$(CONFIG_SETFONT)                   += open_transformer.o
+lib-$(CONFIG_FEATURE_2_4_MODULES)       += open_transformer.o
+lib-$(CONFIG_MODINFO)                   += open_transformer.o
+lib-$(CONFIG_INSMOD)                    += open_transformer.o
+lib-$(CONFIG_DEPMOD)                    += open_transformer.o
+lib-$(CONFIG_RMMOD)                     += open_transformer.o
+lib-$(CONFIG_LSMOD)                     += open_transformer.o
+lib-$(CONFIG_MODPROBE)                  += open_transformer.o
+lib-$(CONFIG_MODPROBE_SMALL)            += open_transformer.o
+
+lib-$(CONFIG_FEATURE_SEAMLESS_Z)        += open_transformer.o decompress_uncompress.o
+lib-$(CONFIG_FEATURE_SEAMLESS_GZ)       += open_transformer.o decompress_gunzip.o
+lib-$(CONFIG_FEATURE_SEAMLESS_BZ2)      += open_transformer.o decompress_bunzip2.o
+lib-$(CONFIG_FEATURE_SEAMLESS_LZMA)     += open_transformer.o decompress_unlzma.o
+lib-$(CONFIG_FEATURE_SEAMLESS_XZ)       += open_transformer.o decompress_unxz.o
+lib-$(CONFIG_FEATURE_COMPRESS_USAGE)    += open_transformer.o decompress_bunzip2.o
+lib-$(CONFIG_FEATURE_COMPRESS_BBCONFIG) += open_transformer.o decompress_bunzip2.o
+
+ifneq ($(lib-y),)
+lib-y += $(COMMON_FILES)
+endif
similarity index 99%
rename from archival/bz/blocksort.c
rename to archival/libarchive/bz/blocksort.c
index f70c370..e600cb7 100644 (file)
@@ -385,7 +385,7 @@ int mainGtU(
  * but speeds up compression 10% overall
  */
 
-#if CONFIG_BZIP2_FEATURE_SPEED >= 1
+#if CONFIG_BZIP2_FAST >= 1
 
 #define TIMES_8(code) \
        code; code; code; code; \
@@ -496,7 +496,7 @@ void mainSimpleSort(uint32_t* ptr,
                        i++;
 
 /* 1.5% overall speedup, +290 bytes */
-#if CONFIG_BZIP2_FEATURE_SPEED >= 3
+#if CONFIG_BZIP2_FAST >= 3
                        /*-- copy 2 --*/
                        if (i > hi) break;
                        v = ptr[i];
@@ -750,7 +750,7 @@ void mainSort(EState* state,
        j = block[0] << 8;
        i = nblock - 1;
 /* 3%, +300 bytes */
-#if CONFIG_BZIP2_FEATURE_SPEED >= 2
+#if CONFIG_BZIP2_FAST >= 2
        for (; i >= 3; i -= 4) {
                quadrant[i] = 0;
                j = (j >> 8) | (((uint16_t)block[i]) << 8);
@@ -787,7 +787,7 @@ void mainSort(EState* state,
 
        s = block[0] << 8;
        i = nblock - 1;
-#if CONFIG_BZIP2_FEATURE_SPEED >= 2
+#if CONFIG_BZIP2_FAST >= 2
        for (; i >= 3; i -= 4) {
                s = (s >> 8) | (block[i] << 8);
                j = ftab[s] - 1;
similarity index 98%
rename from archival/bz/bzlib.c
rename to archival/libarchive/bz/bzlib.c
index 8341794..5f7db74 100644 (file)
@@ -28,7 +28,7 @@ in the file LICENSE.
  * 0.9.0a/b -- no changes in this file.
  * 0.9.0c   -- made zero-length BZ_FLUSH work correctly in bzCompress().
  *             fixed bzWrite/bzRead to ignore zero-length requests.
- *            fixed bzread to correctly handle read requests after EOF.
+ *             fixed bzread to correctly handle read requests after EOF.
  *             wrong parameter order in call to bzDecompressInit in
  *             bzBuffToBuffDecompress.  Fixed.
  */
@@ -361,7 +361,6 @@ int BZ2_bzCompress(bz_stream *strm, int action)
 
 
 /*---------------------------------------------------*/
-#if ENABLE_FEATURE_CLEAN_UP
 static
 void BZ2_bzCompressEnd(bz_stream *strm)
 {
@@ -372,9 +371,8 @@ void BZ2_bzCompressEnd(bz_stream *strm)
        free(s->arr2);
        free(s->ftab);
        free(s->crc32table);
-       free(strm->state);
+       free(s);
 }
-#endif
 
 
 /*---------------------------------------------------*/
similarity index 99%
rename from archival/bz/bzlib_private.h
rename to archival/libarchive/bz/bzlib_private.h
index 6430ce4..43e674b 100644 (file)
@@ -183,7 +183,7 @@ typedef struct EState {
        /* stack-saving measures: these can be local, but they are too big */
        int32_t  sendMTFValues__code [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
        int32_t  sendMTFValues__rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
-#if CONFIG_BZIP2_FEATURE_SPEED >= 5
+#if CONFIG_BZIP2_FAST >= 5
        /* second dimension: only 3 needed; 4 makes index calculations faster */
        uint32_t sendMTFValues__len_pack[BZ_MAX_ALPHA_SIZE][4];
 #endif
similarity index 95%
rename from archival/bz/compress.c
rename to archival/libarchive/bz/compress.c
index 640b887..23de9d3 100644 (file)
@@ -61,7 +61,7 @@ void bsFinishWrite(EState* s)
 /*---------------------------------------------------*/
 static
 /* Helps only on level 5, on other levels hurts. ? */
-#if CONFIG_BZIP2_FEATURE_SPEED >= 5
+#if CONFIG_BZIP2_FAST >= 5
 ALWAYS_INLINE
 #endif
 void bsW(EState* s, int32_t n, uint32_t v)
@@ -134,15 +134,14 @@ void generateMTFValues(EState* s)
         * holds the original block data.
         *
         * The first thing to do is generate the MTF values,
-        * and put them in
-        *      ((uint16_t*)s->arr1)[0 .. s->nblock-1].
+        * and put them in ((uint16_t*)s->arr1)[0 .. s->nblock-1].
+        *
         * Because there are strictly fewer or equal MTF values
         * than block values, ptr values in this area are overwritten
         * with MTF values only when they are no longer needed.
         *
         * The final compressed bitstream is generated into the
-        * area starting at
-        *      &((uint8_t*)s->arr2)[s->nblock]
+        * area starting at &((uint8_t*)s->arr2)[s->nblock]
         *
         * These storage aliases are set up in bzCompressInit(),
         * except for the last one, which is arranged in
@@ -212,7 +211,6 @@ void generateMTFValues(EState* s)
                                wr++;
                                s->mtfFreq[j+1]++;
                        }
-
                }
        }
 
@@ -251,9 +249,9 @@ void generateMTFValues(EState* s)
 static NOINLINE
 void sendMTFValues(EState* s)
 {
-       int32_t v, t, i, j, gs, ge, totc, bt, bc, iter;
+       int32_t v, t, i, j, gs, ge, bt, bc, iter;
        int32_t nSelectors, alphaSize, minLen, maxLen, selCtr;
-       int32_t nGroups, nBytes;
+       int32_t nGroups;
 
        /*
         * uint8_t len[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
@@ -333,7 +331,7 @@ void sendMTFValues(EState* s)
                        for (v = 0; v < alphaSize; v++)
                                s->rfreq[t][v] = 0;
 
-#if CONFIG_BZIP2_FEATURE_SPEED >= 5
+#if CONFIG_BZIP2_FAST >= 5
                /*
                 * Set up an auxiliary length table which is used to fast-track
                 * the common case (nGroups == 6).
@@ -347,7 +345,6 @@ void sendMTFValues(EState* s)
                }
 #endif
                nSelectors = 0;
-               totc = 0;
                gs = 0;
                while (1) {
                        /*--- Set group start & end marks. --*/
@@ -363,7 +360,7 @@ void sendMTFValues(EState* s)
                         */
                        for (t = 0; t < nGroups; t++)
                                cost[t] = 0;
-#if CONFIG_BZIP2_FEATURE_SPEED >= 5
+#if CONFIG_BZIP2_FAST >= 5
                        if (nGroups == 6 && 50 == ge-gs+1) {
                                /*--- fast track the common case ---*/
                                register uint32_t cost01, cost23, cost45;
@@ -413,7 +410,6 @@ void sendMTFValues(EState* s)
                                        bt = t;
                                }
                        }
-                       totc += bc;
                        fave[bt]++;
                        s->selector[nSelectors] = bt;
                        nSelectors++;
@@ -422,7 +418,7 @@ void sendMTFValues(EState* s)
                         * Increment the symbol frequencies for the selected table.
                         */
 /* 1% faster compress. +800 bytes */
-#if CONFIG_BZIP2_FEATURE_SPEED >= 4
+#if CONFIG_BZIP2_FAST >= 4
                        if (nGroups == 6 && 50 == ge-gs+1) {
                                /*--- fast track the common case ---*/
 #define BZ_ITUR(nn) s->rfreq[bt][mtfv[gs + (nn)]]++
@@ -460,7 +456,7 @@ void sendMTFValues(EState* s)
        }
 
        AssertH(nGroups < 8, 3002);
-       AssertH(nSelectors < 32768 && nSelectors <= (2 + (900000 / BZ_G_SIZE)), 3003);
+       AssertH(nSelectors < 32768 && nSelectors <= (2 + (900000 / BZ_G_SIZE)), 3003);
 
        /*--- Compute MTF values for the selectors. ---*/
        {
@@ -503,18 +499,17 @@ void sendMTFValues(EState* s)
                for (i = 0; i < 16; i++) {
                        if (sizeof(long) <= 4) {
                                inUse16 = inUse16*2 +
-                                       ((*(uint32_t*)&(s->inUse[i * 16 + 0])
-                                       | *(uint32_t*)&(s->inUse[i * 16 + 4])
-                                       | *(uint32_t*)&(s->inUse[i * 16 + 8])
-                                       | *(uint32_t*)&(s->inUse[i * 16 + 12])) != 0);
+                                       ((*(bb__aliased_uint32_t*)&(s->inUse[i * 16 + 0])
+                                       | *(bb__aliased_uint32_t*)&(s->inUse[i * 16 + 4])
+                                       | *(bb__aliased_uint32_t*)&(s->inUse[i * 16 + 8])
+                                       | *(bb__aliased_uint32_t*)&(s->inUse[i * 16 + 12])) != 0);
                        } else { /* Our CPU can do better */
                                inUse16 = inUse16*2 +
-                                       ((*(uint64_t*)&(s->inUse[i * 16 + 0])
-                                       | *(uint64_t*)&(s->inUse[i * 16 + 8])) != 0);
+                                       ((*(bb__aliased_uint64_t*)&(s->inUse[i * 16 + 0])
+                                       | *(bb__aliased_uint64_t*)&(s->inUse[i * 16 + 8])) != 0);
                        }
                }
 
-               nBytes = s->numZ;
                bsW(s, 16, inUse16);
 
                inUse16 <<= (sizeof(int)*8 - 16); /* move 15th bit into sign bit */
@@ -530,7 +525,6 @@ void sendMTFValues(EState* s)
        }
 
        /*--- Now the selectors. ---*/
-       nBytes = s->numZ;
        bsW(s, 3, nGroups);
        bsW(s, 15, nSelectors);
        for (i = 0; i < nSelectors; i++) {
@@ -540,8 +534,6 @@ void sendMTFValues(EState* s)
        }
 
        /*--- Now the coding tables. ---*/
-       nBytes = s->numZ;
-
        for (t = 0; t < nGroups; t++) {
                int32_t curr = s->len[t][0];
                bsW(s, 5, curr);
@@ -553,7 +545,6 @@ void sendMTFValues(EState* s)
        }
 
        /*--- And finally, the block data proper ---*/
-       nBytes = s->numZ;
        selCtr = 0;
        gs = 0;
        while (1) {
similarity index 99%
rename from archival/bz/huffman.c
rename to archival/libarchive/bz/huffman.c
index 676b1af..bbec11a 100644 (file)
@@ -48,7 +48,7 @@ in the file LICENSE.
 
 
 /* 90 bytes, 0.3% of overall compress speed */
-#if CONFIG_BZIP2_FEATURE_SPEED >= 1
+#if CONFIG_BZIP2_FAST >= 1
 
 /* macro works better than inline (gcc 4.2.1) */
 #define DOWNHEAP1(heap, weight, Heap) \
similarity index 75%
rename from archival/libunarchive/data_align.c
rename to archival/libarchive/data_align.c
index 0c8542b..a6b84a4 100644 (file)
@@ -1,10 +1,10 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 void FAST_FUNC data_align(archive_handle_t *archive_handle, unsigned boundary)
 {
similarity index 86%
rename from archival/libunarchive/data_extract_all.c
rename to archival/libarchive/data_extract_all.c
index 00e67d4..45776dc 100644 (file)
@@ -1,10 +1,10 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
 {
@@ -13,13 +13,13 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
        int res;
 
 #if ENABLE_FEATURE_TAR_SELINUX
-       char *sctx = archive_handle->tar__next_file_sctx;
+       char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE];
        if (!sctx)
-               sctx = archive_handle->tar__global_sctx;
+               sctx = archive_handle->tar__sctx[PAX_GLOBAL];
        if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */
                setfscreatecon(sctx);
-               free(archive_handle->tar__next_file_sctx);
-               archive_handle->tar__next_file_sctx = NULL;
+               free(archive_handle->tar__sctx[PAX_NEXT_FILE]);
+               archive_handle->tar__sctx[PAX_NEXT_FILE] = NULL;
        }
 #endif
 
@@ -69,7 +69,9 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
                        }
                }
                else if (existing_sb.st_mtime >= file_header->mtime) {
-                       if (!(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) {
+                       if (!(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
+                        && !S_ISDIR(file_header->mode)
+                       ) {
                                bb_error_msg("%s not created: newer or "
                                        "same age file exists", file_header->name);
                        }
@@ -104,15 +106,28 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
        switch (file_header->mode & S_IFMT) {
        case S_IFREG: {
                /* Regular file */
+               char *dst_name;
                int flags = O_WRONLY | O_CREAT | O_EXCL;
                if (archive_handle->ah_flags & ARCHIVE_O_TRUNC)
                        flags = O_WRONLY | O_CREAT | O_TRUNC;
-               dst_fd = xopen3(file_header->name,
+               dst_name = file_header->name;
+#ifdef ARCHIVE_REPLACE_VIA_RENAME
+               if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME)
+                       /* rpm-style temp file name */
+                       dst_name = xasprintf("%s;%x", dst_name, (int)getpid());
+#endif
+               dst_fd = xopen3(dst_name,
                        flags,
                        file_header->mode
                        );
                bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size);
                close(dst_fd);
+#ifdef ARCHIVE_REPLACE_VIA_RENAME
+               if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) {
+                       xrename(dst_name, file_header->name);
+                       free(dst_name);
+               }
+#endif
                break;
        }
        case S_IFDIR:
@@ -1,10 +1,10 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 enum {
        //TAR_FILETYPE,
@@ -64,13 +64,13 @@ void FAST_FUNC data_extract_to_command(archive_handle_t *archive_handle)
        file_header_t *file_header = archive_handle->file_header;
 
 #if 0 /* do we need this? ENABLE_FEATURE_TAR_SELINUX */
-       char *sctx = archive_handle->tar__next_file_sctx;
+       char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE];
        if (!sctx)
-               sctx = archive_handle->tar__global_sctx;
+               sctx = archive_handle->tar__sctx[PAX_GLOBAL];
        if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */
                setfscreatecon(sctx);
-               free(archive_handle->tar__next_file_sctx);
-               archive_handle->tar__next_file_sctx = NULL;
+               free(archive_handle->tar__sctx[PAX_NEXT_FILE]);
+               archive_handle->tar__sctx[PAX_NEXT_FILE] = NULL;
        }
 #endif
 
@@ -99,8 +99,12 @@ void FAST_FUNC data_extract_to_command(archive_handle_t *archive_handle)
                        close(p[1]);
                        xdup2(p[0], STDIN_FILENO);
                        signal(SIGPIPE, SIG_DFL);
-                       execl(DEFAULT_SHELL, DEFAULT_SHELL_SHORT_NAME, "-c", archive_handle->tar__to_command, NULL);
-                       bb_perror_msg_and_die("can't execute '%s'", DEFAULT_SHELL);
+                       execl(archive_handle->tar__to_command_shell,
+                               archive_handle->tar__to_command_shell,
+                               "-c",
+                               archive_handle->tar__to_command,
+                               (char *)0);
+                       bb_perror_msg_and_die("can't execute '%s'", archive_handle->tar__to_command_shell);
                }
                close(p[0]);
                /* Our caller is expected to do signal(SIGPIPE, SIG_IGN)
@@ -1,10 +1,10 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 void FAST_FUNC data_extract_to_stdout(archive_handle_t *archive_handle)
 {
similarity index 65%
rename from archival/libunarchive/data_skip.c
rename to archival/libarchive/data_skip.c
index 06d3dce..588167f 100644 (file)
@@ -1,10 +1,10 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 void FAST_FUNC data_skip(archive_handle_t *archive_handle)
 {
similarity index 66%
rename from archival/libunarchive/decompress_bunzip2.c
rename to archival/libarchive/decompress_bunzip2.c
index bdbd39a..6396fe4 100644 (file)
@@ -6,7 +6,7 @@
    Peter Fenwick, Alistair Moffat, Radford Neal, Ian H. Witten,
    Robert Sedgewick, and Jon L. Bentley.
 
-   Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+   Licensed under GPLv2 or later, see file LICENSE in this source tree.
 */
 
 /*
        function, and various other tweaks.  In (limited) tests, approximately
        20% faster than bzcat on x86 and about 10% faster on arm.
 
-       Note that about 2/3 of the time is spent in read_unzip() reversing
+       Note that about 2/3 of the time is spent in read_bunzip() reversing
        the Burrows-Wheeler transformation.  Much of that time is delay
        resulting from cache misses.
 
+       (2010 update by vda: profiled "bzcat <84mbyte.bz2 >/dev/null"
+       on x86-64 CPU with L2 > 1M: get_next_block is hotter than read_bunzip:
+       %time seconds   calls function
+       71.01   12.69     444 get_next_block
+       28.65    5.12   93065 read_bunzip
+       00.22    0.04 7736490 get_bits
+       00.11    0.02      47 dealloc_bunzip
+       00.00    0.00   93018 full_write
+       ...)
+
+
        I would ask that anyone benefiting from this work, especially those
        using it in commercial products, consider making a donation to my local
        non-profit hospice organization (www.hospiceacadiana.com) in the name of
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
+
+#if 0
+# define dbg(...) bb_error_msg(__VA_ARGS__)
+#else
+# define dbg(...) ((void)0)
+#endif
 
 /* Constants for Huffman coding */
 #define MAX_GROUPS          6
 
 /* Status return values */
 #define RETVAL_OK                       0
-#define RETVAL_LAST_BLOCK               (-1)
-#define RETVAL_NOT_BZIP_DATA            (-2)
-#define RETVAL_UNEXPECTED_INPUT_EOF     (-3)
-#define RETVAL_SHORT_WRITE              (-4)
-#define RETVAL_DATA_ERROR               (-5)
-#define RETVAL_OUT_OF_MEMORY            (-6)
-#define RETVAL_OBSOLETE_INPUT           (-7)
+#define RETVAL_LAST_BLOCK               (dbg("%d", __LINE__), -1)
+#define RETVAL_NOT_BZIP_DATA            (dbg("%d", __LINE__), -2)
+#define RETVAL_UNEXPECTED_INPUT_EOF     (dbg("%d", __LINE__), -3)
+#define RETVAL_SHORT_WRITE              (dbg("%d", __LINE__), -4)
+#define RETVAL_DATA_ERROR               (dbg("%d", __LINE__), -5)
+#define RETVAL_OUT_OF_MEMORY            (dbg("%d", __LINE__), -6)
+#define RETVAL_OBSOLETE_INPUT           (dbg("%d", __LINE__), -7)
 
 /* Other housekeeping constants */
 #define IOBUF_SIZE          4096
@@ -70,38 +87,41 @@ struct bunzip_data {
        /* I/O tracking data (file handles, buffers, positions, etc.) */
        unsigned inbufBitCount, inbufBits;
        int in_fd, out_fd, inbufCount, inbufPos /*, outbufPos*/;
-       unsigned char *inbuf /*,*outbuf*/;
+       uint8_t *inbuf /*,*outbuf*/;
 
        /* State for interrupting output loop */
-       int writeCopies, writePos, writeRunCountdown, writeCount, writeCurrent;
+       int writeCopies, writePos, writeRunCountdown, writeCount;
+       int writeCurrent; /* actually a uint8_t */
 
        /* The CRC values stored in the block header and calculated from the data */
        uint32_t headerCRC, totalCRC, writeCRC;
 
        /* Intermediate buffer and its size (in bytes) */
-       unsigned *dbuf, dbufSize;
+       uint32_t *dbuf;
+       unsigned dbufSize;
 
        /* For I/O error handling */
        jmp_buf jmpbuf;
 
        /* Big things go last (register-relative addressing can be larger for big offsets) */
        uint32_t crc32Table[256];
-       unsigned char selectors[32768];                 /* nSelectors=15 bits */
-       struct group_data groups[MAX_GROUPS];   /* Huffman coding tables */
+       uint8_t selectors[32768];  /* nSelectors=15 bits */
+       struct group_data groups[MAX_GROUPS];  /* Huffman coding tables */
 };
 /* typedef struct bunzip_data bunzip_data; -- done in .h file */
 
 
 /* Return the next nnn bits of input.  All reads from the compressed input
    are done through this function.  All reads are big endian */
-
 static unsigned get_bits(bunzip_data *bd, int bits_wanted)
 {
        unsigned bits = 0;
+       /* Cache bd->inbufBitCount in a CPU register (hopefully): */
+       int bit_count = bd->inbufBitCount;
 
        /* If we need to get more data from the byte buffer, do so.  (Loop getting
           one byte at a time to enforce endianness and avoid unaligned access.) */
-       while ((int)(bd->inbufBitCount) < bits_wanted) {
+       while (bit_count < bits_wanted) {
 
                /* If we need to read more data from file into byte buffer, do so */
                if (bd->inbufPos == bd->inbufCount) {
@@ -113,41 +133,47 @@ static unsigned get_bits(bunzip_data *bd, int bits_wanted)
                }
 
                /* Avoid 32-bit overflow (dump bit buffer to top of output) */
-               if (bd->inbufBitCount >= 24) {
-                       bits = bd->inbufBits & ((1 << bd->inbufBitCount) - 1);
-                       bits_wanted -= bd->inbufBitCount;
+               if (bit_count >= 24) {
+                       bits = bd->inbufBits & ((1 << bit_count) - 1);
+                       bits_wanted -= bit_count;
                        bits <<= bits_wanted;
-                       bd->inbufBitCount = 0;
+                       bit_count = 0;
                }
 
                /* Grab next 8 bits of input from buffer. */
                bd->inbufBits = (bd->inbufBits << 8) | bd->inbuf[bd->inbufPos++];
-               bd->inbufBitCount += 8;
+               bit_count += 8;
        }
 
        /* Calculate result */
-       bd->inbufBitCount -= bits_wanted;
-       bits |= (bd->inbufBits >> bd->inbufBitCount) & ((1 << bits_wanted) - 1);
+       bit_count -= bits_wanted;
+       bd->inbufBitCount = bit_count;
+       bits |= (bd->inbufBits >> bit_count) & ((1 << bits_wanted) - 1);
 
        return bits;
 }
 
-/* Unpacks the next block and sets up for the inverse burrows-wheeler step. */
+/* Unpacks the next block and sets up for the inverse Burrows-Wheeler step. */
 static int get_next_block(bunzip_data *bd)
 {
        struct group_data *hufGroup;
-       int dbufCount, nextSym, dbufSize, groupCount, *base, *limit, selector,
-               i, j, k, t, runPos, symCount, symTotal, nSelectors, byteCount[256];
-       unsigned char uc, symToByte[256], mtfSymbol[256], *selectors;
-       unsigned *dbuf, origPtr;
+       int dbufCount, dbufSize, groupCount, *base, *limit, selector,
+               i, j, t, runPos, symCount, symTotal, nSelectors, byteCount[256];
+       int runCnt = runCnt; /* for compiler */
+       uint8_t uc, symToByte[256], mtfSymbol[256], *selectors;
+       uint32_t *dbuf;
+       unsigned origPtr;
 
        dbuf = bd->dbuf;
        dbufSize = bd->dbufSize;
        selectors = bd->selectors;
 
+/* In bbox, we are ok with aborting through setjmp which is set up in start_bunzip */
+#if 0
        /* Reset longjmp I/O error handling */
        i = setjmp(bd->jmpbuf);
        if (i) return i;
+#endif
 
        /* Read in header signature and CRC, then validate signature.
           (last block signature means CRC is for whole file, return now) */
@@ -169,16 +195,23 @@ static int get_next_block(bunzip_data *bd)
           symbols to deal with, and writes a sparse bitfield indicating which
           values were present.  We make a translation table to convert the symbols
           back to the corresponding bytes. */
-       t = get_bits(bd, 16);
        symTotal = 0;
-       for (i = 0; i < 16; i++) {
-               if (t & (1 << (15-i))) {
-                       k = get_bits(bd, 16);
-                       for (j = 0; j < 16; j++)
-                               if (k & (1 << (15-j)))
-                                       symToByte[symTotal++] = (16*i) + j;
+       i = 0;
+       t = get_bits(bd, 16);
+       do {
+               if (t & (1 << 15)) {
+                       unsigned inner_map = get_bits(bd, 16);
+                       do {
+                               if (inner_map & (1 << 15))
+                                       symToByte[symTotal++] = i;
+                               inner_map <<= 1;
+                               i++;
+                       } while (i & 15);
+                       i -= 16;
                }
-       }
+               t <<= 1;
+               i += 16;
+       } while (i < 256);
 
        /* How many different Huffman coding groups does this block use? */
        groupCount = get_bits(bd, 3);
@@ -189,57 +222,63 @@ static int get_next_block(bunzip_data *bd)
           group.  Read in the group selector list, which is stored as MTF encoded
           bit runs.  (MTF=Move To Front, as each value is used it's moved to the
           start of the list.) */
+       for (i = 0; i < groupCount; i++)
+               mtfSymbol[i] = i;
        nSelectors = get_bits(bd, 15);
-       if (!nSelectors) return RETVAL_DATA_ERROR;
-       for (i = 0; i < groupCount; i++) mtfSymbol[i] = i;
+       if (!nSelectors)
+               return RETVAL_DATA_ERROR;
        for (i = 0; i < nSelectors; i++) {
-
+               uint8_t tmp_byte;
                /* Get next value */
-               for (j = 0; get_bits(bd, 1); j++)
-                       if (j >= groupCount) return RETVAL_DATA_ERROR;
-
+               int n = 0;
+               while (get_bits(bd, 1)) {
+                       if (n >= groupCount) return RETVAL_DATA_ERROR;
+                       n++;
+               }
                /* Decode MTF to get the next selector */
-               uc = mtfSymbol[j];
-               for (;j;j--) mtfSymbol[j] = mtfSymbol[j-1];
-               mtfSymbol[0] = selectors[i] = uc;
+               tmp_byte = mtfSymbol[n];
+               while (--n >= 0)
+                       mtfSymbol[n + 1] = mtfSymbol[n];
+               mtfSymbol[0] = selectors[i] = tmp_byte;
        }
 
        /* Read the Huffman coding tables for each group, which code for symTotal
           literal symbols, plus two run symbols (RUNA, RUNB) */
        symCount = symTotal + 2;
        for (j = 0; j < groupCount; j++) {
-               unsigned char length[MAX_SYMBOLS];
+               uint8_t length[MAX_SYMBOLS];
                /* 8 bits is ALMOST enough for temp[], see below */
                unsigned temp[MAX_HUFCODE_BITS+1];
-               int minLen, maxLen, pp;
+               int minLen, maxLen, pp, len_m1;
 
                /* Read Huffman code lengths for each symbol.  They're stored in
                   a way similar to mtf; record a starting value for the first symbol,
-                  and an offset from the previous value for everys symbol after that.
+                  and an offset from the previous value for every symbol after that.
                   (Subtracting 1 before the loop and then adding it back at the end is
                   an optimization that makes the test inside the loop simpler: symbol
                   length 0 becomes negative, so an unsigned inequality catches it.) */
-               t = get_bits(bd, 5) - 1;
+               len_m1 = get_bits(bd, 5) - 1;
                for (i = 0; i < symCount; i++) {
                        for (;;) {
-                               if ((unsigned)t > (MAX_HUFCODE_BITS-1))
+                               int two_bits;
+                               if ((unsigned)len_m1 > (MAX_HUFCODE_BITS-1))
                                        return RETVAL_DATA_ERROR;
 
                                /* If first bit is 0, stop.  Else second bit indicates whether
                                   to increment or decrement the value.  Optimization: grab 2
                                   bits and unget the second if the first was 0. */
-                               k = get_bits(bd, 2);
-                               if (k < 2) {
+                               two_bits = get_bits(bd, 2);
+                               if (two_bits < 2) {
                                        bd->inbufBitCount++;
                                        break;
                                }
 
                                /* Add one if second bit 1, else subtract 1.  Avoids if/else */
-                               t += (((k+1) & 2) - 1);
+                               len_m1 += (((two_bits+1) & 2) - 1);
                        }
 
                        /* Correct for the initial -1, to get the final symbol length */
-                       length[i] = t + 1;
+                       length[i] = len_m1 + 1;
                }
 
                /* Find largest and smallest lengths in this group */
@@ -265,17 +304,18 @@ static int get_next_block(bunzip_data *bd)
 
                /* Note that minLen can't be smaller than 1, so we adjust the base
                   and limit array pointers so we're not always wasting the first
-                  entry.  We do this again when using them (during symbol decoding).*/
+                  entry.  We do this again when using them (during symbol decoding). */
                base = hufGroup->base - 1;
                limit = hufGroup->limit - 1;
 
                /* Calculate permute[].  Concurently, initialize temp[] and limit[]. */
                pp = 0;
                for (i = minLen; i <= maxLen; i++) {
+                       int k;
                        temp[i] = limit[i] = 0;
-                       for (t = 0; t < symCount; t++)
-                               if (length[t] == i)
-                                       hufGroup->permute[pp++] = t;
+                       for (k = 0; k < symCount; k++)
+                               if (length[k] == i)
+                                       hufGroup->permute[pp++] = k;
                }
 
                /* Count symbols coded for at each bit length */
@@ -288,8 +328,10 @@ static int get_next_block(bunzip_data *bd)
                 * base[] (number of symbols to ignore at each bit length, which is
                 * limit minus the cumulative count of symbols coded for already). */
                pp = t = 0;
-               for (i = minLen; i < maxLen; i++) {
-                       pp += temp[i];
+               for (i = minLen; i < maxLen;) {
+                       unsigned temp_i = temp[i];
+
+                       pp += temp_i;
 
                        /* We read the largest possible symbol size and then unget bits
                           after determining how many we need, and those extra bits could
@@ -299,11 +341,11 @@ static int get_next_block(bunzip_data *bd)
                           don't affect the value>limit[length] comparison. */
                        limit[i] = (pp << (maxLen - i)) - 1;
                        pp <<= 1;
-                       t += temp[i];
-                       base[i+1] = pp - t;
+                       t += temp_i;
+                       base[++i] = pp - t;
                }
-               limit[maxLen+1] = INT_MAX; /* Sentinel value for reading next sym. */
                limit[maxLen] = pp + temp[maxLen] - 1;
+               limit[maxLen+1] = INT_MAX; /* Sentinel value for reading next sym. */
                base[minLen] = 0;
        }
 
@@ -312,16 +354,17 @@ static int get_next_block(bunzip_data *bd)
           and run length encoding, saving the result into dbuf[dbufCount++] = uc */
 
        /* Initialize symbol occurrence counters and symbol Move To Front table */
-       memset(byteCount, 0, sizeof(byteCount)); /* smaller, maybe slower? */
+       /*memset(byteCount, 0, sizeof(byteCount)); - smaller, but slower */
        for (i = 0; i < 256; i++) {
-               //byteCount[i] = 0;
-               mtfSymbol[i] = (unsigned char)i;
+               byteCount[i] = 0;
+               mtfSymbol[i] = (uint8_t)i;
        }
 
        /* Loop through compressed symbols. */
 
        runPos = dbufCount = selector = 0;
        for (;;) {
+               int nextSym;
 
                /* Fetch next Huffman coding group from list. */
                symCount = GROUP_SIZE - 1;
@@ -329,44 +372,49 @@ static int get_next_block(bunzip_data *bd)
                hufGroup = bd->groups + selectors[selector++];
                base = hufGroup->base - 1;
                limit = hufGroup->limit - 1;
- continue_this_group:
 
+ continue_this_group:
                /* Read next Huffman-coded symbol. */
 
                /* Note: It is far cheaper to read maxLen bits and back up than it is
-                  to read minLen bits and then an additional bit at a time, testing
+                  to read minLen bits and then add additional bit at a time, testing
                   as we go.  Because there is a trailing last block (with file CRC),
                   there is no danger of the overread causing an unexpected EOF for a
-                  valid compressed file.  As a further optimization, we do the read
-                  inline (falling back to a call to get_bits if the buffer runs
-                  dry).  The following (up to got_huff_bits:) is equivalent to
-                  j = get_bits(bd, hufGroup->maxLen);
+                  valid compressed file.
                 */
-               while ((int)(bd->inbufBitCount) < hufGroup->maxLen) {
-                       if (bd->inbufPos == bd->inbufCount) {
-                               j = get_bits(bd, hufGroup->maxLen);
-                               goto got_huff_bits;
-                       }
-                       bd->inbufBits = (bd->inbufBits << 8) | bd->inbuf[bd->inbufPos++];
-                       bd->inbufBitCount += 8;
-               };
-               bd->inbufBitCount -= hufGroup->maxLen;
-               j = (bd->inbufBits >> bd->inbufBitCount) & ((1 << hufGroup->maxLen) - 1);
-
- got_huff_bits:
-
-               /* Figure how how many bits are in next symbol and unget extras */
+               if (1) {
+                       /* As a further optimization, we do the read inline
+                          (falling back to a call to get_bits if the buffer runs dry).
+                        */
+                       int new_cnt;
+                       while ((new_cnt = bd->inbufBitCount - hufGroup->maxLen) < 0) {
+                               /* bd->inbufBitCount < hufGroup->maxLen */
+                               if (bd->inbufPos == bd->inbufCount) {
+                                       nextSym = get_bits(bd, hufGroup->maxLen);
+                                       goto got_huff_bits;
+                               }
+                               bd->inbufBits = (bd->inbufBits << 8) | bd->inbuf[bd->inbufPos++];
+                               bd->inbufBitCount += 8;
+                       };
+                       bd->inbufBitCount = new_cnt; /* "bd->inbufBitCount -= hufGroup->maxLen;" */
+                       nextSym = (bd->inbufBits >> new_cnt) & ((1 << hufGroup->maxLen) - 1);
+ got_huff_bits: ;
+               } else { /* unoptimized equivalent */
+                       nextSym = get_bits(bd, hufGroup->maxLen);
+               }
+               /* Figure how many bits are in next symbol and unget extras */
                i = hufGroup->minLen;
-               while (j > limit[i]) ++i;
-               bd->inbufBitCount += (hufGroup->maxLen - i);
+               while (nextSym > limit[i]) ++i;
+               j = hufGroup->maxLen - i;
+               if (j < 0)
+                       return RETVAL_DATA_ERROR;
+               bd->inbufBitCount += j;
 
                /* Huffman decode value to get nextSym (with bounds checking) */
-               if (i > hufGroup->maxLen)
+               nextSym = (nextSym >> j) - base[i];
+               if ((unsigned)nextSym >= MAX_SYMBOLS)
                        return RETVAL_DATA_ERROR;
-               j = (j >> (hufGroup->maxLen - i)) - base[i];
-               if ((unsigned)j >= MAX_SYMBOLS)
-                       return RETVAL_DATA_ERROR;
-               nextSym = hufGroup->permute[j];
+               nextSym = hufGroup->permute[nextSym];
 
                /* We have now decoded the symbol, which indicates either a new literal
                   byte, or a repeated run of the most recent literal byte.  First,
@@ -375,9 +423,9 @@ static int get_next_block(bunzip_data *bd)
                if ((unsigned)nextSym <= SYMBOL_RUNB) { /* RUNA or RUNB */
 
                        /* If this is the start of a new run, zero out counter */
-                       if (!runPos) {
+                       if (runPos == 0) {
                                runPos = 1;
-                               t = 0;
+                               runCnt = 0;
                        }
 
                        /* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at
@@ -387,7 +435,7 @@ static int get_next_block(bunzip_data *bd)
                           the basic or 0/1 method (except all bits 0, which would use no
                           symbols, but a run of length 0 doesn't mean anything in this
                           context).  Thus space is saved. */
-                       t += (runPos << nextSym); /* +runPos if RUNA; +2*runPos if RUNB */
+                       runCnt += (runPos << nextSym); /* +runPos if RUNA; +2*runPos if RUNB */
                        if (runPos < dbufSize) runPos <<= 1;
                        goto end_of_huffman_loop;
                }
@@ -396,13 +444,17 @@ static int get_next_block(bunzip_data *bd)
                   how many times to repeat the last literal, so append that many
                   copies to our buffer of decoded symbols (dbuf) now.  (The last
                   literal used is the one at the head of the mtfSymbol array.) */
-               if (runPos) {
+               if (runPos != 0) {
+                       uint8_t tmp_byte;
+                       if (dbufCount + runCnt > dbufSize) {
+                               dbg("dbufCount:%d+runCnt:%d %d > dbufSize:%d RETVAL_DATA_ERROR",
+                                               dbufCount, runCnt, dbufCount + runCnt, dbufSize);
+                               return RETVAL_DATA_ERROR;
+                       }
+                       tmp_byte = symToByte[mtfSymbol[0]];
+                       byteCount[tmp_byte] += runCnt;
+                       while (--runCnt >= 0) dbuf[dbufCount++] = (uint32_t)tmp_byte;
                        runPos = 0;
-                       if (dbufCount + t >= dbufSize) return RETVAL_DATA_ERROR;
-
-                       uc = symToByte[mtfSymbol[0]];
-                       byteCount[uc] += t;
-                       while (t--) dbuf[dbufCount++] = uc;
                }
 
                /* Is this the terminating symbol? */
@@ -431,12 +483,12 @@ static int get_next_block(bunzip_data *bd)
 
                /* We have our literal byte.  Save it into dbuf. */
                byteCount[uc]++;
-               dbuf[dbufCount++] = (unsigned)uc;
+               dbuf[dbufCount++] = (uint32_t)uc;
 
                /* Skip group initialization if we're not done with this group.  Done
                 * this way to avoid compiler warning. */
  end_of_huffman_loop:
-               if (symCount--) goto continue_this_group;
+               if (--symCount >= 0) goto continue_this_group;
        }
 
        /* At this point, we've read all the Huffman-coded symbols (and repeated
@@ -449,26 +501,28 @@ static int get_next_block(bunzip_data *bd)
        /* Turn byteCount into cumulative occurrence counts of 0 to n-1. */
        j = 0;
        for (i = 0; i < 256; i++) {
-               k = j + byteCount[i];
+               int tmp_count = j + byteCount[i];
                byteCount[i] = j;
-               j = k;
+               j = tmp_count;
        }
 
        /* Figure out what order dbuf would be in if we sorted it. */
        for (i = 0; i < dbufCount; i++) {
-               uc = (unsigned char)(dbuf[i] & 0xff);
-               dbuf[byteCount[uc]] |= (i << 8);
-               byteCount[uc]++;
+               uint8_t tmp_byte = (uint8_t)dbuf[i];
+               int tmp_count = byteCount[tmp_byte];
+               dbuf[tmp_count] |= (i << 8);
+               byteCount[tmp_byte] = tmp_count + 1;
        }
 
        /* Decode first byte by hand to initialize "previous" byte.  Note that it
           doesn't get output, and if the first three characters are identical
           it doesn't qualify as a run (hence writeRunCountdown=5). */
        if (dbufCount) {
+               uint32_t tmp;
                if ((int)origPtr >= dbufCount) return RETVAL_DATA_ERROR;
-               bd->writePos = dbuf[origPtr];
-               bd->writeCurrent = (unsigned char)(bd->writePos & 0xff);
-               bd->writePos >>= 8;
+               tmp = dbuf[origPtr];
+               bd->writeCurrent = (uint8_t)tmp;
+               bd->writePos = (tmp >> 8);
                bd->writeRunCountdown = 5;
        }
        bd->writeCount = dbufCount;
@@ -476,70 +530,84 @@ static int get_next_block(bunzip_data *bd)
        return RETVAL_OK;
 }
 
-/* Undo burrows-wheeler transform on intermediate buffer to produce output.
+/* Undo Burrows-Wheeler transform on intermediate buffer to produce output.
    If start_bunzip was initialized with out_fd=-1, then up to len bytes of
    data are written to outbuf.  Return value is number of bytes written or
    error (all errors are negative numbers).  If out_fd!=-1, outbuf and len
    are ignored, data is written to out_fd and return is RETVAL_OK or error.
+
+   NB: read_bunzip returns < 0 on error, or the number of *unfilled* bytes
+   in outbuf. IOW: on EOF returns len ("all bytes are not filled"), not 0.
+   (Why? This allows to get rid of one local variable)
 */
 int FAST_FUNC read_bunzip(bunzip_data *bd, char *outbuf, int len)
 {
-       const unsigned *dbuf;
-       int pos, current, previous, gotcount;
+       const uint32_t *dbuf;
+       int pos, current, previous;
+       uint32_t CRC;
 
-       /* If last read was short due to end of file, return last block now */
-       if (bd->writeCount < 0) return bd->writeCount;
+       /* If we already have error/end indicator, return it */
+       if (bd->writeCount < 0)
+               return bd->writeCount;
 
-       gotcount = 0;
        dbuf = bd->dbuf;
+
+       /* Register-cached state (hopefully): */
        pos = bd->writePos;
        current = bd->writeCurrent;
+       CRC = bd->writeCRC; /* small loss on x86-32 (not enough regs), win on x86-64 */
 
        /* We will always have pending decoded data to write into the output
           buffer unless this is the very first call (in which case we haven't
           Huffman-decoded a block into the intermediate buffer yet). */
        if (bd->writeCopies) {
 
+ dec_writeCopies:
                /* Inside the loop, writeCopies means extra copies (beyond 1) */
                --bd->writeCopies;
 
                /* Loop outputting bytes */
                for (;;) {
 
-                       /* If the output buffer is full, snapshot state and return */
-                       if (gotcount >= len) {
-                               bd->writePos = pos;
-                               bd->writeCurrent = current;
-                               bd->writeCopies++;
-                               return len;
+                       /* If the output buffer is full, save cached state and return */
+                       if (--len < 0) {
+                               /* Unlikely branch.
+                                * Use of "goto" instead of keeping code here
+                                * helps compiler to realize this. */
+                               goto outbuf_full;
                        }
 
                        /* Write next byte into output buffer, updating CRC */
-                       outbuf[gotcount++] = current;
-                       bd->writeCRC = (bd->writeCRC << 8)
-                               ^ bd->crc32Table[(bd->writeCRC >> 24) ^ current];
+                       *outbuf++ = current;
+                       CRC = (CRC << 8) ^ bd->crc32Table[(CRC >> 24) ^ current];
 
                        /* Loop now if we're outputting multiple copies of this byte */
                        if (bd->writeCopies) {
-                               --bd->writeCopies;
-                               continue;
+                               /* Unlikely branch */
+                               /*--bd->writeCopies;*/
+                               /*continue;*/
+                               /* Same, but (ab)using other existing --writeCopies operation
+                                * (and this if() compiles into just test+branch pair): */
+                               goto dec_writeCopies;
                        }
  decode_next_byte:
-                       if (!bd->writeCount--) break;
+                       if (--bd->writeCount < 0)
+                               break; /* input block is fully consumed, need next one */
+
                        /* Follow sequence vector to undo Burrows-Wheeler transform */
                        previous = current;
                        pos = dbuf[pos];
-                       current = pos & 0xff;
+                       current = (uint8_t)pos;
                        pos >>= 8;
 
                        /* After 3 consecutive copies of the same byte, the 4th
                         * is a repeat count.  We count down from 4 instead
                         * of counting up because testing for non-zero is faster */
-                       if (--bd->writeRunCountdown) {
+                       if (--bd->writeRunCountdown != 0) {
                                if (current != previous)
                                        bd->writeRunCountdown = 4;
                        } else {
-
+                               /* Unlikely branch */
                                /* We have a repeated run, this byte indicates the count */
                                bd->writeCopies = current;
                                current = previous;
@@ -551,30 +619,42 @@ int FAST_FUNC read_bunzip(bunzip_data *bd, char *outbuf, int len)
                                /* Subtract the 1 copy we'd output anyway to get extras */
                                --bd->writeCopies;
                        }
-               }
+               } /* for(;;) */
 
-               /* Decompression of this block completed successfully */
-               bd->writeCRC = ~bd->writeCRC;
-               bd->totalCRC = ((bd->totalCRC << 1) | (bd->totalCRC >> 31)) ^ bd->writeCRC;
+               /* Decompression of this input block completed successfully */
+               bd->writeCRC = CRC = ~CRC;
+               bd->totalCRC = ((bd->totalCRC << 1) | (bd->totalCRC >> 31)) ^ CRC;
 
-               /* If this block had a CRC error, force file level CRC error. */
-               if (bd->writeCRC != bd->headerCRC) {
+               /* If this block had a CRC error, force file level CRC error */
+               if (CRC != bd->headerCRC) {
                        bd->totalCRC = bd->headerCRC + 1;
                        return RETVAL_LAST_BLOCK;
                }
        }
 
        /* Refill the intermediate buffer by Huffman-decoding next block of input */
-       /* (previous is just a convenient unused temp variable here) */
-       previous = get_next_block(bd);
-       if (previous) {
-               bd->writeCount = previous;
-               return (previous != RETVAL_LAST_BLOCK) ? previous : gotcount;
+       {
+               int r = get_next_block(bd);
+               if (r) { /* error/end */
+                       bd->writeCount = r;
+                       return (r != RETVAL_LAST_BLOCK) ? r : len;
+               }
        }
-       bd->writeCRC = ~0;
+
+       CRC = ~0;
        pos = bd->writePos;
        current = bd->writeCurrent;
        goto decode_next_byte;
+
+ outbuf_full:
+       /* Output buffer is full, save cached state and return */
+       bd->writePos = pos;
+       bd->writeCurrent = current;
+       bd->writeCRC = CRC;
+
+       bd->writeCopies++;
+
+       return 0;
 }
 
 /* Allocate the structure, read file header.  If in_fd==-1, inbuf must contain
@@ -584,8 +664,8 @@ int FAST_FUNC read_bunzip(bunzip_data *bd, char *outbuf, int len)
 /* Because bunzip2 is used for help text unpacking, and because bb_show_usage()
    should work for NOFORK applets too, we must be extremely careful to not leak
    any allocations! */
-int FAST_FUNC start_bunzip(bunzip_data **bdp, int in_fd, const unsigned char *inbuf,
-                                               int len)
+int FAST_FUNC start_bunzip(bunzip_data **bdp, int in_fd,
+               const void *inbuf, int len)
 {
        bunzip_data *bd;
        unsigned i;
@@ -606,9 +686,11 @@ int FAST_FUNC start_bunzip(bunzip_data **bdp, int in_fd, const unsigned char *in
        if (-1 == in_fd) {
                /* in this case, bd->inbuf is read-only */
                bd->inbuf = (void*)inbuf; /* cast away const-ness */
-               bd->inbufCount = len;
-       } else
-               bd->inbuf = (unsigned char *)(bd + 1);
+       } else {
+               bd->inbuf = (uint8_t*)(bd + 1);
+               memcpy(bd->inbuf, inbuf, len);
+       }
+       bd->inbufCount = len;
 
        /* Init the CRC32 table (big endian) */
        crc32_filltable(bd->crc32Table, 1);
@@ -632,7 +714,7 @@ int FAST_FUNC start_bunzip(bunzip_data **bdp, int in_fd, const unsigned char *in
        bd->dbufSize = 100000 * (i - h0);
 
        /* Cannot use xmalloc - may leak bd in NOFORK case! */
-       bd->dbuf = malloc_or_warn(bd->dbufSize * sizeof(int));
+       bd->dbuf = malloc_or_warn(bd->dbufSize * sizeof(bd->dbuf[0]));
        if (!bd->dbuf) {
                free(bd);
                xfunc_die();
@@ -649,57 +731,82 @@ void FAST_FUNC dealloc_bunzip(bunzip_data *bd)
 
 /* Decompress src_fd to dst_fd.  Stops at end of bzip data, not end of file. */
 IF_DESKTOP(long long) int FAST_FUNC
-unpack_bz2_stream(int src_fd, int dst_fd)
+unpack_bz2_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
 {
        IF_DESKTOP(long long total_written = 0;)
-       char *outbuf;
        bunzip_data *bd;
+       char *outbuf;
        int i;
+       unsigned len;
+
+       if (check_signature16(aux, src_fd, BZIP2_MAGIC))
+               return -1;
 
        outbuf = xmalloc(IOBUF_SIZE);
-       i = start_bunzip(&bd, src_fd, NULL, 0);
-       if (!i) {
-               for (;;) {
-                       i = read_bunzip(bd, outbuf, IOBUF_SIZE);
-                       if (i <= 0) break;
-                       if (i != full_write(dst_fd, outbuf, i)) {
-                               i = RETVAL_SHORT_WRITE;
-                               break;
+       len = 0;
+       while (1) { /* "Process one BZ... stream" loop */
+
+               i = start_bunzip(&bd, src_fd, outbuf + 2, len);
+
+               if (i == 0) {
+                       while (1) { /* "Produce some output bytes" loop */
+                               i = read_bunzip(bd, outbuf, IOBUF_SIZE);
+                               if (i < 0) /* error? */
+                                       break;
+                               i = IOBUF_SIZE - i; /* number of bytes produced */
+                               if (i == 0) /* EOF? */
+                                       break;
+                               if (i != full_write(dst_fd, outbuf, i)) {
+                                       bb_error_msg("short write");
+                                       i = RETVAL_SHORT_WRITE;
+                                       goto release_mem;
+                               }
+                               IF_DESKTOP(total_written += i;)
                        }
-                       IF_DESKTOP(total_written += i;)
                }
-       }
-
-       /* Check CRC and release memory */
 
-       if (i == RETVAL_LAST_BLOCK) {
+               if (i != RETVAL_LAST_BLOCK
+               /* Observed case when i == RETVAL_OK:
+                * "bzcat z.bz2", where "z.bz2" is a bzipped zero-length file
+                * (to be exact, z.bz2 is exactly these 14 bytes:
+                * 42 5a 68 39 17 72 45 38  50 90 00 00 00 00).
+                */
+                && i != RETVAL_OK
+               ) {
+                       bb_error_msg("bunzip error %d", i);
+                       break;
+               }
                if (bd->headerCRC != bd->totalCRC) {
                        bb_error_msg("CRC error");
-               } else {
-                       i = RETVAL_OK;
+                       break;
                }
-       } else if (i == RETVAL_SHORT_WRITE) {
-               bb_error_msg("short write");
-       } else {
-               bb_error_msg("bunzip error %d", i);
+
+               /* Successfully unpacked one BZ stream */
+               i = RETVAL_OK;
+
+               /* Do we have "BZ..." after last processed byte?
+                * pbzip2 (parallelized bzip2) produces such files.
+                */
+               len = bd->inbufCount - bd->inbufPos;
+               memcpy(outbuf, &bd->inbuf[bd->inbufPos], len);
+               if (len < 2) {
+                       if (safe_read(src_fd, outbuf + len, 2 - len) != 2 - len)
+                               break;
+                       len = 2;
+               }
+               if (*(uint16_t*)outbuf != BZIP2_MAGIC) /* "BZ"? */
+                       break;
+               dealloc_bunzip(bd);
+               len -= 2;
        }
+
+ release_mem:
        dealloc_bunzip(bd);
        free(outbuf);
 
        return i ? i : IF_DESKTOP(total_written) + 0;
 }
 
-IF_DESKTOP(long long) int FAST_FUNC
-unpack_bz2_stream_prime(int src_fd, int dst_fd)
-{
-       uint16_t magic2;
-       xread(src_fd, &magic2, 2);
-       if (magic2 != BZIP2_MAGIC) {
-               bb_error_msg_and_die("invalid magic");
-       }
-       return unpack_bz2_stream(src_fd, dst_fd);
-}
-
 #ifdef TESTING
 
 static char *const bunzip_errors[] = {
@@ -711,10 +818,9 @@ static char *const bunzip_errors[] = {
 /* Dumb little test thing, decompress stdin to stdout */
 int main(int argc, char **argv)
 {
-       int i;
        char c;
 
-       int i = unpack_bz2_stream_prime(0, 1);
+       int i = unpack_bz2_stream(0, 1);
        if (i < 0)
                fprintf(stderr, "%s\n", bunzip_errors[-i]);
        else if (read(STDIN_FILENO, &c, 1))
similarity index 96%
rename from archival/libunarchive/decompress_unzip.c
rename to archival/libarchive/decompress_gunzip.c
index bccd026..7c6f38e 100644 (file)
  *
  * See the file algorithm.doc for the compression algorithms and file formats.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include <setjmp.h>
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 typedef struct huft_t {
        unsigned char e;        /* number of extra bits or operation */
@@ -293,8 +293,8 @@ static unsigned fill_bitbuffer(STATE_PARAM unsigned bitbuffer, unsigned *current
  * m:  maximum lookup bits, returns actual
  */
 static int huft_build(const unsigned *b, const unsigned n,
-                          const unsigned s, const unsigned short *d,
-                          const unsigned char *e, huft_t **t, unsigned *m)
+                       const unsigned s, const unsigned short *d,
+                       const unsigned char *e, huft_t **t, unsigned *m)
 {
        unsigned a;             /* counter for codes of length k */
        unsigned c[BMAX + 1];   /* bit length count table */
@@ -336,7 +336,7 @@ static int huft_build(const unsigned *b, const unsigned n,
        }
 
        /* Find minimum and maximum length, bound *m by those */
-       for (j = 1; (c[j] == 0) && (j <= BMAX); j++)
+       for (j = 1; (j <= BMAX) && (c[j] == 0); j++)
                continue;
        k = j; /* minimum code length */
        for (i = BMAX; (c[i] == 0) && i; i--)
@@ -925,10 +925,7 @@ static int inflate_block(STATE_PARAM smallint *e)
 /* Two callsites, both in inflate_get_next_window */
 static void calculate_gunzip_crc(STATE_PARAM_ONLY)
 {
-       unsigned n;
-       for (n = 0; n < gunzip_outbuf_count; n++) {
-               gunzip_crc = gunzip_crc_table[((int) gunzip_crc ^ (gunzip_window[n])) & 0xff] ^ (gunzip_crc >> 8);
-       }
+       gunzip_crc = crc32_block_endian0(gunzip_crc, gunzip_window, gunzip_outbuf_count, gunzip_crc_table);
        gunzip_bytes_out += gunzip_outbuf_count;
 }
 
@@ -1037,22 +1034,22 @@ inflate_unzip_internal(STATE_PARAM int in, int out)
 /* For unzip */
 
 IF_DESKTOP(long long) int FAST_FUNC
-inflate_unzip(inflate_unzip_result *res, off_t compr_size, int in, int out)
+inflate_unzip(transformer_aux_data_t *aux, int in, int out)
 {
        IF_DESKTOP(long long) int n;
        DECLARE_STATE;
 
        ALLOC_STATE;
 
-       to_read = compr_size;
+       to_read = aux->bytes_in;
 //     bytebuffer_max = 0x8000;
        bytebuffer_offset = 4;
        bytebuffer = xmalloc(bytebuffer_max);
        n = inflate_unzip_internal(PASS_STATE in, out);
        free(bytebuffer);
 
-       res->crc = gunzip_crc;
-       res->bytes_out = gunzip_bytes_out;
+       aux->crc32 = gunzip_crc;
+       aux->bytes_out = gunzip_bytes_out;
        DEALLOC_STATE;
        return n;
 }
@@ -1110,7 +1107,7 @@ static uint32_t buffer_read_le_u32(STATE_PARAM_ONLY)
        return res;
 }
 
-static int check_header_gzip(STATE_PARAM unpack_info_t *info)
+static int check_header_gzip(STATE_PARAM transformer_aux_data_t *aux)
 {
        union {
                unsigned char raw[8];
@@ -1172,8 +1169,8 @@ static int check_header_gzip(STATE_PARAM unpack_info_t *info)
                }
        }
 
-       if (info)
-               info->mtime = SWAP_LE32(header.formatted.mtime);
+       if (aux)
+               aux->mtime = SWAP_LE32(header.formatted.mtime);
 
        /* Read the header checksum */
        if (header.formatted.flags & 0x02) {
@@ -1185,33 +1182,58 @@ static int check_header_gzip(STATE_PARAM unpack_info_t *info)
 }
 
 IF_DESKTOP(long long) int FAST_FUNC
-unpack_gz_stream_with_info(int in, int out, unpack_info_t *info)
+unpack_gz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
 {
        uint32_t v32;
-       IF_DESKTOP(long long) int n;
+       IF_DESKTOP(long long) int total, n;
        DECLARE_STATE;
 
-       n = 0;
+#if !ENABLE_FEATURE_SEAMLESS_Z
+       if (check_signature16(aux, src_fd, GZIP_MAGIC))
+               return -1;
+#else
+       if (aux && aux->check_signature) {
+               uint16_t magic2;
+
+               if (full_read(src_fd, &magic2, 2) != 2) {
+ bad_magic:
+                       bb_error_msg("invalid magic");
+                       return -1;
+               }
+               if (magic2 == COMPRESS_MAGIC) {
+                       aux->check_signature = 0;
+                       return unpack_Z_stream(aux, src_fd, dst_fd);
+               }
+               if (magic2 != GZIP_MAGIC)
+                       goto bad_magic;
+       }
+#endif
+
+       total = 0;
 
        ALLOC_STATE;
        to_read = -1;
 //     bytebuffer_max = 0x8000;
        bytebuffer = xmalloc(bytebuffer_max);
-       gunzip_src_fd = in;
+       gunzip_src_fd = src_fd;
 
  again:
-       if (!check_header_gzip(PASS_STATE info)) {
+       if (!check_header_gzip(PASS_STATE aux)) {
                bb_error_msg("corrupted data");
-               n = -1;
+               total = -1;
                goto ret;
        }
-       n += inflate_unzip_internal(PASS_STATE in, out);
-       if (n < 0)
+
+       n = inflate_unzip_internal(PASS_STATE src_fd, dst_fd);
+       if (n < 0) {
+               total = -1;
                goto ret;
+       }
+       total += n;
 
        if (!top_up(PASS_STATE 8)) {
                bb_error_msg("corrupted data");
-               n = -1;
+               total = -1;
                goto ret;
        }
 
@@ -1219,7 +1241,7 @@ unpack_gz_stream_with_info(int in, int out, unpack_info_t *info)
        v32 = buffer_read_le_u32(PASS_STATE_ONLY);
        if ((~gunzip_crc) != v32) {
                bb_error_msg("crc error");
-               n = -1;
+               total = -1;
                goto ret;
        }
 
@@ -1227,7 +1249,7 @@ unpack_gz_stream_with_info(int in, int out, unpack_info_t *info)
        v32 = buffer_read_le_u32(PASS_STATE_ONLY);
        if ((uint32_t)gunzip_bytes_out != v32) {
                bb_error_msg("incorrect length");
-               n = -1;
+               total = -1;
        }
 
        if (!top_up(PASS_STATE 2))
@@ -1245,11 +1267,5 @@ unpack_gz_stream_with_info(int in, int out, unpack_info_t *info)
  ret:
        free(bytebuffer);
        DEALLOC_STATE;
-       return n;
-}
-
-IF_DESKTOP(long long) int FAST_FUNC
-unpack_gz_stream(int in, int out)
-{
-       return unpack_gz_stream_with_info(in, out, NULL);
+       return total;
 }
similarity index 85%
rename from archival/libunarchive/decompress_uncompress.c
rename to archival/libarchive/decompress_uncompress.c
index 1ff89ce..53c2708 100644 (file)
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 
 /* Default input buffer size */
-#define        IBUFSIZ 2048
+#define IBUFSIZ 2048
 
 /* Default output buffer size */
-#define        OBUFSIZ 2048
+#define OBUFSIZ 2048
 
 /* Defines for third byte of header */
 #define BIT_MASK        0x1f    /* Mask for 'number of compresssion bits'       */
  */
 
 IF_DESKTOP(long long) int FAST_FUNC
-unpack_Z_stream(int fd_in, int fd_out)
+unpack_Z_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
 {
        IF_DESKTOP(long long total_written = 0;)
        IF_DESKTOP(long long) int retval = -1;
        unsigned char *stackp;
-       long code;
        int finchar;
        long oldcode;
        long incode;
@@ -103,16 +102,19 @@ unpack_Z_stream(int fd_in, int fd_out)
        /* block compress mode -C compatible with 2.0 */
        int block_mode; /* = BLOCK_MODE; */
 
+       if (check_signature16(aux, src_fd, COMPRESS_MAGIC))
+               return -1;
+
        inbuf = xzalloc(IBUFSIZ + 64);
        outbuf = xzalloc(OBUFSIZ + 2048);
-       htab = xzalloc(HSIZE);  /* wsn't zeroed out before, maybe can xmalloc? */
+       htab = xzalloc(HSIZE);  /* wasn't zeroed out before, maybe can xmalloc? */
        codetab = xzalloc(HSIZE * sizeof(codetab[0]));
 
        insize = 0;
 
        /* xread isn't good here, we have to return - caller may want
         * to do some cleanup (e.g. delete incomplete unpacked file etc) */
-       if (full_read(fd_in, inbuf, 1) != 1) {
+       if (full_read(src_fd, inbuf, 1) != 1) {
                bb_error_msg("short read");
                goto err;
        }
@@ -140,8 +142,10 @@ unpack_Z_stream(int fd_in, int fd_out)
        /* As above, initialize the first 256 entries in the table. */
        /*clear_tab_prefixof(); - done by xzalloc */
 
-       for (code = 255; code >= 0; --code) {
-               tab_suffixof(code) = (unsigned char) code;
+       {
+               int i;
+               for (i = 255; i >= 0; --i)
+                       tab_suffixof(i) = (unsigned char) i;
        }
 
        do {
@@ -162,8 +166,9 @@ unpack_Z_stream(int fd_in, int fd_out)
                }
 
                if (insize < (int) (IBUFSIZ + 64) - IBUFSIZ) {
-                       rsize = safe_read(fd_in, inbuf + insize, IBUFSIZ);
-//error check??
+                       rsize = safe_read(src_fd, inbuf + insize, IBUFSIZ);
+                       if (rsize < 0)
+                               bb_error_msg_and_die(bb_msg_read_error);
                        insize += rsize;
                }
 
@@ -171,6 +176,8 @@ unpack_Z_stream(int fd_in, int fd_out)
                                  (insize << 3) - (n_bits - 1));
 
                while (inbits > posbits) {
+                       long code;
+
                        if (free_ent > maxcode) {
                                posbits =
                                        ((posbits - 1) +
@@ -187,14 +194,15 @@ unpack_Z_stream(int fd_in, int fd_out)
                        }
                        {
                                unsigned char *p = &inbuf[posbits >> 3];
-
-                               code = ((((long) (p[0])) | ((long) (p[1]) << 8) |
-                                        ((long) (p[2]) << 16)) >> (posbits & 0x7)) & bitmask;
+                               code = ((p[0]
+                                       | ((long) (p[1]) << 8)
+                                       | ((long) (p[2]) << 16)) >> (posbits & 0x7)) & bitmask;
                        }
                        posbits += n_bits;
 
-
                        if (oldcode == -1) {
+                               if (code >= 256)
+                                       bb_error_msg_and_die("corrupted data"); /* %ld", code); */
                                oldcode = code;
                                finchar = (int) oldcode;
                                outbuf[outpos++] = (unsigned char) finchar;
@@ -220,15 +228,16 @@ unpack_Z_stream(int fd_in, int fd_out)
                        /* Special case for KwKwK string. */
                        if (code >= free_ent) {
                                if (code > free_ent) {
+/*
                                        unsigned char *p;
 
                                        posbits -= n_bits;
                                        p = &inbuf[posbits >> 3];
-
                                        bb_error_msg
                                                ("insize:%d posbits:%d inbuf:%02X %02X %02X %02X %02X (%d)",
-                                                insize, posbits, p[-1], p[0], p[1], p[2], p[3],
-                                                (posbits & 07));
+                                               insize, posbits, p[-1], p[0], p[1], p[2], p[3],
+                                               (posbits & 07));
+*/
                                        bb_error_msg("corrupted data");
                                        goto err;
                                }
@@ -238,7 +247,9 @@ unpack_Z_stream(int fd_in, int fd_out)
                        }
 
                        /* Generate output characters in reverse order */
-                       while ((long) code >= (long) 256) {
+                       while (code >= 256) {
+                               if (stackp <= &htabof(0))
+                                       bb_error_msg_and_die("corrupted data");
                                *--stackp = tab_suffixof(code);
                                code = tab_prefixof(code);
                        }
@@ -263,8 +274,7 @@ unpack_Z_stream(int fd_in, int fd_out)
                                                }
 
                                                if (outpos >= OBUFSIZ) {
-                                                       full_write(fd_out, outbuf, outpos);
-//error check??
+                                                       xwrite(dst_fd, outbuf, outpos);
                                                        IF_DESKTOP(total_written += outpos;)
                                                        outpos = 0;
                                                }
@@ -278,11 +288,10 @@ unpack_Z_stream(int fd_in, int fd_out)
                        }
 
                        /* Generate the new entry. */
-                       code = free_ent;
-                       if (code < maxmaxcode) {
-                               tab_prefixof(code) = (unsigned short) oldcode;
-                               tab_suffixof(code) = (unsigned char) finchar;
-                               free_ent = code + 1;
+                       if (free_ent < maxmaxcode) {
+                               tab_prefixof(free_ent) = (unsigned short) oldcode;
+                               tab_suffixof(free_ent) = (unsigned char) finchar;
+                               free_ent++;
                        }
 
                        /* Remember previous code.  */
@@ -292,8 +301,7 @@ unpack_Z_stream(int fd_in, int fd_out)
        } while (rsize > 0);
 
        if (outpos > 0) {
-               full_write(fd_out, outbuf, outpos);
-//error check??
+               xwrite(dst_fd, outbuf, outpos);
                IF_DESKTOP(total_written += outpos;)
        }
 
similarity index 95%
rename from archival/libunarchive/decompress_unlzma.c
rename to archival/libarchive/decompress_unlzma.c
index ecda174..ca32bd8 100644 (file)
@@ -6,10 +6,10 @@
  * Based on LzmaDecode.c from the LZMA SDK 4.22 (http://www.7-zip.org/)
  * Copyright (C) 1999-2005  Igor Pavlov
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 #if ENABLE_FEATURE_LZMA_FAST
 #  define speed_inline ALWAYS_INLINE
@@ -45,16 +45,16 @@ typedef struct {
 #define RC_MODEL_TOTAL_BITS 11
 
 
-/* Called twice: once at startup (LZMA_FAST only) and once in rc_normalize() */
-static size_inline void rc_read(rc_t *rc)
+/* Called once in rc_do_normalize() */
+static void rc_read(rc_t *rc)
 {
        int buffer_size = safe_read(rc->fd, RC_BUFFER, RC_BUFFER_SIZE);
 //TODO: return -1 instead
 //This will make unlzma delete broken unpacked file on unpack errors
        if (buffer_size <= 0)
                bb_error_msg_and_die("unexpected EOF");
-       rc->ptr = RC_BUFFER;
        rc->buffer_end = RC_BUFFER + buffer_size;
+       rc->ptr = RC_BUFFER;
 }
 
 /* Called twice, but one callsite is in speed_inline'd rc_is_bit_1() */
@@ -65,6 +65,12 @@ static void rc_do_normalize(rc_t *rc)
        rc->range <<= 8;
        rc->code = (rc->code << 8) | *rc->ptr++;
 }
+static ALWAYS_INLINE void rc_normalize(rc_t *rc)
+{
+       if (rc->range < (1 << RC_TOP_BITS)) {
+               rc_do_normalize(rc);
+       }
+}
 
 /* Called once */
 static ALWAYS_INLINE rc_t* rc_init(int fd) /*, int buffer_size) */
@@ -78,15 +84,9 @@ static ALWAYS_INLINE rc_t* rc_init(int fd) /*, int buffer_size) */
        /* rc->ptr = rc->buffer_end; */
 
        for (i = 0; i < 5; i++) {
-#if ENABLE_FEATURE_LZMA_FAST
-               if (rc->ptr >= rc->buffer_end)
-                       rc_read(rc);
-               rc->code = (rc->code << 8) | *rc->ptr++;
-#else
                rc_do_normalize(rc);
-#endif
        }
-       rc->range = 0xFFFFFFFF;
+       rc->range = 0xffffffff;
        return rc;
 }
 
@@ -96,13 +96,6 @@ static ALWAYS_INLINE void rc_free(rc_t *rc)
        free(rc);
 }
 
-static ALWAYS_INLINE void rc_normalize(rc_t *rc)
-{
-       if (rc->range < (1 << RC_TOP_BITS)) {
-               rc_do_normalize(rc);
-       }
-}
-
 /* rc_is_bit_1 is called 9 times */
 static speed_inline int rc_is_bit_1(rc_t *rc, uint16_t *p)
 {
@@ -120,7 +113,7 @@ static speed_inline int rc_is_bit_1(rc_t *rc, uint16_t *p)
 }
 
 /* Called 4 times in unlzma loop */
-static speed_inline int rc_get_bit(rc_t *rc, uint16_t *p, int *symbol)
+static ALWAYS_INLINE int rc_get_bit(rc_t *rc, uint16_t *p, int *symbol)
 {
        int ret = rc_is_bit_1(rc, p);
        *symbol = *symbol * 2 + ret;
@@ -213,7 +206,7 @@ enum {
 
 
 IF_DESKTOP(long long) int FAST_FUNC
-unpack_lzma_stream(int src_fd, int dst_fd)
+unpack_lzma_stream(transformer_aux_data_t *aux UNUSED_PARAM, int src_fd, int dst_fd)
 {
        IF_DESKTOP(long long total_written = 0;)
        lzma_header_t header;
similarity index 54%
rename from archival/libunarchive/decompress_unxz.c
rename to archival/libarchive/decompress_unxz.c
index 721acd9..986b7b1 100644 (file)
@@ -7,10 +7,10 @@
  *
  * This file is:
  * Copyright (C) 2010 Denys Vlasenko <vda.linux@googlemail.com>
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 #define XZ_FUNC FAST_FUNC
 #define XZ_EXTERN static
 
 /* We use our own crc32 function */
 #define XZ_INTERNAL_CRC32 0
-static uint32_t *crc32_table;
 static uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
 {
-       crc = ~crc;
-
-       while (size != 0) {
-               crc = crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8);
-               --size;
-       }
-
-       return ~crc;
+       return ~crc32_block_endian0(~crc, buf, size, global_crc32_table);
 }
 
 /* We use arch-optimized unaligned accessors */
 #define get_unaligned_le32(buf) ({ uint32_t v; move_from_unaligned32(v, buf); SWAP_LE32(v); })
 #define get_unaligned_be32(buf) ({ uint32_t v; move_from_unaligned32(v, buf); SWAP_BE32(v); })
-#define put_unaligned_le32(val, buf) move_to_unaligned16(buf, SWAP_LE32(val))
-#define put_unaligned_be32(val, buf) move_to_unaligned16(buf, SWAP_BE32(val))
+#define put_unaligned_le32(val, buf) move_to_unaligned32(buf, SWAP_LE32(val))
+#define put_unaligned_be32(val, buf) move_to_unaligned32(buf, SWAP_BE32(val))
 
 #include "unxz/xz_dec_bcj.c"
 #include "unxz/xz_dec_lzma2.c"
 #include "unxz/xz_dec_stream.c"
 
 IF_DESKTOP(long long) int FAST_FUNC
-unpack_xz_stream(int src_fd, int dst_fd)
+unpack_xz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
 {
+       enum xz_ret xz_result;
        struct xz_buf iobuf;
        struct xz_dec *state;
        unsigned char *membuf;
        IF_DESKTOP(long long) int total = 0;
 
-       if (!crc32_table)
-               crc32_table = crc32_filltable(NULL, /*endian:*/ 0);
+       if (!global_crc32_table)
+               global_crc32_table = crc32_filltable(NULL, /*endian:*/ 0);
 
        memset(&iobuf, 0, sizeof(iobuf));
-       /* Preload XZ file signature */
-       membuf = (void*) strcpy(xmalloc(2 * BUFSIZ), HEADER_MAGIC);
+       membuf = xmalloc(2 * BUFSIZ);
        iobuf.in = membuf;
-       iobuf.in_size = HEADER_MAGIC_SIZE;
        iobuf.out = membuf + BUFSIZ;
        iobuf.out_size = BUFSIZ;
 
+       if (!aux || aux->check_signature == 0) {
+               /* Preload XZ file signature */
+               strcpy((char*)membuf, HEADER_MAGIC);
+               iobuf.in_size = HEADER_MAGIC_SIZE;
+       } /* else: let xz code read & check it */
+
        /* Limit memory usage to about 64 MiB. */
        state = xz_dec_init(XZ_DYNALLOC, 64*1024*1024);
 
+       xz_result = X_OK;
        while (1) {
-               enum xz_ret r;
-
                if (iobuf.in_pos == iobuf.in_size) {
                        int rd = safe_read(src_fd, membuf, BUFSIZ);
                        if (rd < 0) {
@@ -77,28 +73,57 @@ unpack_xz_stream(int src_fd, int dst_fd)
                                total = -1;
                                break;
                        }
+                       if (rd == 0 && xz_result == XZ_STREAM_END)
+                               break;
                        iobuf.in_size = rd;
                        iobuf.in_pos = 0;
                }
+               if (xz_result == XZ_STREAM_END) {
+                       /*
+                        * Try to start decoding next concatenated stream.
+                        * Stream padding must always be a multiple of four
+                        * bytes to preserve four-byte alignment. To keep the
+                        * code slightly smaller, we aren't as strict here as
+                        * the .xz spec requires. We just skip all zero-bytes
+                        * without checking the alignment and thus can accept
+                        * files that aren't valid, e.g. the XZ utils test
+                        * files bad-0pad-empty.xz and bad-0catpad-empty.xz.
+                        */
+                       do {
+                               if (membuf[iobuf.in_pos] != 0) {
+                                       xz_dec_reset(state);
+                                       goto do_run;
+                               }
+                               iobuf.in_pos++;
+                       } while (iobuf.in_pos < iobuf.in_size);
+               }
+ do_run:
 //             bb_error_msg(">in pos:%d size:%d out pos:%d size:%d",
 //                             iobuf.in_pos, iobuf.in_size, iobuf.out_pos, iobuf.out_size);
-               r = xz_dec_run(state, &iobuf);
+               xz_result = xz_dec_run(state, &iobuf);
 //             bb_error_msg("<in pos:%d size:%d out pos:%d size:%d r:%d",
-//                             iobuf.in_pos, iobuf.in_size, iobuf.out_pos, iobuf.out_size, r);
+//                             iobuf.in_pos, iobuf.in_size, iobuf.out_pos, iobuf.out_size, xz_result);
                if (iobuf.out_pos) {
                        xwrite(dst_fd, iobuf.out, iobuf.out_pos);
                        IF_DESKTOP(total += iobuf.out_pos;)
                        iobuf.out_pos = 0;
                }
-               if (r == XZ_STREAM_END) {
-                       break;
+               if (xz_result == XZ_STREAM_END) {
+                       /*
+                        * Can just "break;" here, if not for concatenated
+                        * .xz streams.
+                        * Checking for padding may require buffer
+                        * replenishment. Can't do it here.
+                        */
+                       continue;
                }
-               if (r != XZ_OK && r != XZ_UNSUPPORTED_CHECK) {
+               if (xz_result != XZ_OK && xz_result != XZ_UNSUPPORTED_CHECK) {
                        bb_error_msg("corrupted data");
                        total = -1;
                        break;
                }
        }
+
        xz_dec_end(state);
        free(membuf);
 
similarity index 75%
rename from archival/libunarchive/filter_accept_all.c
rename to archival/libarchive/filter_accept_all.c
index 21f9c5c..c33f7d3 100644 (file)
@@ -2,11 +2,11 @@
 /*
  * Copyright (C) 2002 by Glenn McGrath
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 /* Accept any non-null name, its not really a filter at all */
 char FAST_FUNC filter_accept_all(archive_handle_t *archive_handle)
similarity index 78%
rename from archival/libunarchive/filter_accept_list.c
rename to archival/libarchive/filter_accept_list.c
index afa0b4c..a2d4b23 100644 (file)
@@ -2,11 +2,11 @@
 /*
  * Copyright (C) 2002 by Glenn McGrath
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 /*
  * Accept names that are in the accept list, ignoring reject list.
@@ -2,11 +2,11 @@
 /*
  *  Copyright (C) 2002 by Glenn McGrath
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 /* Built and used only if ENABLE_DPKG || ENABLE_DPKG_DEB */
 
@@ -2,11 +2,11 @@
 /*
  * Copyright (C) 2002 by Glenn McGrath
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 /*
  * Accept names that are in the accept list and not in the reject list
@@ -24,11 +24,13 @@ char FAST_FUNC filter_accept_reject_list(archive_handle_t *archive_handle)
        if (reject_entry) {
                return EXIT_FAILURE;
        }
-       accept_entry = find_list_entry2(archive_handle->accept, key);
 
        /* Fail if an accept list was specified and the key wasnt in there */
-       if ((accept_entry == NULL) && archive_handle->accept) {
-               return EXIT_FAILURE;
+       if (archive_handle->accept) {
+               accept_entry = find_list_entry2(archive_handle->accept, key);
+               if (!accept_entry) {
+                       return EXIT_FAILURE;
+               }
        }
 
        /* Accepted */
similarity index 91%
rename from archival/libunarchive/find_list_entry.c
rename to archival/libarchive/find_list_entry.c
index bc7bc64..56032c6 100644 (file)
@@ -2,12 +2,12 @@
 /*
  * Copyright (C) 2002 by Glenn McGrath
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include <fnmatch.h>
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 /* Find a string in a shell pattern list */
 const llist_t* FAST_FUNC find_list_entry(const llist_t *list, const char *filename)
similarity index 75%
rename from archival/libunarchive/get_header_ar.c
rename to archival/libarchive/get_header_ar.c
index dbc5ec0..c66bb3e 100644 (file)
@@ -1,18 +1,26 @@
 /* vi: set sw=4 ts=4: */
 /* Copyright 2001 Glenn McGrath.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 #include "ar.h"
 
-static unsigned read_num(const char *str, int base)
+/* WARNING: Clobbers str[len], so fields must be read in reverse order! */
+static unsigned read_num(char *str, int base, int len)
 {
+       int err;
+
+       /* ar fields are fixed length text strings (padded with spaces).
+        * Ensure bb_strtou doesn't read past the field in case the full
+        * width is used. */
+       str[len] = 0;
+
        /* This code works because
         * on misformatted numbers bb_strtou returns all-ones */
-       int err = bb_strtou(str, NULL, base);
+       err = bb_strtou(str, NULL, base);
        if (err == -1)
                bb_error_msg_and_die("invalid ar header");
        return err;
@@ -51,11 +59,13 @@ char FAST_FUNC get_header_ar(archive_handle_t *archive_handle)
        if (ar.formatted.magic[0] != '`' || ar.formatted.magic[1] != '\n')
                bb_error_msg_and_die("invalid ar header");
 
-       /* FIXME: more thorough routine would be in order here
-        * (we have something like that in tar)
-        * but for now we are lax. */
-       ar.formatted.magic[0] = '\0'; /* else 4G-2 file will have size="4294967294`\n..." */
-       typed->size = size = read_num(ar.formatted.size, 10);
+       /*
+        * Note that the fields MUST be read in reverse order as
+        * read_num() clobbers the next byte after the field!
+        * Order is: name, date, uid, gid, mode, size, magic.
+        */
+       typed->size = size = read_num(ar.formatted.size, 10,
+                                     sizeof(ar.formatted.size));
 
        /* special filenames have '/' as the first character */
        if (ar.formatted.name[0] == '/') {
@@ -87,10 +97,10 @@ char FAST_FUNC get_header_ar(archive_handle_t *archive_handle)
         * long filename pseudo file. Thus we decode the rest
         * after dealing with long filename pseudo file.
         */
-       typed->mode = read_num(ar.formatted.mode, 8);
-       typed->mtime = read_num(ar.formatted.date, 10);
-       typed->uid = read_num(ar.formatted.uid, 10);
-       typed->gid = read_num(ar.formatted.gid, 10);
+       typed->mode = read_num(ar.formatted.mode, 8, sizeof(ar.formatted.mode));
+       typed->gid = read_num(ar.formatted.gid, 10, sizeof(ar.formatted.gid));
+       typed->uid = read_num(ar.formatted.uid, 10, sizeof(ar.formatted.uid));
+       typed->mtime = read_num(ar.formatted.date, 10, sizeof(ar.formatted.date));
 
 #if ENABLE_FEATURE_AR_LONG_FILENAMES
        if (ar.formatted.name[0] == '/') {
@@ -98,7 +108,8 @@ char FAST_FUNC get_header_ar(archive_handle_t *archive_handle)
 
                /* The number after the '/' indicates the offset in the ar data section
                 * (saved in ar_long_names) that conatains the real filename */
-               long_offset = read_num(&ar.formatted.name[1], 10);
+               long_offset = read_num(&ar.formatted.name[1], 10,
+                                      sizeof(ar.formatted.name) - 1);
                if (long_offset >= ar_long_name_size) {
                        bb_error_msg_and_die("can't resolve long filename");
                }
similarity index 98%
rename from archival/libunarchive/get_header_cpio.c
rename to archival/libarchive/get_header_cpio.c
index 4507d53..1a0058b 100644 (file)
@@ -1,11 +1,11 @@
 /* vi: set sw=4 ts=4: */
 /* Copyright 2002 Laurence Anderson
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 typedef struct hardlinks_t {
        struct hardlinks_t *next;
similarity index 78%
rename from archival/libunarchive/get_header_tar.c
rename to archival/libarchive/get_header_tar.c
index d5c9235..32f8420 100644 (file)
@@ -1,23 +1,52 @@
 /* vi: set sw=4 ts=4: */
-/* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+/* Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
- *  FIXME:
+ * FIXME:
  *    In privileged mode if uname and gname map to a uid and gid then use the
  *    mapped value instead of the uid/gid values in tar header
  *
- *  References:
+ * References:
  *    GNU tar and star man pages,
  *    Opengroup's ustar interchange format,
- *     http://www.opengroup.org/onlinepubs/007904975/utilities/pax.html
+ *    http://www.opengroup.org/onlinepubs/007904975/utilities/pax.html
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 typedef uint32_t aliased_uint32_t FIX_ALIASING;
 typedef off_t    aliased_off_t    FIX_ALIASING;
 
 
+const char* FAST_FUNC strip_unsafe_prefix(const char *str)
+{
+       const char *cp = str;
+       while (1) {
+               char *cp2;
+               if (*cp == '/') {
+                       cp++;
+                       continue;
+               }
+               if (strncmp(cp, "/../"+1, 3) == 0) {
+                       cp += 3;
+                       continue;
+               }
+               cp2 = strstr(cp, "/../");
+               if (!cp2)
+                       break;
+               cp = cp2 + 4;
+       }
+       if (cp != str) {
+               static smallint warned = 0;
+               if (!warned) {
+                       warned = 1;
+                       bb_error_msg("removing leading '%.*s' from member names",
+                               (int)(cp - str), str);
+               }
+       }
+       return cp;
+}
+
 /* NB: _DESTROYS_ str[len] character! */
 static unsigned long long getOctal(char *str, int len)
 {
@@ -50,34 +79,31 @@ static unsigned long long getOctal(char *str, int len)
                 *
                 * NB: tarballs with NEGATIVE unix times encoded that way were seen!
                 */
-               v = first;
-               /* Sign-extend using 6th bit: */
-               v <<= sizeof(unsigned long long)*8 - 7;
-               v = (long long)v >> (sizeof(unsigned long long)*8 - 7);
+               /* Sign-extend 7bit 'first' to 64bit 'v' (that is, using 6th bit as sign): */
+               first <<= 1;
+               first >>= 1; /* now 7th bit = 6th bit */
+               v = first;   /* sign-extend 8 bits to 64 */
                while (--len != 0)
-                       v = (v << 8) + (unsigned char) *str++;
+                       v = (v << 8) + (uint8_t) *++str;
        }
        return v;
 }
 #define GET_OCTAL(a) getOctal((a), sizeof(a))
 
-#if ENABLE_FEATURE_TAR_SELINUX
-/* Scan a PAX header for SELinux contexts, via "RHT.security.selinux" keyword.
- * This is what Red Hat's patched version of tar uses.
- */
-# define SELINUX_CONTEXT_KEYWORD "RHT.security.selinux"
-static char *get_selinux_sctx_from_pax_hdr(archive_handle_t *archive_handle, unsigned sz)
+/* "global" is 0 or 1 */
+static void process_pax_hdr(archive_handle_t *archive_handle, unsigned sz, int global)
 {
        char *buf, *p;
-       char *result;
+       unsigned blk_sz;
+
+       blk_sz = (sz + 511) & (~511);
+       p = buf = xmalloc(blk_sz + 1);
+       xread(archive_handle->src_fd, buf, blk_sz);
+       archive_handle->offset += blk_sz;
 
-       p = buf = xmalloc(sz + 1);
        /* prevent bb_strtou from running off the buffer */
        buf[sz] = '\0';
-       xread(archive_handle->src_fd, buf, sz);
-       archive_handle->offset += sz;
 
-       result = NULL;
        while (sz != 0) {
                char *end, *value;
                unsigned len;
@@ -104,19 +130,33 @@ static char *get_selinux_sctx_from_pax_hdr(archive_handle_t *archive_handle, uns
                 * (we do not bother to check that it *was* a newline)
                 */
                p[-1] = '\0';
-               /* Is it selinux security context? */
                value = end + 1;
+
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+               if (!global && strncmp(value, "path=", sizeof("path=") - 1) == 0) {
+                       value += sizeof("path=") - 1;
+                       free(archive_handle->tar__longname);
+                       archive_handle->tar__longname = xstrdup(value);
+                       continue;
+               }
+#endif
+
+#if ENABLE_FEATURE_TAR_SELINUX
+               /* Scan for SELinux contexts, via "RHT.security.selinux" keyword.
+                * This is what Red Hat's patched version of tar uses.
+                */
+# define SELINUX_CONTEXT_KEYWORD "RHT.security.selinux"
                if (strncmp(value, SELINUX_CONTEXT_KEYWORD"=", sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1) == 0) {
                        value += sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1;
-                       result = xstrdup(value);
-                       break;
+                       free(archive_handle->tar__sctx[global]);
+                       archive_handle->tar__sctx[global] = xstrdup(value);
+                       continue;
                }
+#endif
        }
 
        free(buf);
-       return result;
 }
-#endif
 
 char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
 {
@@ -158,13 +198,13 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
         * the message and we don't check whether we indeed
         * saw zero block directly before this. */
        if (i == 0) {
-               xfunc_error_retval = 0;
- short_read:
-               bb_error_msg_and_die("short read");
+               bb_error_msg("short read");
+               /* this merely signals end of archive, not exit(1): */
+               return EXIT_FAILURE;
        }
        if (i != 512) {
                IF_FEATURE_TAR_AUTODETECT(goto autodetect;)
-               goto short_read;
+               bb_error_msg_and_die("short read");
        }
 
 #else
@@ -181,10 +221,10 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
                         */
                        while (full_read(archive_handle->src_fd, &tar, 512) == 512)
                                continue;
-                       return EXIT_FAILURE;
+                       return EXIT_FAILURE; /* "end of archive" */
                }
                archive_handle->tar__end = 1;
-               return EXIT_SUCCESS;
+               return EXIT_SUCCESS; /* "decoded one header" */
        }
        archive_handle->tar__end = 0;
 
@@ -195,43 +235,18 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
             || memcmp(tar.magic, "\0\0\0\0", 5) != 0)
        ) {
 #if ENABLE_FEATURE_TAR_AUTODETECT
-               char FAST_FUNC (*get_header_ptr)(archive_handle_t *);
-               uint16_t magic2;
-
  autodetect:
-               magic2 = *(uint16_t*)tar.name;
-               /* tar gz/bz autodetect: check for gz/bz2 magic.
-                * If we see the magic, and it is the very first block,
-                * we can switch to get_header_tar_gz/bz2/lzma().
-                * Needs seekable fd. I wish recv(MSG_PEEK) works
-                * on any fd... */
-# if ENABLE_FEATURE_SEAMLESS_GZ
-               if (magic2 == GZIP_MAGIC) {
-                       get_header_ptr = get_header_tar_gz;
-               } else
-# endif
-# if ENABLE_FEATURE_SEAMLESS_BZ2
-               if (magic2 == BZIP2_MAGIC
-                && tar.name[2] == 'h' && isdigit(tar.name[3])
-               ) { /* bzip2 */
-                       get_header_ptr = get_header_tar_bz2;
-               } else
-# endif
-# if ENABLE_FEATURE_SEAMLESS_XZ
-               //TODO: if (magic2 == XZ_MAGIC1)...
-               //else
-# endif
-                       goto err;
                /* Two different causes for lseek() != 0:
                 * unseekable fd (would like to support that too, but...),
                 * or not first block (false positive, it's not .gz/.bz2!) */
                if (lseek(archive_handle->src_fd, -i, SEEK_CUR) != 0)
                        goto err;
-               while (get_header_ptr(archive_handle) == EXIT_SUCCESS)
-                       continue;
-               return EXIT_FAILURE;
+               if (setup_unzip_on_fd(archive_handle->src_fd, /*fail_if_not_detected:*/ 0) != 0)
  err:
-#endif /* FEATURE_TAR_AUTODETECT */
+                       bb_error_msg_and_die("invalid tar magic");
+               archive_handle->offset = 0;
+               goto again_after_align;
+#endif
                bb_error_msg_and_die("invalid tar magic");
        }
 
@@ -319,10 +334,20 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
        /* Set bits 12-15 of the files mode */
        /* (typeflag was not trashed because chksum does not use getOctal) */
        switch (tar.typeflag) {
-       /* busybox identifies hard links as being regular files with 0 size and a link name */
-       case '1':
+       case '1': /* hardlink */
+               /* we mark hardlinks as regular files with zero size and a link name */
                file_header->mode |= S_IFREG;
-               break;
+               /* on size of link fields from star(4)
+                * ... For tar archives written by pre POSIX.1-1988
+                * implementations, the size field usually contains the size of
+                * the file and needs to be ignored as no data may follow this
+                * header type.  For POSIX.1- 1988 compliant archives, the size
+                * field needs to be 0.  For POSIX.1-2001 compliant archives,
+                * the size field may be non zero, indicating that file data is
+                * included in the archive.
+                * i.e; always assume this is zero for safety.
+                */
+               goto size0;
        case '7':
        /* case 0: */
        case '0':
@@ -379,12 +404,14 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
        case 'S':       /* Sparse file */
        case 'V':       /* Volume header */
 #endif
-#if !ENABLE_FEATURE_TAR_SELINUX
        case 'g':       /* pax global header */
-       case 'x':       /* pax extended header */
-#else
+       case 'x': {     /* pax extended header */
+               if ((uoff_t)file_header->size > 0xfffff) /* paranoia */
+                       goto skip_ext_hdr;
+               process_pax_hdr(archive_handle, file_header->size, (tar.typeflag == 'g'));
+               goto again_after_align;
+       }
  skip_ext_hdr:
-#endif
        {
                off_t sz;
                bb_error_msg("warning: skipping header '%c'", tar.typeflag);
@@ -396,18 +423,6 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
                /* return get_header_tar(archive_handle); */
                goto again_after_align;
        }
-#if ENABLE_FEATURE_TAR_SELINUX
-       case 'g':       /* pax global header */
-       case 'x': {     /* pax extended header */
-               char **pp;
-               if ((uoff_t)file_header->size > 0xfffff) /* paranoia */
-                       goto skip_ext_hdr;
-               pp = (tar.typeflag == 'g') ? &archive_handle->tar__global_sctx : &archive_handle->tar__next_file_sctx;
-               free(*pp);
-               *pp = get_selinux_sctx_from_pax_hdr(archive_handle, file_header->size);
-               goto again;
-       }
-#endif
        default:
                bb_error_msg_and_die("unknown typeflag: 0x%x", tar.typeflag);
        }
@@ -422,12 +437,9 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
                p_linkname = NULL;
        }
 #endif
-       if (strncmp(file_header->name, "/../"+1, 3) == 0
-        || strstr(file_header->name, "/../")
-       ) {
-               bb_error_msg_and_die("name with '..' encountered: '%s'",
-                               file_header->name);
-       }
+
+       /* Everything up to and including last ".." component is stripped */
+       overlapping_strcpy(file_header->name, strip_unsafe_prefix(file_header->name));
 
        /* Strip trailing '/' in directories */
        /* Must be done after mode is set as '/' is used to check if it's a directory */
@@ -440,9 +452,11 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
                if (cp)
                        *cp = '\0';
                archive_handle->action_data(archive_handle);
-               if (archive_handle->accept || archive_handle->reject)
+               if (archive_handle->accept || archive_handle->reject
+                || (archive_handle->ah_flags & ARCHIVE_REMEMBER_NAMES)
+               ) {
                        llist_add_to(&archive_handle->passed, file_header->name);
-               else /* Caller isn't interested in list of unpacked files */
+               else /* Caller isn't interested in list of unpacked files */
                        free(file_header->name);
        } else {
                data_skip(archive_handle);
@@ -457,5 +471,5 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
        free(file_header->tar__uname);
        free(file_header->tar__gname);
 #endif
-       return EXIT_SUCCESS;
+       return EXIT_SUCCESS; /* "decoded one header" */
 }
similarity index 65%
rename from archival/libunarchive/get_header_tar_bz2.c
rename to archival/libarchive/get_header_tar_bz2.c
index 615bbba..0ee00df 100644 (file)
@@ -1,17 +1,17 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 char FAST_FUNC get_header_tar_bz2(archive_handle_t *archive_handle)
 {
        /* Can't lseek over pipes */
        archive_handle->seek = seek_by_read;
 
-       open_transformer(archive_handle->src_fd, unpack_bz2_stream_prime, "bunzip2");
+       open_transformer_with_sig(archive_handle->src_fd, unpack_bz2_stream, "bunzip2");
        archive_handle->offset = 0;
        while (get_header_tar(archive_handle) == EXIT_SUCCESS)
                continue;
diff --git a/archival/libarchive/get_header_tar_gz.c b/archival/libarchive/get_header_tar_gz.c
new file mode 100644 (file)
index 0000000..0328434
--- /dev/null
@@ -0,0 +1,21 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "bb_archive.h"
+
+char FAST_FUNC get_header_tar_gz(archive_handle_t *archive_handle)
+{
+       /* Can't lseek over pipes */
+       archive_handle->seek = seek_by_read;
+
+       open_transformer_with_sig(archive_handle->src_fd, unpack_gz_stream, "gunzip");
+       archive_handle->offset = 0;
+       while (get_header_tar(archive_handle) == EXIT_SUCCESS)
+               continue;
+
+       /* Can only do one file at a time */
+       return EXIT_FAILURE;
+}
similarity index 72%
rename from archival/libunarchive/get_header_tar_lzma.c
rename to archival/libarchive/get_header_tar_lzma.c
index 03b1b79..d565a21 100644 (file)
@@ -3,18 +3,18 @@
  * Small lzma deflate implementation.
  * Copyright (C) 2006  Aurelien Jacobs <aurel@gnuage.org>
  *
- * Licensed under GPL v2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 char FAST_FUNC get_header_tar_lzma(archive_handle_t *archive_handle)
 {
        /* Can't lseek over pipes */
        archive_handle->seek = seek_by_read;
 
-       open_transformer(archive_handle->src_fd, unpack_lzma_stream, "unlzma");
+       open_transformer_with_sig(archive_handle->src_fd, unpack_lzma_stream, "unlzma");
        archive_handle->offset = 0;
        while (get_header_tar(archive_handle) == EXIT_SUCCESS)
                continue;
similarity index 66%
rename from archival/libunarchive/header_list.c
rename to archival/libarchive/header_list.c
index b1c425a..0621aa4 100644 (file)
@@ -1,9 +1,9 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 void FAST_FUNC header_list(const file_header_t *file_header)
 {
similarity index 55%
rename from archival/libunarchive/header_skip.c
rename to archival/libarchive/header_skip.c
index a97a9ce..f5987bf 100644 (file)
@@ -1,9 +1,9 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 void FAST_FUNC header_skip(const file_header_t *file_header UNUSED_PARAM)
 {
similarity index 93%
rename from archival/libunarchive/header_verbose_list.c
rename to archival/libarchive/header_verbose_list.c
index 3319e63..87dd821 100644 (file)
@@ -1,10 +1,10 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 void FAST_FUNC header_verbose_list(const file_header_t *file_header)
 {
similarity index 82%
rename from archival/libunarchive/init_handle.c
rename to archival/libarchive/init_handle.c
index ff7d484..cbae06a 100644 (file)
@@ -1,10 +1,10 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 archive_handle_t* FAST_FUNC init_handle(void)
 {
similarity index 98%
rename from archival/lzo1x_9x.c
rename to archival/libarchive/lzo1x_9x.c
index 0baed54..2b490ae 100644 (file)
@@ -94,7 +94,7 @@ typedef struct {
        ( ((0x9f5f * ((((b[p]<<5)^b[p+1])<<5) ^ b[p+2])) >> 5) & (SWD_HSIZE-1) )
 
 #if defined(LZO_UNALIGNED_OK_2)
-#  define HEAD2(b,p)      (* (uint16_t *) &(b[p]))
+#  define HEAD2(b,p)      (* (bb__aliased_uint16_t *) &(b[p]))
 #else
 #  define HEAD2(b,p)      (b[p] ^ ((unsigned)b[p+1]<<8))
 #endif
@@ -466,7 +466,6 @@ static int find_match(lzo1x_999_t *c, lzo_swd_p s,
        }
 
        s->m_len = 1;
-       s->m_len = 1;
 #ifdef SWD_BEST_OFF
        if (s->use_best_off)
                memset(s->best_pos, 0, sizeof(s->best_pos));
@@ -644,7 +643,7 @@ static int len_of_coded_match(unsigned m_len, unsigned m_off, unsigned lit)
 
 
 static int min_gain(unsigned ahead, unsigned lit1,
-                   unsigned lit2, int l1, int l2, int l3)
+                       unsigned lit2, int l1, int l2, int l3)
 {
        int lazy_match_min_gain = 0;
 
@@ -673,9 +672,8 @@ static int min_gain(unsigned ahead, unsigned lit1,
 #if defined(SWD_BEST_OFF)
 
 static void better_match(const lzo_swd_p swd,
-                          unsigned *m_len, unsigned *m_off)
+                       unsigned *m_len, unsigned *m_off)
 {
-
        if (*m_len <= M2_MIN_LEN)
                return;
 
@@ -915,8 +913,8 @@ int lzo1x_999_compress_level(const uint8_t *in, unsigned in_len,
 
        compression_level -= 7;
        return lzo1x_999_compress_internal(in, in_len, out, out_len, wrkmem,
-                                          c[compression_level].good_length,
-                                          c[compression_level].max_lazy,
-                                          c[compression_level].max_chain,
-                                          c[compression_level].use_best_off);
+                                       c[compression_level].good_length,
+                                       c[compression_level].max_lazy,
+                                       c[compression_level].max_chain,
+                                       c[compression_level].use_best_off);
 }
similarity index 99%
rename from archival/lzo1x_c.c
rename to archival/libarchive/lzo1x_c.c
index cc86f74..8c77072 100644 (file)
@@ -15,7 +15,7 @@
 
    The LZO 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
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
similarity index 99%
rename from archival/lzo1x_d.c
rename to archival/libarchive/lzo1x_d.c
index 348a855..9bc1270 100644 (file)
@@ -15,7 +15,7 @@
 
    The LZO 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
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
diff --git a/archival/libarchive/open_transformer.c b/archival/libarchive/open_transformer.c
new file mode 100644 (file)
index 0000000..4e98264
--- /dev/null
@@ -0,0 +1,227 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "bb_archive.h"
+
+void FAST_FUNC init_transformer_aux_data(transformer_aux_data_t *aux)
+{
+       memset(aux, 0, sizeof(*aux));
+}
+
+int FAST_FUNC check_signature16(transformer_aux_data_t *aux, int src_fd, unsigned magic16)
+{
+       if (aux && aux->check_signature) {
+               uint16_t magic2;
+               if (full_read(src_fd, &magic2, 2) != 2 || magic2 != magic16) {
+                       bb_error_msg("invalid magic");
+#if 0 /* possible future extension */
+                       if (aux->check_signature > 1)
+                               xfunc_die();
+#endif
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+void check_errors_in_children(int signo)
+{
+       int status;
+
+       if (!signo) {
+               /* block waiting for any child */
+               if (wait(&status) < 0)
+//FIXME: check EINTR?
+                       return; /* probably there are no children */
+               goto check_status;
+       }
+
+       /* Wait for any child without blocking */
+       for (;;) {
+               if (wait_any_nohang(&status) < 0)
+//FIXME: check EINTR?
+                       /* wait failed?! I'm confused... */
+                       return;
+ check_status:
+               /*if (WIFEXITED(status) && WEXITSTATUS(status) == 0)*/
+               /* On Linux, the above can be checked simply as: */
+               if (status == 0)
+                       /* this child exited with 0 */
+                       continue;
+               /* Cannot happen:
+               if (!WIFSIGNALED(status) && !WIFEXITED(status)) ???;
+                */
+               bb_got_signal = 1;
+       }
+}
+
+/* transformer(), more than meets the eye */
+#if BB_MMU
+void FAST_FUNC open_transformer(int fd,
+       int check_signature,
+       IF_DESKTOP(long long) int FAST_FUNC (*transformer)(transformer_aux_data_t *aux, int src_fd, int dst_fd)
+)
+#else
+void FAST_FUNC open_transformer(int fd, const char *transform_prog)
+#endif
+{
+       struct fd_pair fd_pipe;
+       int pid;
+
+       xpiped_pair(fd_pipe);
+       pid = BB_MMU ? xfork() : xvfork();
+       if (pid == 0) {
+               /* Child */
+               close(fd_pipe.rd); /* we don't want to read from the parent */
+               // FIXME: error check?
+#if BB_MMU
+               {
+                       IF_DESKTOP(long long) int r;
+                       transformer_aux_data_t aux;
+                       init_transformer_aux_data(&aux);
+                       aux.check_signature = check_signature;
+                       r = transformer(&aux, fd, fd_pipe.wr);
+                       if (ENABLE_FEATURE_CLEAN_UP) {
+                               close(fd_pipe.wr); /* send EOF */
+                               close(fd);
+                       }
+                       /* must be _exit! bug was actually seen here */
+                       _exit(/*error if:*/ r < 0);
+               }
+#else
+               {
+                       char *argv[4];
+                       xmove_fd(fd, 0);
+                       xmove_fd(fd_pipe.wr, 1);
+                       argv[0] = (char*)transform_prog;
+                       argv[1] = (char*)"-cf";
+                       argv[2] = (char*)"-";
+                       argv[3] = NULL;
+                       BB_EXECVP(transform_prog, argv);
+                       bb_perror_msg_and_die("can't execute '%s'", transform_prog);
+               }
+#endif
+               /* notreached */
+       }
+
+       /* parent process */
+       close(fd_pipe.wr); /* don't want to write to the child */
+       xmove_fd(fd_pipe.rd, fd);
+}
+
+
+#if SEAMLESS_COMPRESSION
+
+/* Used by e.g. rpm which gives us a fd without filename,
+ * thus we can't guess the format from filename's extension.
+ */
+int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_detected)
+{
+       union {
+               uint8_t b[4];
+               uint16_t b16[2];
+               uint32_t b32[1];
+       } magic;
+       int offset = -2;
+       USE_FOR_MMU(IF_DESKTOP(long long) int FAST_FUNC (*xformer)(transformer_aux_data_t *aux, int src_fd, int dst_fd);)
+       USE_FOR_NOMMU(const char *xformer_prog;)
+
+       /* .gz and .bz2 both have 2-byte signature, and their
+        * unpack_XXX_stream wants this header skipped. */
+       xread(fd, magic.b16, sizeof(magic.b16[0]));
+       if (ENABLE_FEATURE_SEAMLESS_GZ
+        && magic.b16[0] == GZIP_MAGIC
+       ) {
+               USE_FOR_MMU(xformer = unpack_gz_stream;)
+               USE_FOR_NOMMU(xformer_prog = "gunzip";)
+               goto found_magic;
+       }
+       if (ENABLE_FEATURE_SEAMLESS_BZ2
+        && magic.b16[0] == BZIP2_MAGIC
+       ) {
+               USE_FOR_MMU(xformer = unpack_bz2_stream;)
+               USE_FOR_NOMMU(xformer_prog = "bunzip2";)
+               goto found_magic;
+       }
+       if (ENABLE_FEATURE_SEAMLESS_XZ
+        && magic.b16[0] == XZ_MAGIC1
+       ) {
+               offset = -6;
+               xread(fd, magic.b32, sizeof(magic.b32[0]));
+               if (magic.b32[0] == XZ_MAGIC2) {
+                       USE_FOR_MMU(xformer = unpack_xz_stream;)
+                       USE_FOR_NOMMU(xformer_prog = "unxz";)
+                       goto found_magic;
+               }
+       }
+
+       /* No known magic seen */
+       if (fail_if_not_detected)
+               bb_error_msg_and_die("no gzip"
+                       IF_FEATURE_SEAMLESS_BZ2("/bzip2")
+                       IF_FEATURE_SEAMLESS_XZ("/xz")
+                       " magic");
+       xlseek(fd, offset, SEEK_CUR);
+       return 1;
+
+ found_magic:
+# if BB_MMU
+       open_transformer_with_no_sig(fd, xformer);
+# else
+       /* NOMMU version of open_transformer execs
+        * an external unzipper that wants
+        * file position at the start of the file */
+       xlseek(fd, offset, SEEK_CUR);
+       open_transformer_with_sig(fd, xformer, xformer_prog);
+# endif
+       return 0;
+}
+
+int FAST_FUNC open_zipped(const char *fname)
+{
+       char *sfx;
+       int fd;
+
+       fd = open(fname, O_RDONLY);
+       if (fd < 0)
+               return fd;
+
+       sfx = strrchr(fname, '.');
+       if (sfx) {
+               sfx++;
+               if (ENABLE_FEATURE_SEAMLESS_LZMA && strcmp(sfx, "lzma") == 0)
+                       /* .lzma has no header/signature, just trust it */
+                       open_transformer_with_sig(fd, unpack_lzma_stream, "unlzma");
+               else
+               if ((ENABLE_FEATURE_SEAMLESS_GZ && strcmp(sfx, "gz") == 0)
+                || (ENABLE_FEATURE_SEAMLESS_BZ2 && strcmp(sfx, "bz2") == 0)
+                || (ENABLE_FEATURE_SEAMLESS_XZ && strcmp(sfx, "xz") == 0)
+               ) {
+                       setup_unzip_on_fd(fd, /*fail_if_not_detected:*/ 1);
+               }
+       }
+
+       return fd;
+}
+
+#endif /* SEAMLESS_COMPRESSION */
+
+void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p)
+{
+       int fd;
+       char *image;
+
+       fd = open_zipped(fname);
+       if (fd < 0)
+               return NULL;
+
+       image = xmalloc_read(fd, maxsz_p);
+       if (!image)
+               bb_perror_msg("read error from '%s'", fname);
+       close(fd);
+
+       return image;
+}
similarity index 72%
rename from archival/libunarchive/seek_by_jump.c
rename to archival/libarchive/seek_by_jump.c
index 7181cb3..4fcd99a 100644 (file)
@@ -1,10 +1,10 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 void FAST_FUNC seek_by_jump(int fd, off_t amount)
 {
similarity index 73%
rename from archival/libunarchive/seek_by_read.c
rename to archival/libarchive/seek_by_read.c
index af65e5d..c0fde96 100644 (file)
@@ -1,10 +1,10 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 /*  If we are reading through a pipe, or from stdin then we can't lseek,
  *  we must read and discard the data to skip over it.
similarity index 79%
rename from archival/libunarchive/unpack_ar_archive.c
rename to archival/libarchive/unpack_ar_archive.c
index 300d10e..214d17e 100644 (file)
@@ -1,10 +1,10 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 #include "ar.h"
 
 void FAST_FUNC unpack_ar_archive(archive_handle_t *ar_archive)
similarity index 97%
rename from archival/libunarchive/unxz/README
rename to archival/libarchive/unxz/README
index f79b0a4..a849120 100644 (file)
@@ -7,7 +7,7 @@ XZ Embedded
 
     XZ Embedded was written for use in the Linux kernel, but the code can
     be easily used in other environments too, including regular userspace
-    applications.
+    applications. See userspace/xzminidec.c for an example program.
 
     This README contains information that is useful only when the copy
     of XZ Embedded isn't part of the Linux kernel tree. You should also
@@ -55,7 +55,7 @@ Compiler requirements
     code is modified not to support large files, which needs some more
     care than just using 32-bit integer instead of 64-bit).
 
-    If you use GCC, try to use a recent version. For example, on x86,
+    If you use GCC, try to use a recent version. For example, on x86-32,
     xz_dec_lzma2.c compiled with GCC 3.3.6 is 15-25 % slower than when
     compiled with GCC 4.3.3.
 
@@ -93,7 +93,7 @@ BCJ filter support
     them always #defined doesn't hurt either.
 
         #define             Instruction set     BCJ filter endianness
-        XZ_DEC_X86          x86 or x86-64       Little endian only
+        XZ_DEC_X86          x86-32 or x86-64    Little endian only
         XZ_DEC_POWERPC      PowerPC             Big endian only
         XZ_DEC_IA64         Itanium (IA-64)     Big or little endian
         XZ_DEC_ARM          ARM                 Little endian only
@@ -133,4 +133,3 @@ Specifying the calling convention
     For example, on Windows, you may make all functions use the stdcall
     calling convention by defining XZ_FUNC=__stdcall when building and
     using the functions from XZ Embedded.
-
similarity index 96%
rename from archival/libunarchive/unxz/xz.h
rename to archival/libarchive/unxz/xz.h
index c6c071c..e0b22db 100644 (file)
 #      include <stdint.h>
 #endif
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /* In Linux, this is used to make extern functions static when needed. */
 #ifndef XZ_EXTERN
 #      define XZ_EXTERN extern
@@ -70,7 +74,7 @@ enum xz_mode {
  * @XZ_UNSUPPORTED_CHECK:   Integrity check type is not supported. Decoding
  *                          is still possible in multi-call mode by simply
  *                          calling xz_dec_run() again.
- *                          NOTE: This return value is used only if
+ *                          Note that this return value is used only if
  *                          XZ_DEC_ANY_CHECK was defined at build time,
  *                          which is not used in the kernel. Unsupported
  *                          check types return XZ_OPTIONS_ERROR if
@@ -105,7 +109,7 @@ enum xz_mode {
  * stream that is truncated or otherwise corrupt.
  *
  * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer
- * is too small, or the compressed input is corrupt in a way that makes the
+ * is too small or the compressed input is corrupt in a way that makes the
  * decoder produce more output than the caller expected. When it is
  * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR
  * is used instead of XZ_BUF_ERROR.
@@ -207,8 +211,8 @@ XZ_EXTERN struct xz_dec * XZ_FUNC xz_dec_init(
  * The possible return values depend on build options and operation mode.
  * See enum xz_ret for details.
  *
- * NOTE: If an error occurs in single-call mode (return value is not
- * XZ_STREAM_END), b->in_pos and b->out_pos are not modified, and the
+ * Note that if an error occurs in single-call mode (return value is not
+ * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the
  * contents of the output buffer from b->out[b->out_pos] onward are
  * undefined. This is true even after XZ_BUF_ERROR, because with some filter
  * chains, there may be a second pass over the output buffer, and this pass
@@ -268,4 +272,9 @@ XZ_EXTERN void XZ_FUNC xz_crc32_init(void);
 XZ_EXTERN uint32_t XZ_FUNC xz_crc32(
                const uint8_t *buf, size_t size, uint32_t crc);
 #endif
+
+#ifdef __cplusplus
+}
+#endif
+
 #endif
similarity index 92%
rename from archival/libunarchive/unxz/xz_dec_bcj.c
rename to archival/libarchive/unxz/xz_dec_bcj.c
index 09162b5..e0f913a 100644 (file)
@@ -77,10 +77,13 @@ struct xz_dec_bcj {
 
 #ifdef XZ_DEC_X86
 /*
- * This is macro used to test the most significant byte of a memory address
+ * This is used to test the most significant byte of a memory address
  * in an x86 instruction.
  */
-#define bcj_x86_test_msbyte(b) ((b) == 0x00 || (b) == 0xFF)
+static inline int bcj_x86_test_msbyte(uint8_t b)
+{
+       return b == 0x00 || b == 0xFF;
+}
 
 static noinline_for_stack size_t XZ_FUNC bcj_x86(
                struct xz_dec_bcj *s, uint8_t *buf, size_t size)
@@ -443,8 +446,12 @@ XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_run(struct xz_dec_bcj *s,
         * next filter in the chain. Apply the BCJ filter on the new data
         * in the output buffer. If everything cannot be filtered, copy it
         * to temp and rewind the output buffer position accordingly.
+        *
+        * This needs to be always run when temp.size == 0 to handle a special
+        * case where the output buffer is full and the next filter has no
+        * more output coming but hasn't returned XZ_STREAM_END yet.
         */
-       if (s->temp.size < b->out_size - b->out_pos) {
+       if (s->temp.size < b->out_size - b->out_pos || s->temp.size == 0) {
                out_start = b->out_pos;
                memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size);
                b->out_pos += s->temp.size;
@@ -467,16 +474,25 @@ XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_run(struct xz_dec_bcj *s,
                s->temp.size = b->out_pos - out_start;
                b->out_pos -= s->temp.size;
                memcpy(s->temp.buf, b->out + b->out_pos, s->temp.size);
+
+               /*
+                * If there wasn't enough input to the next filter to fill
+                * the output buffer with unfiltered data, there's no point
+                * to try decoding more data to temp.
+                */
+               if (b->out_pos + s->temp.size < b->out_size)
+                       return XZ_OK;
        }
 
        /*
-        * If we have unfiltered data in temp, try to fill by decoding more
-        * data from the next filter. Apply the BCJ filter on temp. Then we
-        * hopefully can fill the actual output buffer by copying filtered
-        * data from temp. A mix of filtered and unfiltered data may be left
-        * in temp; it will be taken care on the next call to this function.
+        * We have unfiltered data in temp. If the output buffer isn't full
+        * yet, try to fill the temp buffer by decoding more data from the
+        * next filter. Apply the BCJ filter on temp. Then we hopefully can
+        * fill the actual output buffer by copying filtered data from temp.
+        * A mix of filtered and unfiltered data may be left in temp; it will
+        * be taken care on the next call to this function.
         */
-       if (s->temp.size > 0) {
+       if (b->out_pos < b->out_size) {
                /* Make b->out{,_pos,_size} temporarily point to s->temp. */
                s->out = b->out;
                s->out_pos = b->out_pos;
similarity index 99%
rename from archival/libunarchive/unxz/xz_dec_lzma2.c
rename to archival/libarchive/unxz/xz_dec_lzma2.c
index da71cb4..3c2dc88 100644 (file)
@@ -407,7 +407,6 @@ static void XZ_FUNC dict_uncompressed(
 
                b->out_pos += copy_size;
                b->in_pos += copy_size;
-
        }
 }
 
@@ -972,6 +971,9 @@ XZ_EXTERN NOINLINE enum xz_ret XZ_FUNC xz_dec_lzma2_run(
                         */
                        tmp = b->in[b->in_pos++];
 
+                       if (tmp == 0x00)
+                               return XZ_STREAM_END;
+
                        if (tmp >= 0xE0 || tmp == 0x01) {
                                s->lzma2.need_props = true;
                                s->lzma2.need_dict_reset = false;
@@ -1004,9 +1006,6 @@ XZ_EXTERN NOINLINE enum xz_ret XZ_FUNC xz_dec_lzma2_run(
                                                lzma_reset(s);
                                }
                        } else {
-                               if (tmp == 0x00)
-                                       return XZ_STREAM_END;
-
                                if (tmp > 0x02)
                                        return XZ_DATA_ERROR;
 
similarity index 72%
rename from archival/libunarchive/unxz/xz_stream.h
rename to archival/libarchive/unxz/xz_stream.h
index 36f2a7c..66cb5a7 100644 (file)
 
 #define STREAM_HEADER_SIZE 12
 
-#define HEADER_MAGIC "\3757zXZ\0"
+#define HEADER_MAGIC "\3757zXZ"
 #define HEADER_MAGIC_SIZE 6
 
 #define FOOTER_MAGIC "YZ"
 #define FOOTER_MAGIC_SIZE 2
 
 /*
- * Variable-length integer can hold a 63-bit unsigned integer, or a special
- * value to indicate that the value is unknown.
+ * Variable-length integer can hold a 63-bit unsigned integer or a special
+ * value indicating that the value is unknown.
+ *
+ * Experimental: vli_type can be defined to uint32_t to save a few bytes
+ * in code size (no effect on speed). Doing so limits the uncompressed and
+ * compressed size of the file to less than 256 MiB and may also weaken
+ * error detection slightly.
  */
 typedef uint64_t vli_type;
 
diff --git a/archival/libunarchive/Kbuild.src b/archival/libunarchive/Kbuild.src
deleted file mode 100644 (file)
index a854957..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
-#
-# Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
-
-lib-y:=
-
-COMMON_FILES:= \
-\
-       data_skip.o \
-       data_extract_all.o \
-       data_extract_to_stdout.o \
-\
-       filter_accept_all.o \
-       filter_accept_list.o \
-       filter_accept_reject_list.o \
-\
-       header_skip.o \
-       header_list.o \
-       header_verbose_list.o \
-\
-       seek_by_read.o \
-       seek_by_jump.o \
-\
-       data_align.o \
-       find_list_entry.o \
-       init_handle.o
-
-DPKG_FILES:= \
-       get_header_ar.o \
-       unpack_ar_archive.o \
-       get_header_tar.o \
-       filter_accept_list_reassign.o
-
-INSERT
-
-lib-$(CONFIG_AR)                        += get_header_ar.o unpack_ar_archive.o
-lib-$(CONFIG_BUNZIP2)                   += decompress_bunzip2.o
-lib-$(CONFIG_UNLZMA)                    += decompress_unlzma.o
-lib-$(CONFIG_UNXZ)                      += decompress_unxz.o
-lib-$(CONFIG_CPIO)                      += get_header_cpio.o
-lib-$(CONFIG_DPKG)                      += $(DPKG_FILES)
-lib-$(CONFIG_DPKG_DEB)                  += $(DPKG_FILES)
-lib-$(CONFIG_GUNZIP)                    += decompress_unzip.o
-lib-$(CONFIG_RPM2CPIO)                  += decompress_unzip.o get_header_cpio.o
-lib-$(CONFIG_RPM)                       += open_transformer.o decompress_unzip.o get_header_cpio.o
-lib-$(CONFIG_TAR)                       += get_header_tar.o
-lib-$(CONFIG_UNCOMPRESS)                += decompress_uncompress.o
-lib-$(CONFIG_UNZIP)                     += decompress_unzip.o
-lib-$(CONFIG_FEATURE_SEAMLESS_Z)        += open_transformer.o decompress_uncompress.o
-lib-$(CONFIG_FEATURE_SEAMLESS_GZ)       += open_transformer.o decompress_unzip.o get_header_tar_gz.o
-lib-$(CONFIG_FEATURE_SEAMLESS_BZ2)      += open_transformer.o decompress_bunzip2.o get_header_tar_bz2.o
-lib-$(CONFIG_FEATURE_SEAMLESS_LZMA)     += open_transformer.o decompress_unlzma.o get_header_tar_lzma.o
-lib-$(CONFIG_FEATURE_SEAMLESS_XZ)       += open_transformer.o decompress_unxz.o
-lib-$(CONFIG_FEATURE_COMPRESS_USAGE)    += decompress_bunzip2.o
-lib-$(CONFIG_FEATURE_TAR_TO_COMMAND)    += data_extract_to_command.o
-
-ifneq ($(lib-y),)
-lib-y += $(COMMON_FILES)
-endif
diff --git a/archival/libunarchive/get_header_tar_gz.c b/archival/libunarchive/get_header_tar_gz.c
deleted file mode 100644 (file)
index e88b720..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
- */
-
-#include "libbb.h"
-#include "unarchive.h"
-
-char FAST_FUNC get_header_tar_gz(archive_handle_t *archive_handle)
-{
-#if BB_MMU
-       unsigned char magic[2];
-#endif
-
-       /* Can't lseek over pipes */
-       archive_handle->seek = seek_by_read;
-
-       /* Check gzip magic only if open_transformer will invoke unpack_gz_stream (MMU case).
-        * Otherwise, it will invoke an external helper "gunzip -cf" (NOMMU case) which will
-        * need the header. */
-#if BB_MMU
-       xread(archive_handle->src_fd, &magic, 2);
-       /* Can skip this check, but error message will be less clear */
-       if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
-               bb_error_msg_and_die("invalid gzip magic");
-       }
-#endif
-
-       open_transformer(archive_handle->src_fd, unpack_gz_stream, "gunzip");
-       archive_handle->offset = 0;
-       while (get_header_tar(archive_handle) == EXIT_SUCCESS)
-               continue;
-
-       /* Can only do one file at a time */
-       return EXIT_FAILURE;
-}
diff --git a/archival/libunarchive/open_transformer.c b/archival/libunarchive/open_transformer.c
deleted file mode 100644 (file)
index cba049f..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
- */
-
-#include "libbb.h"
-#include "unarchive.h"
-
-/* transformer(), more than meets the eye */
-/*
- * On MMU machine, the transform_prog is removed by macro magic
- * in include/unarchive.h. On NOMMU, transformer is removed.
- */
-void FAST_FUNC open_transformer(int fd,
-       IF_DESKTOP(long long) int FAST_FUNC (*transformer)(int src_fd, int dst_fd),
-       const char *transform_prog)
-{
-       struct fd_pair fd_pipe;
-       int pid;
-
-       xpiped_pair(fd_pipe);
-       pid = BB_MMU ? xfork() : xvfork();
-       if (pid == 0) {
-               /* Child */
-               close(fd_pipe.rd); /* we don't want to read from the parent */
-               // FIXME: error check?
-#if BB_MMU
-               transformer(fd, fd_pipe.wr);
-               if (ENABLE_FEATURE_CLEAN_UP) {
-                       close(fd_pipe.wr); /* send EOF */
-                       close(fd);
-               }
-               /* must be _exit! bug was actually seen here */
-               _exit(EXIT_SUCCESS);
-#else
-               {
-                       char *argv[4];
-                       xmove_fd(fd, 0);
-                       xmove_fd(fd_pipe.wr, 1);
-                       argv[0] = (char*)transform_prog;
-                       argv[1] = (char*)"-cf";
-                       argv[2] = (char*)"-";
-                       argv[3] = NULL;
-                       BB_EXECVP(transform_prog, argv);
-                       bb_perror_msg_and_die("can't execute '%s'", transform_prog);
-               }
-#endif
-               /* notreached */
-       }
-
-       /* parent process */
-       close(fd_pipe.wr); /* don't want to write to the child */
-       xmove_fd(fd_pipe.rd, fd);
-}
index ab4d34c..5062d93 100644 (file)
@@ -14,7 +14,7 @@
 
    This program 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
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    "Minimalized" for busybox by Alain Knaff
 */
 
+//config:config LZOP
+//config:      bool "lzop"
+//config:      default y
+//config:      help
+//config:        Lzop compression/decompresion.
+//config:
+//config:config LZOP_COMPR_HIGH
+//config:      bool "lzop compression levels 7,8,9 (not very useful)"
+//config:      default n
+//config:      depends on LZOP
+//config:      help
+//config:        High levels (7,8,9) of lzop compression. These levels
+//config:        are actually slower than gzip at equivalent compression ratios
+//config:        and take up 3.2K of code.
+
+//applet:IF_LZOP(APPLET(lzop, BB_DIR_BIN, BB_SUID_DROP))
+//applet:IF_LZOP(APPLET_ODDNAME(lzopcat, lzop, BB_DIR_USR_BIN, BB_SUID_DROP, lzopcat))
+//applet:IF_LZOP(APPLET_ODDNAME(unlzop, lzop, BB_DIR_USR_BIN, BB_SUID_DROP, unlzop))
+//kbuild:lib-$(CONFIG_LZOP) += lzop.o
+
+//usage:#define lzop_trivial_usage
+//usage:       "[-cfvd123456789CF] [FILE]..."
+//usage:#define lzop_full_usage "\n\n"
+//usage:       "       -1..9   Compression level"
+//usage:     "\n       -d      Decompress"
+//usage:     "\n       -c      Write to stdout"
+//usage:     "\n       -f      Force"
+//usage:     "\n       -v      Verbose"
+//usage:     "\n       -F      Don't store or verify checksum"
+//usage:     "\n       -C      Also write checksum of compressed block"
+//usage:
+//usage:#define lzopcat_trivial_usage
+//usage:       "[-vCF] [FILE]..."
+//usage:#define lzopcat_full_usage "\n\n"
+//usage:       "       -v      Verbose"
+//usage:     "\n       -F      Don't store or verify checksum"
+//usage:
+//usage:#define unlzop_trivial_usage
+//usage:       "[-cfvCF] [FILE]..."
+//usage:#define unlzop_full_usage "\n\n"
+//usage:       "       -c      Write to stdout"
+//usage:     "\n       -f      Force"
+//usage:     "\n       -v      Verbose"
+//usage:     "\n       -F      Don't store or verify checksum"
+
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 #include "liblzo_interface.h"
 
 /* lzo-2.03/src/lzo_ptr.h */
@@ -91,7 +136,7 @@ static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
        unsigned nl;
        unsigned long o_m1_a = 0, o_m1_b = 0, o_m2 = 0, o_m3_a = 0, o_m3_b = 0;
 
-//       LZO_UNUSED(wrkmem);
+//     LZO_UNUSED(wrkmem);
 
        *out_len = 0;
 
@@ -176,7 +221,7 @@ static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
                                        /* remove short run */
                                        *litp &= ~3;
                                        /* copy over the 2 literals that replace the match */
-                                       copy2(ip-3+1,m_pos,pd(op,m_pos));
+                                       copy2(ip-3+1, m_pos, pd(op, m_pos));
                                        /* move literals 1 byte ahead */
                                        litp += 2;
                                        if (lit > 0)
@@ -186,7 +231,8 @@ static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
                                        *litp = (unsigned char)(lit - 3);
 
                                        o_m1_b++;
-                                       *op++ = *m_pos++; *op++ = *m_pos++;
+                                       *op++ = *m_pos++;
+                                       *op++ = *m_pos++;
                                        goto copy_literal_run;
                                }
  copy_m1:
@@ -215,7 +261,7 @@ static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
                                        ) {
                                                t = *ip++;
                                                /* copy over the 3 literals that replace the match */
-                                               copy3(ip-1-2,m_pos,pd(op,m_pos));
+                                               copy3(ip-1-2, m_pos, pd(op, m_pos));
                                                /* set new length of previous literal run */
                                                lit += 3 + t + 3;
                                                *litp = (unsigned char)(lit - 3);
@@ -264,7 +310,7 @@ static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
                                                lit += 3;
                                                *litp = (unsigned char)((*litp & ~3) | lit);
                                                /* copy over the 3 literals that replace the match */
-                                               copy3(ip-3,m_pos,pd(op,m_pos));
+                                               copy3(ip-3, m_pos, pd(op, m_pos));
                                                o_m3_a++;
                                        }
                                        /* test if a literal run follows */
@@ -275,7 +321,7 @@ static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
                                                /* remove short run */
                                                *litp &= ~3;
                                                /* copy over the 3 literals that replace the match */
-                                               copy3(ip-4+1,m_pos,pd(op,m_pos));
+                                               copy3(ip-4+1, m_pos, pd(op, m_pos));
                                                /* move literals 1 byte ahead */
                                                litp += 2;
                                                if (lit > 0)
@@ -320,11 +366,11 @@ static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
        return LZO_E_EOF_NOT_FOUND;
 
  eof_found:
-//       LZO_UNUSED(o_m1_a); LZO_UNUSED(o_m1_b); LZO_UNUSED(o_m2);
-//       LZO_UNUSED(o_m3_a); LZO_UNUSED(o_m3_b);
+//     LZO_UNUSED(o_m1_a); LZO_UNUSED(o_m1_b); LZO_UNUSED(o_m2);
+//     LZO_UNUSED(o_m3_a); LZO_UNUSED(o_m3_b);
        *out_len = pd(op, out);
        return (ip == ip_end ? LZO_E_OK :
-                  (ip < ip_end  ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
+               (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
 }
 
 /**********************************************************************/
@@ -393,7 +439,7 @@ typedef struct header_t {
 } header_t;
 
 struct globals {
-       const uint32_t *lzo_crc32_table;
+       /*const uint32_t *lzo_crc32_table;*/
        chksum_t chksum_in;
        chksum_t chksum_out;
 } FIX_ALIASING;
@@ -401,7 +447,7 @@ struct globals {
 #define INIT_G() do { } while (0)
 //#define G (*ptr_to_globals)
 //#define INIT_G() do {
-//        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G)));
+//     SET_PTR_TO_GLOBALS(xzalloc(sizeof(G)));
 //} while (0)
 
 
@@ -410,25 +456,27 @@ struct globals {
 //#define LZOP_VERSION_STRING     "1.01"
 //#define LZOP_VERSION_DATE       "Apr 27th 2003"
 
-#define OPTION_STRING "cfvdt123456789CF"
+#define OPTION_STRING "cfvqdt123456789CF"
 
+/* Note: must be kept in sync with archival/bbunzip.c */
 enum {
        OPT_STDOUT      = (1 << 0),
        OPT_FORCE       = (1 << 1),
        OPT_VERBOSE     = (1 << 2),
-       OPT_DECOMPRESS  = (1 << 3),
-       OPT_TEST        = (1 << 4),
-       OPT_1           = (1 << 5),
-       OPT_2           = (1 << 6),
-       OPT_3           = (1 << 7),
-       OPT_4           = (1 << 8),
-       OPT_5           = (1 << 9),
-       OPT_6           = (1 << 10),
-       OPT_789         = (7 << 11),
-       OPT_7           = (1 << 11),
-       OPT_8           = (1 << 12),
-       OPT_C           = (1 << 14),
-       OPT_F           = (1 << 15),
+       OPT_QUIET       = (1 << 3),
+       OPT_DECOMPRESS  = (1 << 4),
+       OPT_TEST        = (1 << 5),
+       OPT_1           = (1 << 6),
+       OPT_2           = (1 << 7),
+       OPT_3           = (1 << 8),
+       OPT_4           = (1 << 9),
+       OPT_5           = (1 << 10),
+       OPT_6           = (1 << 11),
+       OPT_789         = (7 << 12),
+       OPT_7           = (1 << 13),
+       OPT_8           = (1 << 14),
+       OPT_C           = (1 << 15),
+       OPT_F           = (1 << 16),
 };
 
 /**********************************************************************/
@@ -468,19 +516,10 @@ lzo_adler32(uint32_t adler, const uint8_t* buf, unsigned len)
 static FAST_FUNC uint32_t
 lzo_crc32(uint32_t c, const uint8_t* buf, unsigned len)
 {
-       uint32_t crc;
-
-       if (buf == NULL)
-               return 0;
+       //if (buf == NULL) - impossible
+       //      return 0;
 
-       crc = ~c;
-       if (len != 0) do {
-               crc = G.lzo_crc32_table[(uint8_t)((int)crc ^ *buf)] ^ (crc >> 8);
-               buf += 1;
-               len -= 1;
-       } while (len > 0);
-
-       return ~crc;
+       return ~crc32_block_endian0(~c, buf, len, global_crc32_table);
 }
 
 /**********************************************************************/
@@ -679,8 +718,7 @@ static NOINLINE smallint lzo_compress(const header_t *h)
                if (dst_len < src_len) {
                        /* write checksum of compressed block */
                        if (h->flags & F_ADLER32_C)
-                               write32(lzo_adler32(ADLER32_INIT_VALUE, b2,
-                                                       dst_len));
+                               write32(lzo_adler32(ADLER32_INIT_VALUE, b2, dst_len));
                        if (h->flags & F_CRC32_C)
                                write32(lzo_crc32(CRC32_INIT_VALUE, b2, dst_len));
                        /* write compressed block data */
@@ -697,10 +735,16 @@ static NOINLINE smallint lzo_compress(const header_t *h)
        return ok;
 }
 
-static void lzo_check(uint32_t FAST_FUNC (*fn)(uint32_t, const uint8_t*, unsigned),
-               uint32_t ref, uint32_t init,
-               uint8_t* buf, unsigned len)
+static FAST_FUNC void lzo_check(
+               uint32_t init,
+               uint8_t* buf, unsigned len,
+               uint32_t FAST_FUNC (*fn)(uint32_t, const uint8_t*, unsigned),
+               uint32_t ref)
 {
+       /* This function, by having the same order of parameters
+        * as fn, and by being marked FAST_FUNC (same as fn),
+        * saves a dozen bytes of code.
+        */
        uint32_t c = fn(init, buf, len);
        if (c != ref)
                bb_error_msg_and_die("checksum error");
@@ -747,9 +791,8 @@ static NOINLINE smallint lzo_decompress(const header_t *h)
 
                if (dst_len > block_size) {
                        if (b2) {
-//FIXME!
-                               b2 = NULL;
                                free(b2);
+                               b2 = NULL;
                        }
                        block_size = dst_len;
                        mcs_block_size = MAX_COMPRESSED_SIZE(block_size);
@@ -781,13 +824,13 @@ static NOINLINE smallint lzo_decompress(const header_t *h)
                        if (!(option_mask32 & OPT_F)) {
                                /* verify checksum of compressed block */
                                if (h->flags & F_ADLER32_C)
-                                       lzo_check(lzo_adler32, c_adler32,
-                                                       ADLER32_INIT_VALUE,
-                                                       b1, src_len);
+                                       lzo_check(ADLER32_INIT_VALUE,
+                                                       b1, src_len,
+                                                       lzo_adler32, c_adler32);
                                if (h->flags & F_CRC32_C)
-                                       lzo_check(lzo_crc32, c_crc32,
-                                                       CRC32_INIT_VALUE,
-                                                       b1, src_len);
+                                       lzo_check(CRC32_INIT_VALUE,
+                                                       b1, src_len,
+                                                       lzo_crc32, c_crc32);
                        }
 
                        /* decompress */
@@ -808,11 +851,13 @@ static NOINLINE smallint lzo_decompress(const header_t *h)
                if (!(option_mask32 & OPT_F)) {
                        /* verify checksum of uncompressed block */
                        if (h->flags & F_ADLER32_D)
-                               lzo_check(lzo_adler32, d_adler32, ADLER32_INIT_VALUE,
-                                         dst, dst_len);
+                               lzo_check(ADLER32_INIT_VALUE,
+                                       dst, dst_len,
+                                       lzo_adler32, d_adler32);
                        if (h->flags & F_CRC32_D)
-                               lzo_check(lzo_crc32, d_crc32, CRC32_INIT_VALUE,
-                                         dst, dst_len);
+                               lzo_check(CRC32_INIT_VALUE,
+                                       dst, dst_len,
+                                       lzo_crc32, d_crc32);
                }
 
                /* write uncompressed block data */
@@ -1054,7 +1099,7 @@ static char* FAST_FUNC make_new_name_lzop(char *filename, const char *expected_e
        return xasprintf("%s.lzo", filename);
 }
 
-static IF_DESKTOP(long long) int FAST_FUNC pack_lzop(unpack_info_t *info UNUSED_PARAM)
+static IF_DESKTOP(long long) int FAST_FUNC pack_lzop(transformer_aux_data_t *aux UNUSED_PARAM)
 {
        if (option_mask32 & OPT_DECOMPRESS)
                return do_lzo_decompress();
@@ -1070,9 +1115,9 @@ int lzop_main(int argc UNUSED_PARAM, char **argv)
        if (applet_name[4] == 'c')
                option_mask32 |= (OPT_STDOUT | OPT_DECOMPRESS);
        /* unlzop? */
-       if (applet_name[0] == 'u')
+       if (applet_name[4] == 'o')
                option_mask32 |= OPT_DECOMPRESS;
 
-       G.lzo_crc32_table = crc32_filltable(NULL, 0);
+       global_crc32_table = crc32_filltable(NULL, 0);
        return bbunpack(argv, pack_lzop, make_new_name_lzop, /*unused:*/ NULL);
 }
index 38ec20e..885eddd 100644 (file)
@@ -4,11 +4,32 @@
  *
  * Copyright (C) 2001,2002 by Laurence Anderson
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//config:config RPM
+//config:      bool "rpm"
+//config:      default y
+//config:      help
+//config:        Mini RPM applet - queries and extracts RPM packages.
+
+//applet:IF_RPM(APPLET(rpm, BB_DIR_BIN, BB_SUID_DROP))
+//kbuild:lib-$(CONFIG_RPM) += rpm.o
+
+//usage:#define rpm_trivial_usage
+//usage:       "-i PACKAGE.rpm; rpm -qp[ildc] PACKAGE.rpm"
+//usage:#define rpm_full_usage "\n\n"
+//usage:       "Manipulate RPM packages\n"
+//usage:     "\nCommands:"
+//usage:     "\n       -i      Install package"
+//usage:     "\n       -qp     Query package"
+//usage:     "\n       -qpi    Show information"
+//usage:     "\n       -qpl    List contents"
+//usage:     "\n       -qpd    List documents"
+//usage:     "\n       -qpc    List config files"
+
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 #include "rpm.h"
 
 #define RPM_CHAR_TYPE           1
@@ -67,136 +88,13 @@ typedef struct {
        uint32_t count; /* 4 byte count */
 } rpm_index;
 
-static void *map;
-static rpm_index **mytags;
-static int tagcount;
-
-static void extract_cpio(int fd, const char *source_rpm);
-static rpm_index **rpm_gettags(int fd, int *num_tags);
-static int bsearch_rpmtag(const void *key, const void *item);
-static char *rpm_getstr(int tag, int itemindex);
-static int rpm_getint(int tag, int itemindex);
-static int rpm_getcount(int tag);
-static void fileaction_dobackup(char *filename, int fileref);
-static void fileaction_setowngrp(char *filename, int fileref);
-static void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref));
-
-int rpm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int rpm_main(int argc, char **argv)
-{
-       int opt = 0, func = 0, rpm_fd, offset;
-       const int pagesize = getpagesize();
-
-       while ((opt = getopt(argc, argv, "iqpldc")) != -1) {
-               switch (opt) {
-               case 'i': /* First arg: Install mode, with q: Information */
-                       if (!func) func = rpm_install;
-                       else func |= rpm_query_info;
-                       break;
-               case 'q': /* First arg: Query mode */
-                       if (func) bb_show_usage();
-                       func = rpm_query;
-                       break;
-               case 'p': /* Query a package */
-                       func |= rpm_query_package;
-                       break;
-               case 'l': /* List files in a package */
-                       func |= rpm_query_list;
-                       break;
-               case 'd': /* List doc files in a package (implies list) */
-                       func |= rpm_query_list;
-                       func |= rpm_query_list_doc;
-                       break;
-               case 'c': /* List config files in a package (implies list) */
-                       func |= rpm_query_list;
-                       func |= rpm_query_list_config;
-                       break;
-               default:
-                       bb_show_usage();
-               }
-       }
-       argv += optind;
-       //argc -= optind;
-       if (!argv[0]) {
-               bb_show_usage();
-       }
-
-       while (*argv) {
-               const char *source_rpm;
-
-               rpm_fd = xopen(*argv++, O_RDONLY);
-               mytags = rpm_gettags(rpm_fd, &tagcount);
-               if (!mytags)
-                       bb_error_msg_and_die("error reading rpm header");
-               offset = xlseek(rpm_fd, 0, SEEK_CUR);
-               /* Mimimum is one page */
-               map = mmap(0, offset > pagesize ? (offset + offset % pagesize) : pagesize, PROT_READ, MAP_PRIVATE, rpm_fd, 0);
-
-               source_rpm = rpm_getstr(TAG_SOURCERPM, 0);
-
-               if (func & rpm_install) {
-                       /* Backup any config files */
-                       loop_through_files(TAG_BASENAMES, fileaction_dobackup);
-                       /* Extact the archive */
-                       extract_cpio(rpm_fd, source_rpm);
-                       /* Set the correct file uid/gid's */
-                       loop_through_files(TAG_BASENAMES, fileaction_setowngrp);
-               }
-               else if ((func & (rpm_query|rpm_query_package)) == (rpm_query|rpm_query_package)) {
-                       if (!(func & (rpm_query_info|rpm_query_list))) {
-                               /* If just a straight query, just give package name */
-                               printf("%s-%s-%s\n", rpm_getstr(TAG_NAME, 0), rpm_getstr(TAG_VERSION, 0), rpm_getstr(TAG_RELEASE, 0));
-                       }
-                       if (func & rpm_query_info) {
-                               /* Do the nice printout */
-                               time_t bdate_time;
-                               struct tm *bdate_ptm;
-                               char bdatestring[50];
-                               const char *p;
-
-                               p = rpm_getstr(TAG_PREFIXS, 0);
-                               if (!p) p = "(not relocateable)";
-                               printf("Name        : %-29sRelocations: %s\n", rpm_getstr(TAG_NAME, 0), p);
-                               p = rpm_getstr(TAG_VENDOR, 0);
-                               if (!p) p = "(none)";
-                               printf("Version     : %-34sVendor: %s\n", rpm_getstr(TAG_VERSION, 0), p);
-                               bdate_time = rpm_getint(TAG_BUILDTIME, 0);
-                               bdate_ptm = localtime(&bdate_time);
-                               strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate_ptm);
-                               printf("Release     : %-30sBuild Date: %s\n", rpm_getstr(TAG_RELEASE, 0), bdatestring);
-                               printf("Install date: %-30sBuild Host: %s\n", "(not installed)", rpm_getstr(TAG_BUILDHOST, 0));
-                               printf("Group       : %-30sSource RPM: %s\n", rpm_getstr(TAG_GROUP, 0), source_rpm);
-                               printf("Size        : %-33dLicense: %s\n", rpm_getint(TAG_SIZE, 0), rpm_getstr(TAG_LICENSE, 0));
-                               printf("URL         : %s\n", rpm_getstr(TAG_URL, 0));
-                               printf("Summary     : %s\n", rpm_getstr(TAG_SUMMARY, 0));
-                               printf("Description :\n%s\n", rpm_getstr(TAG_DESCRIPTION, 0));
-                       }
-                       if (func & rpm_query_list) {
-                               int count, it, flags;
-                               count = rpm_getcount(TAG_BASENAMES);
-                               for (it = 0; it < count; it++) {
-                                       flags = rpm_getint(TAG_FILEFLAGS, it);
-                                       switch (func & (rpm_query_list_doc|rpm_query_list_config)) {
-                                       case rpm_query_list_doc:
-                                               if (!(flags & RPMFILE_DOC)) continue;
-                                               break;
-                                       case rpm_query_list_config:
-                                               if (!(flags & RPMFILE_CONFIG)) continue;
-                                               break;
-                                       case rpm_query_list_doc|rpm_query_list_config:
-                                               if (!(flags & (RPMFILE_CONFIG|RPMFILE_DOC))) continue;
-                                               break;
-                                       }
-                                       printf("%s%s\n",
-                                               rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, it)),
-                                               rpm_getstr(TAG_BASENAMES, it));
-                               }
-                       }
-               }
-               free(mytags);
-       }
-       return 0;
-}
+struct globals {
+       void *map;
+       rpm_index **mytags;
+       int tagcount;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { } while (0)
 
 static void extract_cpio(int fd, const char *source_rpm)
 {
@@ -219,12 +117,12 @@ static void extract_cpio(int fd, const char *source_rpm)
                /* compat: overwrite existing files.
                 * try "rpm -i foo.src.rpm" few times in a row -
                 * standard rpm will not complain.
-                * (TODO? real rpm creates "file;1234" and then renames it) */
-               | ARCHIVE_UNLINK_OLD;
+                */
+               | ARCHIVE_REPLACE_VIA_RENAME;
        archive_handle->src_fd = fd;
        /*archive_handle->offset = 0; - init_handle() did it */
 
-       setup_unzip_on_fd(archive_handle->src_fd /*, fail_if_not_detected: 1*/);
+       setup_unzip_on_fd(archive_handle->src_fd, /*fail_if_not_detected:*/ 1);
        while (get_header_cpio(archive_handle) == EXIT_SUCCESS)
                continue;
 }
@@ -282,7 +180,7 @@ static int bsearch_rpmtag(const void *key, const void *item)
 static int rpm_getcount(int tag)
 {
        rpm_index **found;
-       found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
+       found = bsearch(&tag, G.mytags, G.tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
        if (!found)
                return 0;
        return found[0]->count;
@@ -291,7 +189,7 @@ static int rpm_getcount(int tag)
 static char *rpm_getstr(int tag, int itemindex)
 {
        rpm_index **found;
-       found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
+       found = bsearch(&tag, G.mytags, G.tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
        if (!found || itemindex >= found[0]->count)
                return NULL;
        if (found[0]->type == RPM_STRING_TYPE
@@ -299,7 +197,7 @@ static char *rpm_getstr(int tag, int itemindex)
         || found[0]->type == RPM_STRING_ARRAY_TYPE
        ) {
                int n;
-               char *tmpstr = (char *) map + found[0]->offset;
+               char *tmpstr = (char *) G.map + found[0]->offset;
                for (n = 0; n < itemindex; n++)
                        tmpstr = tmpstr + strlen(tmpstr) + 1;
                return tmpstr;
@@ -310,32 +208,25 @@ static char *rpm_getstr(int tag, int itemindex)
 static int rpm_getint(int tag, int itemindex)
 {
        rpm_index **found;
-       int *tmpint; /* NB: using int8_t* would be easier to code */
+       char *tmpint;
 
        /* gcc throws warnings here when sizeof(void*)!=sizeof(int) ...
         * it's ok to ignore it because tag won't be used as a pointer */
-       found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
+       found = bsearch(&tag, G.mytags, G.tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
        if (!found || itemindex >= found[0]->count)
                return -1;
 
-       tmpint = (int *) ((char *) map + found[0]->offset);
-
+       tmpint = (char *) G.map + found[0]->offset;
        if (found[0]->type == RPM_INT32_TYPE) {
-               tmpint = (int *) ((char *) tmpint + itemindex*4);
-               /*return ntohl(*tmpint);*/
-               /* int can be != int32_t */
+               tmpint += itemindex*4;
                return ntohl(*(int32_t*)tmpint);
        }
        if (found[0]->type == RPM_INT16_TYPE) {
-               tmpint = (int *) ((char *) tmpint + itemindex*2);
-               /* ??? read int, and THEN ntohs() it?? */
-               /*return ntohs(*tmpint);*/
+               tmpint += itemindex*2;
                return ntohs(*(int16_t*)tmpint);
        }
        if (found[0]->type == RPM_INT8_TYPE) {
-               tmpint = (int *) ((char *) tmpint + itemindex);
-               /* ??? why we don't read byte here??? */
-               /*return ntohs(*tmpint);*/
+               tmpint += itemindex;
                return *(int8_t*)tmpint;
        }
        return -1;
@@ -380,3 +271,134 @@ static void loop_through_files(int filetag, void (*fileaction)(char *filename, i
                free(filename);
        }
 }
+
+int rpm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int rpm_main(int argc, char **argv)
+{
+       int opt, func = 0;
+       const unsigned pagesize = getpagesize();
+
+       while ((opt = getopt(argc, argv, "iqpldc")) != -1) {
+               switch (opt) {
+               case 'i': /* First arg: Install mode, with q: Information */
+                       if (!func) func = rpm_install;
+                       else func |= rpm_query_info;
+                       break;
+               case 'q': /* First arg: Query mode */
+                       if (func) bb_show_usage();
+                       func = rpm_query;
+                       break;
+               case 'p': /* Query a package */
+                       func |= rpm_query_package;
+                       break;
+               case 'l': /* List files in a package */
+                       func |= rpm_query_list;
+                       break;
+               case 'd': /* List doc files in a package (implies list) */
+                       func |= rpm_query_list;
+                       func |= rpm_query_list_doc;
+                       break;
+               case 'c': /* List config files in a package (implies list) */
+                       func |= rpm_query_list;
+                       func |= rpm_query_list_config;
+                       break;
+               default:
+                       bb_show_usage();
+               }
+       }
+       argv += optind;
+       //argc -= optind;
+       if (!argv[0]) {
+               bb_show_usage();
+       }
+
+       while (*argv) {
+               int rpm_fd;
+               unsigned mapsize;
+               const char *source_rpm;
+
+               rpm_fd = xopen(*argv++, O_RDONLY);
+               G.mytags = rpm_gettags(rpm_fd, &G.tagcount);
+               if (!G.mytags)
+                       bb_error_msg_and_die("error reading rpm header");
+               mapsize = xlseek(rpm_fd, 0, SEEK_CUR);
+               mapsize = (mapsize + pagesize) & -(int)pagesize;
+               /* Some NOMMU systems prefer MAP_PRIVATE over MAP_SHARED */
+               G.map = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, rpm_fd, 0);
+//FIXME: error check?
+
+               source_rpm = rpm_getstr(TAG_SOURCERPM, 0);
+
+               if (func & rpm_install) {
+                       /* Backup any config files */
+                       loop_through_files(TAG_BASENAMES, fileaction_dobackup);
+                       /* Extact the archive */
+                       extract_cpio(rpm_fd, source_rpm);
+                       /* Set the correct file uid/gid's */
+                       loop_through_files(TAG_BASENAMES, fileaction_setowngrp);
+               }
+               else if ((func & (rpm_query|rpm_query_package)) == (rpm_query|rpm_query_package)) {
+                       if (!(func & (rpm_query_info|rpm_query_list))) {
+                               /* If just a straight query, just give package name */
+                               printf("%s-%s-%s\n", rpm_getstr(TAG_NAME, 0), rpm_getstr(TAG_VERSION, 0), rpm_getstr(TAG_RELEASE, 0));
+                       }
+                       if (func & rpm_query_info) {
+                               /* Do the nice printout */
+                               time_t bdate_time;
+                               struct tm *bdate_ptm;
+                               char bdatestring[50];
+                               const char *p;
+
+                               printf("%-12s: %s\n", "Name"        , rpm_getstr(TAG_NAME, 0));
+                               /* TODO compat: add "Epoch" here */
+                               printf("%-12s: %s\n", "Version"     , rpm_getstr(TAG_VERSION, 0));
+                               printf("%-12s: %s\n", "Release"     , rpm_getstr(TAG_RELEASE, 0));
+                               /* add "Architecture" */
+                               printf("%-12s: %s\n", "Install Date", "(not installed)");
+                               printf("%-12s: %s\n", "Group"       , rpm_getstr(TAG_GROUP, 0));
+                               printf("%-12s: %d\n", "Size"        , rpm_getint(TAG_SIZE, 0));
+                               printf("%-12s: %s\n", "License"     , rpm_getstr(TAG_LICENSE, 0));
+                               /* add "Signature" */
+                               printf("%-12s: %s\n", "Source RPM"  , source_rpm ? source_rpm : "(none)");
+                               bdate_time = rpm_getint(TAG_BUILDTIME, 0);
+                               bdate_ptm = localtime(&bdate_time);
+                               strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate_ptm);
+                               printf("%-12s: %s\n", "Build Date"  , bdatestring);
+                               printf("%-12s: %s\n", "Build Host"  , rpm_getstr(TAG_BUILDHOST, 0));
+                               p = rpm_getstr(TAG_PREFIXS, 0);
+                               printf("%-12s: %s\n", "Relocations" , p ? p : "(not relocatable)");
+                               /* add "Packager" */
+                               p = rpm_getstr(TAG_VENDOR, 0);
+                               printf("%-12s: %s\n", "Vendor"      , p ? p : "(none)");
+                               printf("%-12s: %s\n", "URL"         , rpm_getstr(TAG_URL, 0));
+                               printf("%-12s: %s\n", "Summary"     , rpm_getstr(TAG_SUMMARY, 0));
+                               printf("Description :\n%s\n", rpm_getstr(TAG_DESCRIPTION, 0));
+                       }
+                       if (func & rpm_query_list) {
+                               int count, it, flags;
+                               count = rpm_getcount(TAG_BASENAMES);
+                               for (it = 0; it < count; it++) {
+                                       flags = rpm_getint(TAG_FILEFLAGS, it);
+                                       switch (func & (rpm_query_list_doc|rpm_query_list_config)) {
+                                       case rpm_query_list_doc:
+                                               if (!(flags & RPMFILE_DOC)) continue;
+                                               break;
+                                       case rpm_query_list_config:
+                                               if (!(flags & RPMFILE_CONFIG)) continue;
+                                               break;
+                                       case rpm_query_list_doc|rpm_query_list_config:
+                                               if (!(flags & (RPMFILE_CONFIG|RPMFILE_DOC))) continue;
+                                               break;
+                                       }
+                                       printf("%s%s\n",
+                                               rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, it)),
+                                               rpm_getstr(TAG_BASENAMES, it));
+                               }
+                       }
+               }
+               munmap(G.map, mapsize);
+               free(G.mytags);
+               close(rpm_fd);
+       }
+       return 0;
+}
index f7c6fc2..afe2b55 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2001 by Laurence Anderson
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* RPM file starts with this struct: */
index 5bc50b8..61adde7 100644 (file)
@@ -4,10 +4,25 @@
  *
  * Copyright (C) 2001 by Laurence Anderson
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//config:config RPM2CPIO
+//config:      bool "rpm2cpio"
+//config:      default y
+//config:      help
+//config:        Converts a RPM file into a CPIO archive.
+
+//applet:IF_RPM2CPIO(APPLET(rpm2cpio, BB_DIR_USR_BIN, BB_SUID_DROP))
+//kbuild:lib-$(CONFIG_RPM2CPIO) += rpm2cpio.o
+
+//usage:#define rpm2cpio_trivial_usage
+//usage:       "package.rpm"
+//usage:#define rpm2cpio_full_usage "\n\n"
+//usage:       "Output a cpio archive of the rpm file"
+
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 #include "rpm.h"
 
 enum { rpm_fd = STDIN_FILENO };
@@ -60,54 +75,22 @@ int rpm2cpio_main(int argc UNUSED_PARAM, char **argv)
        /* Skip the main header */
        skip_header();
 
-#if 0
+       //if (SEAMLESS_COMPRESSION)
+       //      /* We need to know whether child (gzip/bzip/etc) exits abnormally */
+       //      signal(SIGCHLD, check_errors_in_children);
+
        /* This works, but doesn't report uncompress errors (they happen in child) */
-       setup_unzip_on_fd(rpm_fd /*fail_if_not_detected: 1*/);
+       setup_unzip_on_fd(rpm_fd, /*fail_if_not_detected:*/ 1);
        if (bb_copyfd_eof(rpm_fd, STDOUT_FILENO) < 0)
                bb_error_msg_and_die("error unpacking");
-#else
-       /* BLOAT */
-       {
-               union {
-                       uint8_t b[4];
-                       uint16_t b16[2];
-                       uint32_t b32[1];
-               } magic;
-               IF_DESKTOP(long long) int FAST_FUNC (*unpack)(int src_fd, int dst_fd);
-
-               xread(rpm_fd, magic.b16, sizeof(magic.b16[0]));
-               if (magic.b16[0] == GZIP_MAGIC) {
-                       unpack = unpack_gz_stream;
-               } else
-               if (ENABLE_FEATURE_SEAMLESS_BZ2
-                && magic.b16[0] == BZIP2_MAGIC
-               ) {
-                       unpack = unpack_bz2_stream;
-               } else
-               if (ENABLE_FEATURE_SEAMLESS_XZ
-                && magic.b16[0] == XZ_MAGIC1
-               ) {
-                       xread(rpm_fd, magic.b32, sizeof(magic.b32[0]));
-                       if (magic.b32[0] != XZ_MAGIC2)
-                               goto no_magic;
-                       /* unpack_xz_stream wants fd at position 6, no need to seek */
-                       //xlseek(rpm_fd, -6, SEEK_CUR);
-                       unpack = unpack_xz_stream;
-               } else {
- no_magic:
-                       bb_error_msg_and_die("no gzip"
-                                       IF_FEATURE_SEAMLESS_BZ2("/bzip2")
-                                       IF_FEATURE_SEAMLESS_XZ("/xz")
-                                       " magic");
-               }
-               if (unpack(rpm_fd, STDOUT_FILENO) < 0)
-                       bb_error_msg_and_die("error unpacking");
-       }
-#endif
 
        if (ENABLE_FEATURE_CLEAN_UP) {
                close(rpm_fd);
        }
 
-       return 0;
+       if (SEAMLESS_COMPRESSION) {
+               check_errors_in_children(0);
+               return bb_got_signal;
+       }
+       return EXIT_SUCCESS;
 }
index 5ddff7f..bd61abd 100644 (file)
  *  Copyright (c) 1999 by David I. Bell
  *  Permission is granted to use, distribute, or modify this source,
  *  provided that this copyright notice remains intact.
- *  Permission to distribute sash derived code under the GPL has been granted.
+ *  Permission to distribute sash derived code under GPL has been granted.
  *
  * Based in part on the tar implementation from busybox-0.28
  *  Copyright (C) 1995 Bruce Perens
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * 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"
+//config:      default y
+//config:      help
+//config:        tar is an archiving program. It's commonly used with gzip to
+//config:        create compressed archives. It's probably the most widely used
+//config:        UNIX archive program.
+//config:
+//config:config FEATURE_TAR_CREATE
+//config:      bool "Enable archive creation"
+//config:      default y
+//config:      depends on TAR
+//config:      help
+//config:        If you enable this option you'll be able to create
+//config:        tar archives using the `-c' option.
+//config:
+//config:config FEATURE_TAR_AUTODETECT
+//config:      bool "Autodetect compressed tarballs"
+//config:      default y
+//config:      depends on TAR && (FEATURE_SEAMLESS_Z || FEATURE_SEAMLESS_GZ || FEATURE_SEAMLESS_BZ2 || FEATURE_SEAMLESS_LZMA || FEATURE_SEAMLESS_XZ)
+//config:      help
+//config:        With this option tar can automatically detect compressed
+//config:        tarballs. Currently it works only on files (not pipes etc).
+//config:
+//config:config FEATURE_TAR_FROM
+//config:      bool "Enable -X (exclude from) and -T (include from) options)"
+//config:      default y
+//config:      depends on TAR
+//config:      help
+//config:        If you enable this option you'll be able to specify
+//config:        a list of files to include or exclude from an archive.
+//config:
+//config:config FEATURE_TAR_OLDGNU_COMPATIBILITY
+//config:      bool "Support for old tar header format"
+//config:      default y
+//config:      depends on TAR || DPKG
+//config:      help
+//config:        This option is required to unpack archives created in
+//config:        the old GNU format; help to kill this old format by
+//config:        repacking your ancient archives with the new format.
+//config:
+//config:config FEATURE_TAR_OLDSUN_COMPATIBILITY
+//config:      bool "Enable untarring of tarballs with checksums produced by buggy Sun tar"
+//config:      default y
+//config:      depends on TAR || DPKG
+//config:      help
+//config:        This option is required to unpack archives created by some old
+//config:        version of Sun's tar (it was calculating checksum using signed
+//config:        arithmetic). It is said to be fixed in newer Sun tar, but "old"
+//config:        tarballs still exist.
+//config:
+//config:config FEATURE_TAR_GNU_EXTENSIONS
+//config:      bool "Support for GNU tar extensions (long filenames)"
+//config:      default y
+//config:      depends on TAR || DPKG
+//config:      help
+//config:        With this option busybox supports GNU long filenames and
+//config:        linknames.
+//config:
+//config:config FEATURE_TAR_LONG_OPTIONS
+//config:      bool "Enable long options"
+//config:      default y
+//config:      depends on TAR && LONG_OPTS
+//config:      help
+//config:        Enable use of long options, increases size by about 400 Bytes
+//config:
+//config:config FEATURE_TAR_TO_COMMAND
+//config:      bool "Support for writing to an external program"
+//config:      default y
+//config:      depends on TAR && FEATURE_TAR_LONG_OPTIONS
+//config:      help
+//config:        If you enable this option you'll be able to instruct tar to send
+//config:        the contents of each extracted file to the standard input of an
+//config:        external program.
+//config:
+//config:config FEATURE_TAR_UNAME_GNAME
+//config:      bool "Enable use of user and group names"
+//config:      default y
+//config:      depends on TAR
+//config:      help
+//config:        Enables use of user and group names in tar. This affects contents
+//config:        listings (-t) and preserving permissions when unpacking (-p).
+//config:        +200 bytes.
+//config:
+//config:config FEATURE_TAR_NOPRESERVE_TIME
+//config:      bool "Enable -m (do not preserve time) option"
+//config:      default y
+//config:      depends on TAR
+//config:      help
+//config:        With this option busybox supports GNU tar -m
+//config:        (do not preserve time) option.
+//config:
+//config:config FEATURE_TAR_SELINUX
+//config:      bool "Support for extracting SELinux labels"
+//config:      default n
+//config:      depends on TAR && SELINUX
+//config:      help
+//config:        With this option busybox supports restoring SELinux labels
+//config:        when extracting files from tar archives.
+
+//applet:IF_TAR(APPLET(tar, BB_DIR_BIN, BB_SUID_DROP))
+//kbuild:lib-$(CONFIG_TAR) += tar.o
+
 #include <fnmatch.h>
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 /* FIXME: Stop using this non-standard feature */
 #ifndef FNM_LEADING_DIR
 # define FNM_LEADING_DIR 0
 
 #if !ENABLE_FEATURE_SEAMLESS_GZ && !ENABLE_FEATURE_SEAMLESS_BZ2
 /* Do not pass gzip flag to writeTarFile() */
-#define writeTarFile(tar_fd, verboseFlag, dereferenceFlag, include, exclude, gzip) \
-       writeTarFile(tar_fd, verboseFlag, dereferenceFlag, include, exclude)
+#define writeTarFile(tar_fd, verboseFlag, recurseFlags, include, exclude, gzip) \
+       writeTarFile(tar_fd, verboseFlag, recurseFlags, include, exclude)
 #endif
 
 
@@ -245,7 +366,8 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
        PUT_OCTAL(header.uid, statbuf->st_uid);
        PUT_OCTAL(header.gid, statbuf->st_gid);
        memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */
-       PUT_OCTAL(header.mtime, statbuf->st_mtime);
+       /* users report that files with negative st_mtime cause trouble, so: */
+       PUT_OCTAL(header.mtime, statbuf->st_mtime >= 0 ? statbuf->st_mtime : 0);
 
        /* Enter the user and group names */
        safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname));
@@ -297,15 +419,42 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
        } else if (S_ISFIFO(statbuf->st_mode)) {
                header.typeflag = FIFOTYPE;
        } else if (S_ISREG(statbuf->st_mode)) {
-               if (sizeof(statbuf->st_size) > 4
-                && statbuf->st_size > (off_t)0777777777777LL
+               /* header.size field is 12 bytes long */
+               /* Does octal-encoded size fit? */
+               uoff_t filesize = statbuf->st_size;
+               if (sizeof(filesize) <= 4
+                || filesize <= (uoff_t)0777777777777LL
+               ) {
+                       PUT_OCTAL(header.size, filesize);
+               }
+               /* Does base256-encoded size fit?
+                * It always does unless off_t is wider than 64 bits.
+                */
+               else if (ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+#if ULLONG_MAX > 0xffffffffffffffffLL /* 2^64-1 */
+                && (filesize <= 0x3fffffffffffffffffffffffLL)
+#endif
                ) {
+                       /* GNU tar uses "base-256 encoding" for very large numbers.
+                        * Encoding is binary, with highest bit always set as a marker
+                        * and sign in next-highest bit:
+                        * 80 00 .. 00 - zero
+                        * bf ff .. ff - largest positive number
+                        * ff ff .. ff - minus 1
+                        * c0 00 .. 00 - smallest negative number
+                        */
+                       char *p8 = header.size + sizeof(header.size);
+                       do {
+                               *--p8 = (uint8_t)filesize;
+                               filesize >>= 8;
+                       } while (p8 != header.size);
+                       *p8 |= 0x80;
+               } else {
                        bb_error_msg_and_die("can't store file '%s' "
                                "of size %"OFF_FMT"u, aborting",
                                fileName, statbuf->st_size);
                }
                header.typeflag = REGTYPE;
-               PUT_OCTAL(header.size, statbuf->st_size);
        } else {
                bb_error_msg("%s: unknown file type", fileName);
                return FALSE;
@@ -378,17 +527,8 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb
 
        DBG("writeFileToTarball('%s')", fileName);
 
-       /* Strip leading '/' (must be before memorizing hardlink's name) */
-       header_name = fileName;
-       while (header_name[0] == '/') {
-               static smallint warned;
-
-               if (!warned) {
-                       bb_error_msg("removing leading '/' from member names");
-                       warned = 1;
-               }
-               header_name++;
-       }
+       /* Strip leading '/' and such (must be before memorizing hardlink's name) */
+       header_name = strip_unsafe_prefix(fileName);
 
        if (header_name[0] == '\0')
                return TRUE;
@@ -529,7 +669,7 @@ static void NOINLINE vfork_compressor(int tar_fd, int gzip)
                xmove_fd(gzipDataPipe.rd, 0);
                xmove_fd(tar_fd, 1);
                /* exec gzip/bzip2 program/applet */
-               BB_EXECLP(zip_exec, zip_exec, "-f", NULL);
+               BB_EXECLP(zip_exec, zip_exec, "-f", (char *)0);
                vfork_exec_errno = errno;
                _exit(EXIT_FAILURE);
        }
@@ -560,7 +700,7 @@ static void NOINLINE vfork_compressor(int tar_fd, int gzip)
 
 /* gcc 4.2.1 inlines it, making code bigger */
 static NOINLINE int writeTarFile(int tar_fd, int verboseFlag,
-       int dereferenceFlag, const llist_t *include,
+       int recurseFlags, const llist_t *include,
        const llist_t *exclude, int gzip)
 {
        int errorFlag = FALSE;
@@ -572,8 +712,7 @@ static NOINLINE int writeTarFile(int tar_fd, int verboseFlag,
 
        /* Store the stat info for the tarball's file, so
         * can avoid including the tarball into itself....  */
-       if (fstat(tbInfo.tarFd, &tbInfo.tarFileStatBuf) < 0)
-               bb_perror_msg_and_die("can't stat tar file");
+       xfstat(tbInfo.tarFd, &tbInfo.tarFileStatBuf, "can't stat tar file");
 
 #if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
        if (gzip)
@@ -584,8 +723,7 @@ static NOINLINE int writeTarFile(int tar_fd, int verboseFlag,
 
        /* Read the directory/files and iterate over them one at a time */
        while (include) {
-               if (!recursive_action(include->data, ACTION_RECURSE |
-                               (dereferenceFlag ? ACTION_FOLLOWLINKS : 0),
+               if (!recursive_action(include->data, recurseFlags,
                                writeFileToTarball, writeFileToTarball, &tbInfo, 0)
                ) {
                        errorFlag = TRUE;
@@ -625,7 +763,7 @@ static NOINLINE int writeTarFile(int tar_fd, int verboseFlag,
 }
 #else
 int writeTarFile(int tar_fd, int verboseFlag,
-       int dereferenceFlag, const llist_t *include,
+       int recurseFlags, const llist_t *include,
        const llist_t *exclude, int gzip);
 #endif /* FEATURE_TAR_CREATE */
 
@@ -637,76 +775,32 @@ static llist_t *append_file_list_to_list(llist_t *list)
        llist_t *newlist = NULL;
 
        while (list) {
-               src_stream = xfopen_for_read(llist_pop(&list));
+               src_stream = xfopen_stdin(llist_pop(&list));
                while ((line = xmalloc_fgetline(src_stream)) != NULL) {
                        /* kill trailing '/' unless the string is just "/" */
                        char *cp = last_char_is(line, '/');
                        if (cp > line)
                                *cp = '\0';
-                       llist_add_to(&newlist, line);
+                       llist_add_to_end(&newlist, line);
                }
                fclose(src_stream);
        }
        return newlist;
 }
-#else
-# define append_file_list_to_list(x) 0
-#endif
-
-#if ENABLE_FEATURE_SEAMLESS_Z
-static char FAST_FUNC get_header_tar_Z(archive_handle_t *archive_handle)
-{
-       /* Can't lseek over pipes */
-       archive_handle->seek = seek_by_read;
-
-       /* do the decompression, and cleanup */
-       if (xread_char(archive_handle->src_fd) != 0x1f
-        || xread_char(archive_handle->src_fd) != 0x9d
-       ) {
-               bb_error_msg_and_die("invalid magic");
-       }
-
-       open_transformer(archive_handle->src_fd, unpack_Z_stream, "uncompress");
-       archive_handle->offset = 0;
-       while (get_header_tar(archive_handle) == EXIT_SUCCESS)
-               continue;
-
-       /* Can only do one file at a time */
-       return EXIT_FAILURE;
-}
-#else
-# define get_header_tar_Z NULL
-#endif
-
-#ifdef CHECK_FOR_CHILD_EXITCODE
-/* Looks like it isn't needed - tar detects malformed (truncated)
- * archive if e.g. bunzip2 fails */
-static int child_error;
-
-static void handle_SIGCHLD(int status)
-{
-       /* Actually, 'status' is a signo. We reuse it for other needs */
-
-       /* Wait for any child without blocking */
-       if (wait_any_nohang(&status) < 0)
-               /* wait failed?! I'm confused... */
-               return;
-
-       if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
-               /* child exited with 0 */
-               return;
-       /* Cannot happen?
-       if (!WIFSIGNALED(status) && !WIFEXITED(status)) return; */
-       child_error = 1;
-}
 #endif
 
 //usage:#define tar_trivial_usage
-//usage:       "-[" IF_FEATURE_TAR_CREATE("c") "xt" IF_FEATURE_SEAMLESS_GZ("z")
-//usage:       IF_FEATURE_SEAMLESS_BZ2("j") IF_FEATURE_SEAMLESS_LZMA("a")
-//usage:       IF_FEATURE_SEAMLESS_Z("Z") IF_FEATURE_TAR_NOPRESERVE_TIME("m") "vO] "
-//usage:       IF_FEATURE_TAR_FROM("[-X FILE] ")
-//usage:       "[-f TARFILE] [-C DIR] [FILE]..."
+//usage:       "-[" IF_FEATURE_TAR_CREATE("c") "xt"
+//usage:       IF_FEATURE_SEAMLESS_Z("Z")
+//usage:       IF_FEATURE_SEAMLESS_GZ("z")
+//usage:       IF_FEATURE_SEAMLESS_XZ("J")
+//usage:       IF_FEATURE_SEAMLESS_BZ2("j")
+//usage:       IF_FEATURE_SEAMLESS_LZMA("a")
+//usage:       IF_FEATURE_TAR_CREATE("h")
+//usage:       IF_FEATURE_TAR_NOPRESERVE_TIME("m")
+//usage:       "vO] "
+//usage:       IF_FEATURE_TAR_FROM("[-X FILE] [-T FILE] ")
+//usage:       "[-f TARFILE] [-C DIR] [FILE]..."
 //usage:#define tar_full_usage "\n\n"
 //usage:       IF_FEATURE_TAR_CREATE("Create, extract, ")
 //usage:       IF_NOT_FEATURE_TAR_CREATE("Extract ")
@@ -717,22 +811,24 @@ static void handle_SIGCHLD(int status)
 //usage:       )
 //usage:     "\n       x       Extract"
 //usage:     "\n       t       List"
-//usage:     "\nOptions:"
 //usage:     "\n       f       Name of TARFILE ('-' for stdin/out)"
 //usage:     "\n       C       Change to DIR before operation"
 //usage:     "\n       v       Verbose"
+//usage:       IF_FEATURE_SEAMLESS_Z(
+//usage:     "\n       Z       (De)compress using compress"
+//usage:       )
 //usage:       IF_FEATURE_SEAMLESS_GZ(
 //usage:     "\n       z       (De)compress using gzip"
 //usage:       )
+//usage:       IF_FEATURE_SEAMLESS_XZ(
+//usage:     "\n       J       (De)compress using xz"
+//usage:       )
 //usage:       IF_FEATURE_SEAMLESS_BZ2(
 //usage:     "\n       j       (De)compress using bzip2"
 //usage:       )
 //usage:       IF_FEATURE_SEAMLESS_LZMA(
 //usage:     "\n       a       (De)compress using lzma"
 //usage:       )
-//usage:       IF_FEATURE_SEAMLESS_Z(
-//usage:     "\n       Z       (De)compress using compress"
-//usage:       )
 //usage:     "\n       O       Extract to stdout"
 //usage:       IF_FEATURE_TAR_CREATE(
 //usage:     "\n       h       Follow symlinks"
@@ -756,6 +852,7 @@ static void handle_SIGCHLD(int status)
 //     o       no-same-owner
 //     p       same-permissions
 //     k       keep-old
+//     no-recursion
 //     numeric-owner
 //     no-same-permissions
 //     overwrite
@@ -772,9 +869,11 @@ enum {
        IF_FEATURE_TAR_FROM(     OPTBIT_INCLUDE_FROM,)
        IF_FEATURE_TAR_FROM(     OPTBIT_EXCLUDE_FROM,)
        IF_FEATURE_SEAMLESS_GZ(  OPTBIT_GZIP        ,)
-       IF_FEATURE_SEAMLESS_Z(   OPTBIT_COMPRESS    ,) // 16th bit
+       IF_FEATURE_SEAMLESS_XZ(  OPTBIT_XZ          ,) // 16th bit
+       IF_FEATURE_SEAMLESS_Z(   OPTBIT_COMPRESS    ,)
        IF_FEATURE_TAR_NOPRESERVE_TIME(OPTBIT_NOPRESERVE_TIME,)
 #if ENABLE_FEATURE_TAR_LONG_OPTIONS
+       OPTBIT_NORECURSION,
        IF_FEATURE_TAR_TO_COMMAND(OPTBIT_2COMMAND   ,)
        OPTBIT_NUMERIC_OWNER,
        OPTBIT_NOPRESERVE_PERM,
@@ -796,12 +895,16 @@ enum {
        OPT_INCLUDE_FROM = IF_FEATURE_TAR_FROM(     (1 << OPTBIT_INCLUDE_FROM)) + 0, // T
        OPT_EXCLUDE_FROM = IF_FEATURE_TAR_FROM(     (1 << OPTBIT_EXCLUDE_FROM)) + 0, // X
        OPT_GZIP         = IF_FEATURE_SEAMLESS_GZ(  (1 << OPTBIT_GZIP        )) + 0, // z
+       OPT_XZ           = IF_FEATURE_SEAMLESS_XZ(  (1 << OPTBIT_XZ          )) + 0, // J
        OPT_COMPRESS     = IF_FEATURE_SEAMLESS_Z(   (1 << OPTBIT_COMPRESS    )) + 0, // Z
        OPT_NOPRESERVE_TIME = IF_FEATURE_TAR_NOPRESERVE_TIME((1 << OPTBIT_NOPRESERVE_TIME)) + 0, // m
+       OPT_NORECURSION     = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NORECURSION    )) + 0, // no-recursion
        OPT_2COMMAND        = IF_FEATURE_TAR_TO_COMMAND(  (1 << OPTBIT_2COMMAND       )) + 0, // to-command
        OPT_NUMERIC_OWNER   = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NUMERIC_OWNER  )) + 0, // numeric-owner
        OPT_NOPRESERVE_PERM = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NOPRESERVE_PERM)) + 0, // no-same-permissions
        OPT_OVERWRITE       = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_OVERWRITE      )) + 0, // overwrite
+
+       OPT_ANY_COMPRESS = (OPT_BZIP2 | OPT_LZMA | OPT_GZIP | OPT_XZ | OPT_COMPRESS),
 };
 #if ENABLE_FEATURE_TAR_LONG_OPTIONS
 static const char tar_longopts[] ALIGN1 =
@@ -834,12 +937,16 @@ static const char tar_longopts[] ALIGN1 =
 # if ENABLE_FEATURE_SEAMLESS_GZ
        "gzip\0"                No_argument       "z"
 # endif
+# if ENABLE_FEATURE_SEAMLESS_XZ
+       "xz\0"                  No_argument       "J"
+# endif
 # if ENABLE_FEATURE_SEAMLESS_Z
        "compress\0"            No_argument       "Z"
 # endif
 # if ENABLE_FEATURE_TAR_NOPRESERVE_TIME
        "touch\0"               No_argument       "m"
 # endif
+       "no-recursion\0"        No_argument       "\xfa"
 # if ENABLE_FEATURE_TAR_TO_COMMAND
        "to-command\0"          Required_argument "\xfb"
 # endif
@@ -860,7 +967,6 @@ static const char tar_longopts[] ALIGN1 =
 int tar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int tar_main(int argc UNUSED_PARAM, char **argv)
 {
-       char FAST_FUNC (*get_header_ptr)(archive_handle_t *) = get_header_tar;
        archive_handle_t *tar_handle;
        char *base_dir = NULL;
        const char *tar_filename = "-";
@@ -883,8 +989,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
        /* Prepend '-' to the first argument if required */
        opt_complementary = "--:" // first arg is options
                "tt:vv:" // count -t,-v
-               "?:" // bail out with usage instead of error return
-               "X::T::" // cumulative lists
+               IF_FEATURE_TAR_FROM("X::T::") // cumulative lists
 #if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
                "\xff::" // cumulative lists for --exclude
 #endif
@@ -928,6 +1033,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
                IF_FEATURE_SEAMLESS_LZMA("a"   )
                IF_FEATURE_TAR_FROM(     "T:X:")
                IF_FEATURE_SEAMLESS_GZ(  "z"   )
+               IF_FEATURE_SEAMLESS_XZ(  "J"   )
                IF_FEATURE_SEAMLESS_Z(   "Z"   )
                IF_FEATURE_TAR_NOPRESERVE_TIME("m")
                , &base_dir // -C dir
@@ -957,6 +1063,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
                putenv((char*)"TAR_FILETYPE=f");
                signal(SIGPIPE, SIG_IGN);
                tar_handle->action_data = data_extract_to_command;
+               IF_FEATURE_TAR_TO_COMMAND(tar_handle->tar__to_command_shell = xstrdup(get_shell_name());)
        }
 
        if (opt & OPT_KEEP_OLD)
@@ -976,18 +1083,6 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
                tar_handle->ah_flags |= ARCHIVE_O_TRUNC;
        }
 
-       if (opt & OPT_GZIP)
-               get_header_ptr = get_header_tar_gz;
-
-       if (opt & OPT_BZIP2)
-               get_header_ptr = get_header_tar_bz2;
-
-       if (opt & OPT_LZMA)
-               get_header_ptr = get_header_tar_lzma;
-
-       if (opt & OPT_COMPRESS)
-               get_header_ptr = get_header_tar_Z;
-
        if (opt & OPT_NOPRESERVE_TIME)
                tar_handle->ah_flags &= ~ARCHIVE_RESTORE_DATE;
 
@@ -1038,8 +1133,10 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
                        tar_handle->src_fd = tar_fd;
                        tar_handle->seek = seek_by_read;
                } else {
-                       if (ENABLE_FEATURE_TAR_AUTODETECT && flags == O_RDONLY) {
-                               get_header_ptr = get_header_tar;
+                       if (ENABLE_FEATURE_TAR_AUTODETECT
+                        && flags == O_RDONLY
+                        && !(opt & OPT_ANY_COMPRESS)
+                       ) {
                                tar_handle->src_fd = open_zipped(tar_filename);
                                if (tar_handle->src_fd < 0)
                                        bb_perror_msg_and_die("can't open '%s'", tar_filename);
@@ -1052,10 +1149,9 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
        if (base_dir)
                xchdir(base_dir);
 
-#ifdef CHECK_FOR_CHILD_EXITCODE
-       /* We need to know whether child (gzip/bzip/etc) exits abnormally */
-       signal(SIGCHLD, handle_SIGCHLD);
-#endif
+       //if (SEAMLESS_COMPRESSION || OPT_COMPRESS)
+       //      /* We need to know whether child (gzip/bzip/etc) exits abnormally */
+       //      signal(SIGCHLD, check_errors_in_children);
 
        /* Create an archive */
        if (opt & OPT_CREATE) {
@@ -1067,13 +1163,47 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
                        zipMode = 2;
 #endif
                /* NB: writeTarFile() closes tar_handle->src_fd */
-               return writeTarFile(tar_handle->src_fd, verboseFlag, opt & OPT_DEREFERENCE,
+               return writeTarFile(tar_handle->src_fd, verboseFlag,
+                               (opt & OPT_DEREFERENCE ? ACTION_FOLLOWLINKS : 0)
+                               | (opt & OPT_NORECURSION ? 0 : ACTION_RECURSE),
                                tar_handle->accept,
                                tar_handle->reject, zipMode);
        }
 
-       while (get_header_ptr(tar_handle) == EXIT_SUCCESS)
-               continue;
+       if (opt & OPT_ANY_COMPRESS) {
+               USE_FOR_MMU(IF_DESKTOP(long long) int FAST_FUNC (*xformer)(transformer_aux_data_t *aux, int src_fd, int dst_fd);)
+               USE_FOR_NOMMU(const char *xformer_prog;)
+
+               if (opt & OPT_COMPRESS)
+                       USE_FOR_MMU(xformer = unpack_Z_stream;)
+                       USE_FOR_NOMMU(xformer_prog = "uncompress";)
+               if (opt & OPT_GZIP)
+                       USE_FOR_MMU(xformer = unpack_gz_stream;)
+                       USE_FOR_NOMMU(xformer_prog = "gunzip";)
+               if (opt & OPT_BZIP2)
+                       USE_FOR_MMU(xformer = unpack_bz2_stream;)
+                       USE_FOR_NOMMU(xformer_prog = "bunzip2";)
+               if (opt & OPT_LZMA)
+                       USE_FOR_MMU(xformer = unpack_lzma_stream;)
+                       USE_FOR_NOMMU(xformer_prog = "unlzma";)
+               if (opt & OPT_XZ)
+                       USE_FOR_MMU(xformer = unpack_xz_stream;)
+                       USE_FOR_NOMMU(xformer_prog = "unxz";)
+
+               open_transformer_with_sig(tar_handle->src_fd, xformer, xformer_prog);
+               /* Can't lseek over pipes */
+               tar_handle->seek = seek_by_read;
+               /*tar_handle->offset = 0; - already is */
+       }
+
+       /* Zero processed headers (== empty file) is not a valid tarball.
+        * We (ab)use bb_got_signal as exitcode here,
+        * because check_errors_in_children() uses _it_ as error indicator.
+        */
+       bb_got_signal = EXIT_FAILURE;
+
+       while (get_header_tar(tar_handle) == EXIT_SUCCESS)
+               bb_got_signal = EXIT_SUCCESS; /* saw at least one header, good */
 
        /* Check that every file that should have been extracted was */
        while (tar_handle->accept) {
@@ -1088,5 +1218,10 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
        if (ENABLE_FEATURE_CLEAN_UP /* && tar_handle->src_fd != STDIN_FILENO */)
                close(tar_handle->src_fd);
 
-       return EXIT_SUCCESS;
+       if (SEAMLESS_COMPRESSION || OPT_COMPRESS) {
+               /* Set bb_got_signal to 1 if a child died with !0 exitcode */
+               check_errors_in_children(0);
+       }
+
+       return bb_got_signal;
 }
index 84081c0..fcfc9a4 100644 (file)
@@ -7,20 +7,43 @@
  * Loosely based on original busybox unzip applet by Laurence Anderson.
  * All options and features should work in this version.
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
-
 /* For reference see
  * http://www.pkware.com/company/standards/appnote/
  * http://www.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip
- */
-
-/* TODO
+ *
+ * TODO
  * Zip64 + other methods
  */
 
+//config:config UNZIP
+//config:      bool "unzip"
+//config:      default y
+//config:      help
+//config:        unzip will list or extract files from a ZIP archive,
+//config:        commonly found on DOS/WIN systems. The default behavior
+//config:        (with no options) is to extract the archive into the
+//config:        current directory. Use the `-d' option to extract to a
+//config:        directory of your choice.
+
+//applet:IF_UNZIP(APPLET(unzip, BB_DIR_USR_BIN, BB_SUID_DROP))
+//kbuild:lib-$(CONFIG_UNZIP) += unzip.o
+
+//usage:#define unzip_trivial_usage
+//usage:       "[-lnopq] FILE[.zip] [FILE]... [-x FILE...] [-d DIR]"
+//usage:#define unzip_full_usage "\n\n"
+//usage:       "Extract FILEs from ZIP archive\n"
+//usage:     "\n       -l      List contents (with -q for short form)"
+//usage:     "\n       -n      Never overwrite files (default: ask)"
+//usage:     "\n       -o      Overwrite"
+//usage:     "\n       -p      Print to stdout"
+//usage:     "\n       -q      Quiet"
+//usage:     "\n       -x FILE Exclude FILEs"
+//usage:     "\n       -d DIR  Extract into DIR"
+
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 enum {
 #if BB_BIG_ENDIAN
@@ -151,7 +174,17 @@ enum { zip_fd = 3 };
 
 #if ENABLE_DESKTOP
 
-#define PEEK_FROM_END 16384
+/* Seen in the wild:
+ * Self-extracting PRO2K3XP_32.exe contains 19078464 byte zip archive,
+ * where CDE was nearly 48 kbytes before EOF.
+ * (Surprisingly, it also apparently has *another* CDE structure
+ * closer to the end, with bogus cdf_offset).
+ * To make extraction work, bumped PEEK_FROM_END from 16k to 64k.
+ */
+#define PEEK_FROM_END (64*1024)
+
+/* This value means that we failed to find CDF */
+#define BAD_CDF_OFFSET ((uint32_t)0xffffffff)
 
 /* NB: does not preserve file position! */
 static uint32_t find_cdf_offset(void)
@@ -168,6 +201,7 @@ static uint32_t find_cdf_offset(void)
        xlseek(zip_fd, end, SEEK_SET);
        full_read(zip_fd, buf, PEEK_FROM_END);
 
+       cde_header.formatted.cdf_offset = BAD_CDF_OFFSET;
        p = buf;
        while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) {
                if (*p != 'P') {
@@ -183,11 +217,17 @@ static uint32_t find_cdf_offset(void)
                /* we found CDE! */
                memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN);
                FIX_ENDIANNESS_CDE(cde_header);
-               free(buf);
-               return cde_header.formatted.cdf_offset;
+               /*
+                * I've seen .ZIP files with seemingly valid CDEs
+                * where cdf_offset points past EOF - ??
+                * Ignore such CDEs:
+                */
+               if (cde_header.formatted.cdf_offset < end + (p - buf))
+                       break;
+               cde_header.formatted.cdf_offset = BAD_CDF_OFFSET;
        }
-       //free(buf);
-       bb_error_msg_and_die("can't find file table");
+       free(buf);
+       return cde_header.formatted.cdf_offset;
 };
 
 static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
@@ -199,13 +239,15 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
        if (!cdf_offset)
                cdf_offset = find_cdf_offset();
 
-       xlseek(zip_fd, cdf_offset + 4, SEEK_SET);
-       xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN);
-       FIX_ENDIANNESS_CDF(*cdf_ptr);
-       cdf_offset += 4 + CDF_HEADER_LEN
-               + cdf_ptr->formatted.file_name_length
-               + cdf_ptr->formatted.extra_field_length
-               + cdf_ptr->formatted.file_comment_length;
+       if (cdf_offset != BAD_CDF_OFFSET) {
+               xlseek(zip_fd, cdf_offset + 4, SEEK_SET);
+               xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN);
+               FIX_ENDIANNESS_CDF(*cdf_ptr);
+               cdf_offset += 4 + CDF_HEADER_LEN
+                       + cdf_ptr->formatted.file_name_length
+                       + cdf_ptr->formatted.extra_field_length
+                       + cdf_ptr->formatted.file_comment_length;
+       }
 
        xlseek(zip_fd, org, SEEK_SET);
        return cdf_offset;
@@ -214,8 +256,9 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
 
 static void unzip_skip(off_t skip)
 {
-       if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1)
-               bb_copyfd_exact_size(zip_fd, -1, skip);
+       if (skip != 0)
+               if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1)
+                       bb_copyfd_exact_size(zip_fd, -1, skip);
 }
 
 static void unzip_create_leading_dirs(const char *fn)
@@ -223,7 +266,7 @@ static void unzip_create_leading_dirs(const char *fn)
        /* Create all leading directories */
        char *name = xstrdup(fn);
        if (bb_make_directory(dirname(name), 0777, FILEUTILS_RECUR)) {
-               bb_error_msg_and_die("exiting"); /* bb_make_directory is noisy */
+               xfunc_die(); /* bb_make_directory is noisy */
        }
        free(name);
 }
@@ -237,15 +280,17 @@ static void unzip_extract(zip_header_t *zip_header, int dst_fd)
                        bb_copyfd_exact_size(zip_fd, dst_fd, size);
        } else {
                /* Method 8 - inflate */
-               inflate_unzip_result res;
-               if (inflate_unzip(&res, zip_header->formatted.cmpsize, zip_fd, dst_fd) < 0)
+               transformer_aux_data_t aux;
+               init_transformer_aux_data(&aux);
+               aux.bytes_in = zip_header->formatted.cmpsize;
+               if (inflate_unzip(&aux, zip_fd, dst_fd) < 0)
                        bb_error_msg_and_die("inflate error");
                /* Validate decompression - crc */
-               if (zip_header->formatted.crc32 != (res.crc ^ 0xffffffffL)) {
+               if (zip_header->formatted.crc32 != (aux.crc32 ^ 0xffffffffL)) {
                        bb_error_msg_and_die("crc error");
                }
                /* Validate decompression - size */
-               if (zip_header->formatted.ucmpsize != res.bytes_out) {
+               if (zip_header->formatted.ucmpsize != aux.bytes_out) {
                        /* Don't die. Who knows, maybe len calculation
                         * was botched somewhere. After all, crc matched! */
                        bb_error_msg("bad length");
@@ -253,6 +298,14 @@ static void unzip_extract(zip_header_t *zip_header, int dst_fd)
        }
 }
 
+static void my_fgets80(char *buf80)
+{
+       fflush_all();
+       if (!fgets(buf80, 80, stdin)) {
+               bb_perror_msg_and_die("can't read standard input");
+       }
+}
+
 int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int unzip_main(int argc, char **argv)
 {
@@ -263,6 +316,7 @@ int unzip_main(int argc, char **argv)
        IF_NOT_DESKTOP(const) smallint verbose = 0;
        smallint listing = 0;
        smallint overwrite = O_PROMPT;
+       smallint x_opt_seen;
 #if ENABLE_DESKTOP
        uint32_t cdf_offset;
 #endif
@@ -276,8 +330,7 @@ int unzip_main(int argc, char **argv)
        llist_t *zreject = NULL;
        char *base_dir = NULL;
        int i, opt;
-       int opt_range = 0;
-       char key_buf[80];
+       char key_buf[80]; /* must match size used by my_fgets80 */
        struct stat stat_buf;
 
 /* -q, -l and -v: UnZip 5.52 of 28 February 2005, by Info-ZIP:
@@ -321,82 +374,81 @@ int unzip_main(int argc, char **argv)
  *    204372                   1 file
  */
 
+       x_opt_seen = 0;
        /* '-' makes getopt return 1 for non-options */
        while ((opt = getopt(argc, argv, "-d:lnopqxv")) != -1) {
-               switch (opt_range) {
-               case 0: /* Options */
-                       switch (opt) {
-                       case 'l': /* List */
-                               listing = 1;
-                               break;
-
-                       case 'n': /* Never overwrite existing files */
-                               overwrite = O_NEVER;
-                               break;
+               switch (opt) {
+               case 'd':  /* Extract to base directory */
+                       base_dir = optarg;
+                       break;
 
-                       case 'o': /* Always overwrite existing files */
-                               overwrite = O_ALWAYS;
-                               break;
+               case 'l': /* List */
+                       listing = 1;
+                       break;
 
-                       case 'p': /* Extract files to stdout and fall through to set verbosity */
-                               dst_fd = STDOUT_FILENO;
+               case 'n': /* Never overwrite existing files */
+                       overwrite = O_NEVER;
+                       break;
 
-                       case 'q': /* Be quiet */
-                               quiet++;
-                               break;
+               case 'o': /* Always overwrite existing files */
+                       overwrite = O_ALWAYS;
+                       break;
 
-                       case 'v': /* Verbose list */
-                               IF_DESKTOP(verbose++;)
-                               listing = 1;
-                               break;
+               case 'p': /* Extract files to stdout and fall through to set verbosity */
+                       dst_fd = STDOUT_FILENO;
 
-                       case 1: /* The zip file */
-                               /* +5: space for ".zip" and NUL */
-                               src_fn = xmalloc(strlen(optarg) + 5);
-                               strcpy(src_fn, optarg);
-                               opt_range++;
-                               break;
+               case 'q': /* Be quiet */
+                       quiet++;
+                       break;
 
-                       default:
-                               bb_show_usage();
+               case 'v': /* Verbose list */
+                       IF_DESKTOP(verbose++;)
+                       listing = 1;
+                       break;
 
-                       }
+               case 'x':
+                       x_opt_seen = 1;
                        break;
 
-               case 1: /* Include files */
-                       if (opt == 1) {
+               case 1:
+                       if (!src_fn) {
+                               /* The zip file */
+                               /* +5: space for ".zip" and NUL */
+                               src_fn = xmalloc(strlen(optarg) + 5);
+                               strcpy(src_fn, optarg);
+                       } else if (!x_opt_seen) {
+                               /* Include files */
                                llist_add_to(&zaccept, optarg);
-                               break;
-                       }
-                       if (opt == 'd') {
-                               base_dir = optarg;
-                               opt_range += 2;
-                               break;
-                       }
-                       if (opt == 'x') {
-                               opt_range++;
-                               break;
-                       }
-                       bb_show_usage();
-
-               case 2 : /* Exclude files */
-                       if (opt == 1) {
+                       } else {
+                               /* Exclude files */
                                llist_add_to(&zreject, optarg);
-                               break;
-                       }
-                       if (opt == 'd') { /* Extract to base directory */
-                               base_dir = optarg;
-                               opt_range++;
-                               break;
                        }
-                       /* fall through */
+                       break;
 
                default:
                        bb_show_usage();
                }
        }
 
-       if (src_fn == NULL) {
+#ifndef __GLIBC__
+       /*
+        * This code is needed for non-GNU getopt
+        * which doesn't understand "-" in option string.
+        * The -x option won't work properly in this case:
+        * "unzip a.zip q -x w e" will be interpreted as
+        * "unzip a.zip q w e -x" = "unzip a.zip q w e"
+        */
+       argv += optind;
+       if (argv[0]) {
+               /* +5: space for ".zip" and NUL */
+               src_fn = xmalloc(strlen(argv[0]) + 5);
+               strcpy(src_fn, argv[0]);
+               while (*++argv)
+                       llist_add_to(&zaccept, *argv);
+       }
+#endif
+
+       if (!src_fn) {
                bb_show_usage();
        }
 
@@ -407,17 +459,20 @@ int unzip_main(int argc, char **argv)
                if (overwrite == O_PROMPT)
                        overwrite = O_NEVER;
        } else {
-               static const char extn[][5] = {"", ".zip", ".ZIP"};
-               int orig_src_fn_len = strlen(src_fn);
-               int src_fd = -1;
+               static const char extn[][5] = { ".zip", ".ZIP" };
+               char *ext = src_fn + strlen(src_fn);
+               int src_fd;
 
-               for (i = 0; (i < 3) && (src_fd == -1); i++) {
-                       strcpy(src_fn + orig_src_fn_len, extn[i]);
+               i = 0;
+               for (;;) {
                        src_fd = open(src_fn, O_RDONLY);
-               }
-               if (src_fd == -1) {
-                       src_fn[orig_src_fn_len] = '\0';
-                       bb_error_msg_and_die("can't open %s, %s.zip, %s.ZIP", src_fn, src_fn, src_fn);
+                       if (src_fd >= 0)
+                               break;
+                       if (++i > 2) {
+                               *ext = '\0';
+                               bb_error_msg_and_die("can't open %s[.zip]", src_fn);
+                       }
+                       strcpy(ext, extn[i - 1]);
                }
                xmove_fd(src_fd, zip_fd);
        }
@@ -504,21 +559,31 @@ int unzip_main(int argc, char **argv)
                        bb_error_msg_and_die("zip flag 1 (encryption) is not supported");
                }
 
-               {
+               if (cdf_offset != BAD_CDF_OFFSET) {
                        cdf_header_t cdf_header;
                        cdf_offset = read_next_cdf(cdf_offset, &cdf_header);
+                       /*
+                        * Note: cdf_offset can become BAD_CDF_OFFSET after the above call.
+                        */
                        if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) {
                                /* 0x0008 - streaming. [u]cmpsize can be reliably gotten
-                                * only from Central Directory. See unzip_doc.txt */
+                                * only from Central Directory. See unzip_doc.txt
+                                */
                                zip_header.formatted.crc32    = cdf_header.formatted.crc32;
                                zip_header.formatted.cmpsize  = cdf_header.formatted.cmpsize;
                                zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize;
                        }
                        if ((cdf_header.formatted.version_made_by >> 8) == 3) {
-                               /* this archive is created on Unix */
+                               /* This archive is created on Unix */
                                dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16);
                        }
                }
+               if (cdf_offset == BAD_CDF_OFFSET
+                && (zip_header.formatted.zip_flags & SWAP_LE16(0x0008))
+               ) {
+                       /* If it's a streaming zip, we _require_ CDF */
+                       bb_error_msg_and_die("can't find file table");
+               }
 #endif
 
                /* Read filename */
@@ -583,8 +648,8 @@ int unzip_main(int argc, char **argv)
                                                printf("   creating: %s\n", dst_fn);
                                        }
                                        unzip_create_leading_dirs(dst_fn);
-                                       if (bb_make_directory(dst_fn, dir_mode, 0)) {
-                                               bb_error_msg_and_die("exiting");
+                                       if (bb_make_directory(dst_fn, dir_mode, FILEUTILS_IGNORE_CHMOD_ERR)) {
+                                               xfunc_die();
                                        }
                                } else {
                                        if (!S_ISDIR(stat_buf.st_mode)) {
@@ -608,9 +673,7 @@ int unzip_main(int argc, char **argv)
                                                        i = 'y';
                                                } else {
                                                        printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn);
-                                                       if (!fgets(key_buf, sizeof(key_buf), stdin)) {
-                                                               bb_perror_msg_and_die("can't read input");
-                                                       }
+                                                       my_fgets80(key_buf);
                                                        i = key_buf[0];
                                                }
                                        } else { /* File is not regular file */
@@ -651,9 +714,7 @@ int unzip_main(int argc, char **argv)
                case 'r':
                        /* Prompt for new name */
                        printf("new name: ");
-                       if (!fgets(key_buf, sizeof(key_buf), stdin)) {
-                               bb_perror_msg_and_die("can't read input");
-                       }
+                       my_fgets80(key_buf);
                        free(dst_fn);
                        dst_fn = xstrdup(key_buf);
                        chomp(dst_fn);
similarity index 99%
rename from TEST_config_nommu
rename to configs/TEST_nommu_defconfig
index 911f02f..b45afd9 100644 (file)
@@ -79,7 +79,7 @@ CONFIG_PREFIX="./_install"
 # Busybox Library Tuning
 #
 CONFIG_PASSWORD_MINLEN=6
-CONFIG_MD5_SIZE_VS_SPEED=2
+CONFIG_MD5_SMALL=1
 CONFIG_FEATURE_FAST_TOP=y
 CONFIG_FEATURE_ETC_NETWORKS=y
 CONFIG_FEATURE_EDITING=y
@@ -189,7 +189,6 @@ CONFIG_HOSTID=y
 CONFIG_ID=y
 CONFIG_INSTALL=y
 CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y
-CONFIG_LENGTH=y
 CONFIG_LN=y
 CONFIG_LOGNAME=y
 CONFIG_LS=y
@@ -342,7 +341,6 @@ CONFIG_FEATURE_VI_READONLY=y
 CONFIG_FEATURE_VI_SETOPTS=y
 CONFIG_FEATURE_VI_SET=y
 CONFIG_FEATURE_VI_WIN_RESIZE=y
-CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y
 CONFIG_FEATURE_ALLOW_EXEC=y
 
 #
@@ -904,7 +902,6 @@ CONFIG_HUSH_FUNCTIONS=y
 CONFIG_HUSH_LOCAL=y
 CONFIG_HUSH_EXPORT_N=y
 CONFIG_HUSH_RANDOM_SUPPORT=y
-CONFIG_LASH=y
 CONFIG_MSH=y
 CONFIG_SH_MATH_SUPPORT=y
 CONFIG_SH_MATH_SUPPORT_64=y
similarity index 99%
rename from TEST_config_noprintf
rename to configs/TEST_noprintf_defconfig
index ba003a1..809b60c 100644 (file)
@@ -89,7 +89,7 @@ CONFIG_PREFIX="./_install"
 # Busybox Library Tuning
 #
 CONFIG_PASSWORD_MINLEN=6
-CONFIG_MD5_SIZE_VS_SPEED=2
+CONFIG_MD5_SMALL=1
 CONFIG_FEATURE_FAST_TOP=y
 # CONFIG_FEATURE_ETC_NETWORKS is not set
 CONFIG_FEATURE_EDITING=y
@@ -211,7 +211,6 @@ CONFIG_FALSE=y
 # CONFIG_ID is not set
 # CONFIG_INSTALL is not set
 # CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set
-# CONFIG_LENGTH is not set
 # CONFIG_LN is not set
 # CONFIG_LOGNAME is not set
 # CONFIG_LS is not set
@@ -347,7 +346,6 @@ CONFIG_FEATURE_VI_MAX_LEN=0
 # CONFIG_FEATURE_VI_SET is not set
 # CONFIG_FEATURE_VI_WIN_RESIZE is not set
 # CONFIG_FEATURE_VI_ASK_TERMINAL is not set
-# CONFIG_FEATURE_VI_OPTIMIZE_CURSOR is not set
 # CONFIG_FEATURE_ALLOW_EXEC is not set
 
 #
@@ -904,7 +902,6 @@ CONFIG_FEATURE_SH_IS_NONE=y
 # CONFIG_FEATURE_BASH_IS_ASH is not set
 # CONFIG_FEATURE_BASH_IS_HUSH is not set
 CONFIG_FEATURE_BASH_IS_NONE=y
-# CONFIG_LASH is not set
 # CONFIG_MSH is not set
 # CONFIG_SH_MATH_SUPPORT is not set
 # CONFIG_SH_MATH_SUPPORT_64 is not set
similarity index 99%
rename from TEST_config_rh9
rename to configs/TEST_rh9_defconfig
index f376cd4..565b826 100644 (file)
@@ -88,7 +88,7 @@ CONFIG_PREFIX="./_install"
 # Busybox Library Tuning
 #
 CONFIG_PASSWORD_MINLEN=6
-CONFIG_MD5_SIZE_VS_SPEED=2
+CONFIG_MD5_SMALL=1
 CONFIG_FEATURE_FAST_TOP=y
 # CONFIG_FEATURE_ETC_NETWORKS is not set
 CONFIG_FEATURE_EDITING=y
@@ -200,7 +200,6 @@ CONFIG_HOSTID=y
 CONFIG_ID=y
 CONFIG_INSTALL=y
 CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y
-CONFIG_LENGTH=y
 CONFIG_LN=y
 CONFIG_LOGNAME=y
 CONFIG_LS=y
@@ -359,7 +358,6 @@ CONFIG_FEATURE_VI_READONLY=y
 CONFIG_FEATURE_VI_SETOPTS=y
 CONFIG_FEATURE_VI_SET=y
 CONFIG_FEATURE_VI_WIN_RESIZE=y
-CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y
 CONFIG_FEATURE_ALLOW_EXEC=y
 
 #
@@ -803,6 +801,7 @@ CONFIG_WGET=y
 CONFIG_FEATURE_WGET_STATUSBAR=y
 CONFIG_FEATURE_WGET_AUTHENTICATION=y
 CONFIG_FEATURE_WGET_LONG_OPTIONS=y
+CONFIG_FEATURE_WGET_TIMEOUT=y
 CONFIG_ZCIP=y
 
 #
@@ -917,7 +916,6 @@ CONFIG_HUSH_FUNCTIONS=y
 CONFIG_HUSH_LOCAL=y
 CONFIG_HUSH_EXPORT_N=y
 CONFIG_HUSH_RANDOM_SUPPORT=y
-# CONFIG_LASH is not set
 CONFIG_MSH=y
 CONFIG_SH_MATH_SUPPORT=y
 CONFIG_SH_MATH_SUPPORT_64=y
similarity index 57%
rename from debian/config/pkg/udeb
rename to configs/android2_defconfig
index 4940749..4dfbdb5 100644 (file)
@@ -1,7 +1,7 @@
+# Run "make android2_defconfig", then "make".
 #
-# Automatically generated make config: don't edit
-# Busybox version: 1.17.1
-# Tue Nov  9 10:36:38 2010
+# Tested with the standalone toolchain from ndk r6:
+# android-ndk-r6/build/tools/make-standalone-toolchain.sh --platform=android-8
 #
 CONFIG_HAVE_DOT_CONFIG=y
 
@@ -16,31 +16,33 @@ CONFIG_HAVE_DOT_CONFIG=y
 # CONFIG_EXTRA_COMPAT is not set
 # CONFIG_INCLUDE_SUSv2 is not set
 # CONFIG_USE_PORTABLE_CODE is not set
+CONFIG_PLATFORM_LINUX=y
 CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
 # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
 # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
-CONFIG_SHOW_USAGE=y
+# CONFIG_SHOW_USAGE is not set
 # CONFIG_FEATURE_VERBOSE_USAGE is not set
-CONFIG_FEATURE_COMPRESS_USAGE=y
+# CONFIG_FEATURE_COMPRESS_USAGE is not set
 # CONFIG_FEATURE_INSTALLER is not set
+# CONFIG_INSTALL_NO_USR is not set
 # CONFIG_LOCALE_SUPPORT is not set
-CONFIG_UNICODE_SUPPORT=y
+# CONFIG_UNICODE_SUPPORT is not set
 # CONFIG_UNICODE_USING_LOCALE is not set
-CONFIG_FEATURE_CHECK_UNICODE_IN_ENV=y
-CONFIG_SUBST_WCHAR=63
-CONFIG_LAST_SUPPORTED_WCHAR=767
-CONFIG_UNICODE_COMBINING_WCHARS=y
-CONFIG_UNICODE_WIDE_WCHARS=y
+# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
+CONFIG_SUBST_WCHAR=0
+CONFIG_LAST_SUPPORTED_WCHAR=0
+# CONFIG_UNICODE_COMBINING_WCHARS is not set
+# CONFIG_UNICODE_WIDE_WCHARS is not set
 # CONFIG_UNICODE_BIDI_SUPPORT is not set
 # CONFIG_UNICODE_NEUTRAL_TABLE is not set
 # CONFIG_UNICODE_PRESERVE_BROKEN is not set
-CONFIG_LONG_OPTS=y
-CONFIG_FEATURE_DEVPTS=y
+# CONFIG_LONG_OPTS is not set
+# CONFIG_FEATURE_DEVPTS is not set
 # CONFIG_FEATURE_CLEAN_UP is not set
 # CONFIG_FEATURE_UTMP is not set
 # CONFIG_FEATURE_WTMP is not set
 # CONFIG_FEATURE_PIDFILE is not set
-CONFIG_FEATURE_SUID=y
+# CONFIG_FEATURE_SUID is not set
 # CONFIG_FEATURE_SUID_CONFIG is not set
 # CONFIG_FEATURE_SUID_CONFIG_QUIET is not set
 # CONFIG_SELINUX is not set
@@ -58,8 +60,8 @@ CONFIG_FEATURE_SYSLOG=y
 # CONFIG_BUILD_LIBBUSYBOX is not set
 # CONFIG_FEATURE_INDIVIDUAL is not set
 # CONFIG_FEATURE_SHARED_BUSYBOX is not set
-CONFIG_LFS=y
-CONFIG_CROSS_COMPILER_PREFIX=""
+# CONFIG_LFS is not set
+CONFIG_CROSS_COMPILER_PREFIX="arm-linux-androideabi-"
 CONFIG_EXTRA_CFLAGS=""
 
 #
@@ -73,9 +75,8 @@ CONFIG_NO_DEBUG_LIB=y
 # CONFIG_EFENCE is not set
 
 #
-# Installation Options
+# Installation Options ("make install" behavior)
 #
-# CONFIG_INSTALL_NO_USR is not set
 CONFIG_INSTALL_APPLET_SYMLINKS=y
 # CONFIG_INSTALL_APPLET_HARDLINKS is not set
 # CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
@@ -88,25 +89,29 @@ CONFIG_PREFIX="./_install"
 #
 # Busybox Library Tuning
 #
+# CONFIG_FEATURE_SYSTEMD is not set
+# CONFIG_FEATURE_RTMINMAX is not set
 CONFIG_PASSWORD_MINLEN=6
-CONFIG_MD5_SIZE_VS_SPEED=1
+CONFIG_MD5_SMALL=1
 # CONFIG_FEATURE_FAST_TOP is not set
 # CONFIG_FEATURE_ETC_NETWORKS is not set
-CONFIG_FEATURE_EDITING=y
-CONFIG_FEATURE_EDITING_MAX_LEN=1024
+CONFIG_FEATURE_USE_TERMIOS=y
+# CONFIG_FEATURE_EDITING is not set
+CONFIG_FEATURE_EDITING_MAX_LEN=0
 # CONFIG_FEATURE_EDITING_VI is not set
-CONFIG_FEATURE_EDITING_HISTORY=15
+CONFIG_FEATURE_EDITING_HISTORY=0
 # CONFIG_FEATURE_EDITING_SAVEHISTORY is not set
-CONFIG_FEATURE_TAB_COMPLETION=y
+# CONFIG_FEATURE_TAB_COMPLETION is not set
 # CONFIG_FEATURE_USERNAME_COMPLETION is not set
-CONFIG_FEATURE_EDITING_FANCY_PROMPT=y
-CONFIG_FEATURE_EDITING_ASK_TERMINAL=y
+# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set
+# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
 # CONFIG_FEATURE_NON_POSIX_CP is not set
-CONFIG_FEATURE_VERBOSE_CP_MESSAGE=y
+# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
 CONFIG_FEATURE_COPYBUF_KB=4
-CONFIG_MONOTONIC_SYSCALL=y
-CONFIG_IOCTL_HEX2STR_ERROR=y
-CONFIG_FEATURE_HWIB=y
+# CONFIG_FEATURE_SKIP_ROOTFS is not set
+# CONFIG_MONOTONIC_SYSCALL is not set
+# CONFIG_IOCTL_HEX2STR_ERROR is not set
+# CONFIG_FEATURE_HWIB is not set
 
 #
 # Applets
@@ -115,48 +120,48 @@ CONFIG_FEATURE_HWIB=y
 #
 # Archival Utilities
 #
-# CONFIG_FEATURE_SEAMLESS_XZ is not set
+CONFIG_FEATURE_SEAMLESS_XZ=y
 CONFIG_FEATURE_SEAMLESS_LZMA=y
 CONFIG_FEATURE_SEAMLESS_BZ2=y
 CONFIG_FEATURE_SEAMLESS_GZ=y
-# CONFIG_FEATURE_SEAMLESS_Z is not set
+CONFIG_FEATURE_SEAMLESS_Z=y
 CONFIG_AR=y
-# CONFIG_FEATURE_AR_LONG_FILENAMES is not set
-# CONFIG_FEATURE_AR_CREATE is not set
-# CONFIG_BUNZIP2 is not set
-# CONFIG_BZIP2 is not set
-# CONFIG_CPIO is not set
-# CONFIG_FEATURE_CPIO_O is not set
-# CONFIG_FEATURE_CPIO_P is not set
-# CONFIG_DPKG is not set
-# CONFIG_DPKG_DEB is not set
+CONFIG_FEATURE_AR_LONG_FILENAMES=y
+CONFIG_FEATURE_AR_CREATE=y
+CONFIG_BUNZIP2=y
+CONFIG_BZIP2=y
+CONFIG_CPIO=y
+CONFIG_FEATURE_CPIO_O=y
+CONFIG_FEATURE_CPIO_P=y
+CONFIG_DPKG=y
+CONFIG_DPKG_DEB=y
 # CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
 CONFIG_GUNZIP=y
-# CONFIG_GZIP is not set
+CONFIG_GZIP=y
 # CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set
-# CONFIG_LZOP is not set
-# CONFIG_LZOP_COMPR_HIGH is not set
-# CONFIG_RPM2CPIO is not set
-# CONFIG_RPM is not set
+CONFIG_LZOP=y
+CONFIG_LZOP_COMPR_HIGH=y
+CONFIG_RPM2CPIO=y
+CONFIG_RPM=y
 CONFIG_TAR=y
 CONFIG_FEATURE_TAR_CREATE=y
-# CONFIG_FEATURE_TAR_AUTODETECT is not set
-# CONFIG_FEATURE_TAR_FROM is not set
-# CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY is not set
-# CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set
+CONFIG_FEATURE_TAR_AUTODETECT=y
+CONFIG_FEATURE_TAR_FROM=y
+CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y
+CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y
 CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
 # CONFIG_FEATURE_TAR_LONG_OPTIONS is not set
 # CONFIG_FEATURE_TAR_TO_COMMAND is not set
-# CONFIG_FEATURE_TAR_UNAME_GNAME is not set
+CONFIG_FEATURE_TAR_UNAME_GNAME=y
 CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
 # CONFIG_FEATURE_TAR_SELINUX is not set
-# CONFIG_UNCOMPRESS is not set
-# CONFIG_UNLZMA is not set
-# CONFIG_FEATURE_LZMA_FAST is not set
-# CONFIG_LZMA is not set
-# CONFIG_UNXZ is not set
-# CONFIG_XZ is not set
-# CONFIG_UNZIP is not set
+CONFIG_UNCOMPRESS=y
+CONFIG_UNLZMA=y
+CONFIG_FEATURE_LZMA_FAST=y
+CONFIG_LZMA=y
+CONFIG_UNXZ=y
+CONFIG_XZ=y
+CONFIG_UNZIP=y
 
 #
 # Coreutils
@@ -167,58 +172,60 @@ CONFIG_CAT=y
 # CONFIG_FEATURE_DATE_ISOFMT is not set
 # CONFIG_FEATURE_DATE_NANO is not set
 # CONFIG_FEATURE_DATE_COMPAT is not set
+# CONFIG_ID is not set
+# CONFIG_GROUPS is not set
 CONFIG_TEST=y
 CONFIG_FEATURE_TEST_64=y
+CONFIG_TOUCH=y
 CONFIG_TR=y
-# CONFIG_FEATURE_TR_CLASSES is not set
-# CONFIG_FEATURE_TR_EQUIV is not set
-# CONFIG_CAL is not set
-# CONFIG_CATV is not set
-# CONFIG_CHGRP is not set
+CONFIG_FEATURE_TR_CLASSES=y
+CONFIG_FEATURE_TR_EQUIV=y
+CONFIG_BASE64=y
+CONFIG_CAL=y
+CONFIG_CATV=y
+CONFIG_CHGRP=y
 CONFIG_CHMOD=y
 CONFIG_CHOWN=y
-CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y
+# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set
 CONFIG_CHROOT=y
-# CONFIG_CKSUM is not set
-# CONFIG_COMM is not set
+CONFIG_CKSUM=y
+CONFIG_COMM=y
 CONFIG_CP=y
-CONFIG_FEATURE_CP_LONG_OPTIONS=y
+# CONFIG_FEATURE_CP_LONG_OPTIONS is not set
 CONFIG_CUT=y
 CONFIG_DD=y
 CONFIG_FEATURE_DD_SIGNAL_HANDLING=y
-# CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set
+CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y
 CONFIG_FEATURE_DD_IBS_OBS=y
-CONFIG_DF=y
+# CONFIG_DF is not set
 # CONFIG_FEATURE_DF_FANCY is not set
 CONFIG_DIRNAME=y
-# CONFIG_DOS2UNIX is not set
-# CONFIG_UNIX2DOS is not set
-# CONFIG_DU is not set
-# CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K is not set
+CONFIG_DOS2UNIX=y
+CONFIG_UNIX2DOS=y
+CONFIG_DU=y
+CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y
 CONFIG_ECHO=y
 CONFIG_FEATURE_FANCY_ECHO=y
-CONFIG_ENV=y
+# CONFIG_ENV is not set
 # CONFIG_FEATURE_ENV_LONG_OPTIONS is not set
-# CONFIG_EXPAND is not set
+CONFIG_EXPAND=y
 # CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set
-CONFIG_EXPR=y
-CONFIG_EXPR_MATH_SUPPORT_64=y
+# CONFIG_EXPR is not set
+# CONFIG_EXPR_MATH_SUPPORT_64 is not set
 CONFIG_FALSE=y
-# CONFIG_FOLD is not set
+CONFIG_FOLD=y
 # CONFIG_FSYNC is not set
 CONFIG_HEAD=y
 CONFIG_FEATURE_FANCY_HEAD=y
 # CONFIG_HOSTID is not set
-CONFIG_ID=y
-# CONFIG_INSTALL is not set
+CONFIG_INSTALL=y
 # CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set
-# CONFIG_LENGTH is not set
 CONFIG_LN=y
 # CONFIG_LOGNAME is not set
 CONFIG_LS=y
-# CONFIG_FEATURE_LS_FILETYPES is not set
+CONFIG_FEATURE_LS_FILETYPES=y
 CONFIG_FEATURE_LS_FOLLOWLINKS=y
-# CONFIG_FEATURE_LS_RECURSIVE is not set
+CONFIG_FEATURE_LS_RECURSIVE=y
 CONFIG_FEATURE_LS_SORTFILES=y
 CONFIG_FEATURE_LS_TIMESTAMPS=y
 CONFIG_FEATURE_LS_USERNAME=y
@@ -227,14 +234,14 @@ CONFIG_FEATURE_LS_USERNAME=y
 CONFIG_MD5SUM=y
 CONFIG_MKDIR=y
 # CONFIG_FEATURE_MKDIR_LONG_OPTIONS is not set
-# CONFIG_MKFIFO is not set
+CONFIG_MKFIFO=y
 CONFIG_MKNOD=y
 CONFIG_MV=y
 # CONFIG_FEATURE_MV_LONG_OPTIONS is not set
-# CONFIG_NICE is not set
-# CONFIG_NOHUP is not set
-# CONFIG_OD is not set
-# CONFIG_PRINTENV is not set
+CONFIG_NICE=y
+CONFIG_NOHUP=y
+CONFIG_OD=y
+CONFIG_PRINTENV=y
 CONFIG_PRINTF=y
 CONFIG_PWD=y
 CONFIG_READLINK=y
@@ -243,42 +250,41 @@ CONFIG_REALPATH=y
 CONFIG_RM=y
 CONFIG_RMDIR=y
 # CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set
-# CONFIG_SEQ is not set
-# CONFIG_SHA1SUM is not set
+CONFIG_SEQ=y
+CONFIG_SHA1SUM=y
 CONFIG_SHA256SUM=y
-# CONFIG_SHA512SUM is not set
+CONFIG_SHA512SUM=y
 CONFIG_SLEEP=y
-# CONFIG_FEATURE_FANCY_SLEEP is not set
-# CONFIG_FEATURE_FLOAT_SLEEP is not set
+CONFIG_FEATURE_FANCY_SLEEP=y
+CONFIG_FEATURE_FLOAT_SLEEP=y
 CONFIG_SORT=y
 CONFIG_FEATURE_SORT_BIG=y
-# CONFIG_SPLIT is not set
-# CONFIG_FEATURE_SPLIT_FANCY is not set
+CONFIG_SPLIT=y
+CONFIG_FEATURE_SPLIT_FANCY=y
 # CONFIG_STAT is not set
 # CONFIG_FEATURE_STAT_FORMAT is not set
-# CONFIG_STTY is not set
-# CONFIG_SUM is not set
+CONFIG_STTY=y
+CONFIG_SUM=y
 CONFIG_SYNC=y
-# CONFIG_TAC is not set
+CONFIG_TAC=y
 CONFIG_TAIL=y
 CONFIG_FEATURE_FANCY_TAIL=y
-# CONFIG_TEE is not set
-# CONFIG_FEATURE_TEE_USE_BLOCK_IO is not set
-CONFIG_TOUCH=y
+CONFIG_TEE=y
+CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
 CONFIG_TRUE=y
-CONFIG_TTY=y
+# CONFIG_TTY is not set
 CONFIG_UNAME=y
-# CONFIG_UNEXPAND is not set
+CONFIG_UNEXPAND=y
 # CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set
 CONFIG_UNIQ=y
 # CONFIG_USLEEP is not set
-# CONFIG_UUDECODE is not set
-# CONFIG_UUENCODE is not set
+CONFIG_UUDECODE=y
+CONFIG_UUENCODE=y
 CONFIG_WC=y
-# CONFIG_FEATURE_WC_LARGE is not set
+CONFIG_FEATURE_WC_LARGE=y
 # CONFIG_WHO is not set
-# CONFIG_WHOAMI is not set
-# CONFIG_YES is not set
+CONFIG_WHOAMI=y
+CONFIG_YES=y
 
 #
 # Common options for cp and mv
@@ -303,60 +309,53 @@ CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
 #
 # Console Utilities
 #
-# CONFIG_CHVT is not set
-# CONFIG_FGCONSOLE is not set
-# CONFIG_CLEAR is not set
-# CONFIG_DEALLOCVT is not set
-# CONFIG_DUMPKMAP is not set
+CONFIG_CHVT=y
+CONFIG_FGCONSOLE=y
+CONFIG_CLEAR=y
+CONFIG_DEALLOCVT=y
+CONFIG_DUMPKMAP=y
 # CONFIG_KBD_MODE is not set
 # CONFIG_LOADFONT is not set
-# CONFIG_LOADKMAP is not set
-# CONFIG_OPENVT is not set
-# CONFIG_RESET is not set
-# CONFIG_RESIZE is not set
-# CONFIG_FEATURE_RESIZE_PRINT is not set
-# CONFIG_SETCONSOLE is not set
+CONFIG_LOADKMAP=y
+CONFIG_OPENVT=y
+CONFIG_RESET=y
+CONFIG_RESIZE=y
+CONFIG_FEATURE_RESIZE_PRINT=y
+CONFIG_SETCONSOLE=y
 # CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
 # CONFIG_SETFONT is not set
 # CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
 CONFIG_DEFAULT_SETFONT_DIR=""
-# CONFIG_SETKEYCODES is not set
-# CONFIG_SETLOGCONS is not set
-# CONFIG_SHOWKEY is not set
+CONFIG_SETKEYCODES=y
+CONFIG_SETLOGCONS=y
+CONFIG_SHOWKEY=y
 # CONFIG_FEATURE_LOADFONT_PSF2 is not set
 # CONFIG_FEATURE_LOADFONT_RAW is not set
 
 #
 # Debian Utilities
 #
-# CONFIG_MKTEMP is not set
-# CONFIG_PIPE_PROGRESS is not set
-# CONFIG_RUN_PARTS is not set
+CONFIG_MKTEMP=y
+CONFIG_PIPE_PROGRESS=y
+CONFIG_RUN_PARTS=y
 # CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set
-# CONFIG_FEATURE_RUN_PARTS_FANCY is not set
-# CONFIG_START_STOP_DAEMON is not set
-# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set
+CONFIG_FEATURE_RUN_PARTS_FANCY=y
+CONFIG_START_STOP_DAEMON=y
+CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y
 # CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set
-# CONFIG_WHICH is not set
+CONFIG_WHICH=y
 
 #
 # Editors
 #
-# CONFIG_AWK is not set
-# CONFIG_FEATURE_AWK_LIBM is not set
-# CONFIG_CMP is not set
-# CONFIG_DIFF is not set
-# CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set
-# CONFIG_FEATURE_DIFF_DIR is not set
-# CONFIG_ED is not set
-# CONFIG_PATCH is not set
-CONFIG_SED=y
+CONFIG_PATCH=y
 # CONFIG_VI is not set
 CONFIG_FEATURE_VI_MAX_LEN=0
 # CONFIG_FEATURE_VI_8BIT is not set
 # CONFIG_FEATURE_VI_COLON is not set
 # CONFIG_FEATURE_VI_YANKMARK is not set
 # CONFIG_FEATURE_VI_SEARCH is not set
+# CONFIG_FEATURE_VI_REGEX_SEARCH is not set
 # CONFIG_FEATURE_VI_USE_SIGNALS is not set
 # CONFIG_FEATURE_VI_DOT_CMD is not set
 # CONFIG_FEATURE_VI_READONLY is not set
@@ -364,44 +363,51 @@ CONFIG_FEATURE_VI_MAX_LEN=0
 # CONFIG_FEATURE_VI_SET is not set
 # CONFIG_FEATURE_VI_WIN_RESIZE is not set
 # CONFIG_FEATURE_VI_ASK_TERMINAL is not set
-# CONFIG_FEATURE_VI_OPTIMIZE_CURSOR is not set
+# CONFIG_AWK is not set
+# CONFIG_FEATURE_AWK_LIBM is not set
+CONFIG_CMP=y
+CONFIG_DIFF=y
+# CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set
+CONFIG_FEATURE_DIFF_DIR=y
+# CONFIG_ED is not set
+# CONFIG_SED is not set
 # CONFIG_FEATURE_ALLOW_EXEC is not set
 
 #
 # Finding Utilities
 #
-CONFIG_FIND=y
-CONFIG_FEATURE_FIND_PRINT0=y
-CONFIG_FEATURE_FIND_MTIME=y
-CONFIG_FEATURE_FIND_MMIN=y
-CONFIG_FEATURE_FIND_PERM=y
-CONFIG_FEATURE_FIND_TYPE=y
-CONFIG_FEATURE_FIND_XDEV=y
-CONFIG_FEATURE_FIND_MAXDEPTH=y
-CONFIG_FEATURE_FIND_NEWER=y
-CONFIG_FEATURE_FIND_INUM=y
-CONFIG_FEATURE_FIND_EXEC=y
-CONFIG_FEATURE_FIND_USER=y
-CONFIG_FEATURE_FIND_GROUP=y
-CONFIG_FEATURE_FIND_NOT=y
-CONFIG_FEATURE_FIND_DEPTH=y
-CONFIG_FEATURE_FIND_PAREN=y
-CONFIG_FEATURE_FIND_SIZE=y
-CONFIG_FEATURE_FIND_PRUNE=y
+# CONFIG_FIND is not set
+# CONFIG_FEATURE_FIND_PRINT0 is not set
+# CONFIG_FEATURE_FIND_MTIME is not set
+# CONFIG_FEATURE_FIND_MMIN is not set
+# CONFIG_FEATURE_FIND_PERM is not set
+# CONFIG_FEATURE_FIND_TYPE is not set
+# CONFIG_FEATURE_FIND_XDEV is not set
+# CONFIG_FEATURE_FIND_MAXDEPTH is not set
+# CONFIG_FEATURE_FIND_NEWER is not set
+# CONFIG_FEATURE_FIND_INUM is not set
+# CONFIG_FEATURE_FIND_EXEC is not set
+# CONFIG_FEATURE_FIND_USER is not set
+# CONFIG_FEATURE_FIND_GROUP is not set
+# CONFIG_FEATURE_FIND_NOT is not set
+# CONFIG_FEATURE_FIND_DEPTH is not set
+# CONFIG_FEATURE_FIND_PAREN is not set
+# CONFIG_FEATURE_FIND_SIZE is not set
+# CONFIG_FEATURE_FIND_PRUNE is not set
 # CONFIG_FEATURE_FIND_DELETE is not set
-CONFIG_FEATURE_FIND_PATH=y
+# CONFIG_FEATURE_FIND_PATH is not set
 # CONFIG_FEATURE_FIND_REGEX is not set
 # CONFIG_FEATURE_FIND_CONTEXT is not set
-CONFIG_FEATURE_FIND_LINKS=y
-CONFIG_GREP=y
-CONFIG_FEATURE_GREP_EGREP_ALIAS=y
+# CONFIG_FEATURE_FIND_LINKS is not set
+# CONFIG_GREP is not set
+# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set
 # CONFIG_FEATURE_GREP_FGREP_ALIAS is not set
 # CONFIG_FEATURE_GREP_CONTEXT is not set
-# CONFIG_XARGS is not set
-# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set
-# CONFIG_FEATURE_XARGS_SUPPORT_QUOTES is not set
-# CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT is not set
-# CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM is not set
+CONFIG_XARGS=y
+CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
+CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
+CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
+CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
 
 #
 # Init Utilities
@@ -409,6 +415,9 @@ CONFIG_FEATURE_GREP_EGREP_ALIAS=y
 # CONFIG_BOOTCHARTD is not set
 # CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set
 # CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set
+CONFIG_HALT=y
+# CONFIG_FEATURE_CALL_TELINIT is not set
+CONFIG_TELINIT_PATH=""
 CONFIG_INIT=y
 CONFIG_FEATURE_USE_INITTAB=y
 # CONFIG_FEATURE_KILL_REMOVED is not set
@@ -416,33 +425,33 @@ CONFIG_FEATURE_KILL_DELAY=0
 CONFIG_FEATURE_INIT_SCTTY=y
 CONFIG_FEATURE_INIT_SYSLOG=y
 CONFIG_FEATURE_EXTRA_QUIET=y
-# CONFIG_FEATURE_INIT_COREDUMPS is not set
-# CONFIG_FEATURE_INITRD is not set
+CONFIG_FEATURE_INIT_COREDUMPS=y
+CONFIG_FEATURE_INITRD=y
 CONFIG_INIT_TERMINAL_TYPE="linux"
-CONFIG_HALT=y
-# CONFIG_FEATURE_CALL_TELINIT is not set
-CONFIG_TELINIT_PATH=""
-# CONFIG_MESG is not set
+CONFIG_MESG=y
+CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y
 
 #
 # Login/Password Management Utilities
 #
+# CONFIG_ADD_SHELL is not set
+# CONFIG_REMOVE_SHELL is not set
 # CONFIG_FEATURE_SHADOWPASSWDS is not set
-CONFIG_USE_BB_PWD_GRP=y
+# CONFIG_USE_BB_PWD_GRP is not set
 # CONFIG_USE_BB_SHADOW is not set
-CONFIG_USE_BB_CRYPT=y
+# CONFIG_USE_BB_CRYPT is not set
 # CONFIG_USE_BB_CRYPT_SHA is not set
-# CONFIG_ADDGROUP is not set
-# CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set
-# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
-# CONFIG_DELGROUP is not set
-# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
-# CONFIG_FEATURE_CHECK_NAMES is not set
 # CONFIG_ADDUSER is not set
 # CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set
+# CONFIG_FEATURE_CHECK_NAMES is not set
 CONFIG_FIRST_SYSTEM_ID=0
 CONFIG_LAST_SYSTEM_ID=0
+# CONFIG_ADDGROUP is not set
+# CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set
+# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
 # CONFIG_DELUSER is not set
+# CONFIG_DELGROUP is not set
+# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
 # CONFIG_GETTY is not set
 # CONFIG_LOGIN is not set
 # CONFIG_PAM is not set
@@ -462,18 +471,18 @@ CONFIG_LAST_SYSTEM_ID=0
 #
 # Linux Ext2 FS Progs
 #
-# CONFIG_CHATTR is not set
+CONFIG_CHATTR=y
 # CONFIG_FSCK is not set
-# CONFIG_LSATTR is not set
-# CONFIG_TUNE2FS is not set
+CONFIG_LSATTR=y
+CONFIG_TUNE2FS=y
 
 #
 # Linux Module Utilities
 #
 CONFIG_MODINFO=y
-# CONFIG_MODPROBE_SMALL is not set
-# CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set
-# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set
+CONFIG_MODPROBE_SMALL=y
+CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE=y
+CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y
 # CONFIG_INSMOD is not set
 # CONFIG_RMMOD is not set
 # CONFIG_LSMOD is not set
@@ -495,34 +504,36 @@ CONFIG_MODINFO=y
 # CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
 # CONFIG_FEATURE_MODUTILS_ALIAS is not set
 # CONFIG_FEATURE_MODUTILS_SYMBOLS is not set
-CONFIG_DEFAULT_MODULES_DIR=""
-CONFIG_DEFAULT_DEPMOD_FILE=""
+CONFIG_DEFAULT_MODULES_DIR="/lib/modules"
+CONFIG_DEFAULT_DEPMOD_FILE="modules.dep"
 
 #
 # Linux System Utilities
 #
 CONFIG_BLOCKDEV=y
-# CONFIG_REV is not set
+CONFIG_REV=y
 # CONFIG_ACPID is not set
 # CONFIG_FEATURE_ACPID_COMPAT is not set
-# CONFIG_BLKID is not set
+CONFIG_BLKID=y
+# CONFIG_FEATURE_BLKID_TYPE is not set
 CONFIG_DMESG=y
 CONFIG_FEATURE_DMESG_PRETTY=y
-# CONFIG_FBSET is not set
-# CONFIG_FEATURE_FBSET_FANCY is not set
-# CONFIG_FEATURE_FBSET_READMODE is not set
-# CONFIG_FDFLUSH is not set
-# CONFIG_FDFORMAT is not set
-# CONFIG_FDISK is not set
+CONFIG_FBSET=y
+CONFIG_FEATURE_FBSET_FANCY=y
+CONFIG_FEATURE_FBSET_READMODE=y
+CONFIG_FDFLUSH=y
+CONFIG_FDFORMAT=y
+CONFIG_FDISK=y
 CONFIG_FDISK_SUPPORT_LARGE_DISKS=y
-# CONFIG_FEATURE_FDISK_WRITABLE is not set
+CONFIG_FEATURE_FDISK_WRITABLE=y
 # CONFIG_FEATURE_AIX_LABEL is not set
 # CONFIG_FEATURE_SGI_LABEL is not set
 # CONFIG_FEATURE_SUN_LABEL is not set
 # CONFIG_FEATURE_OSF_LABEL is not set
-# CONFIG_FEATURE_FDISK_ADVANCED is not set
-# CONFIG_FINDFS is not set
-# CONFIG_FLOCK is not set
+# CONFIG_FEATURE_GPT_LABEL is not set
+CONFIG_FEATURE_FDISK_ADVANCED=y
+CONFIG_FINDFS=y
+CONFIG_FLOCK=y
 CONFIG_FREERAMDISK=y
 # CONFIG_FSCK_MINIX is not set
 # CONFIG_MKFS_EXT2 is not set
@@ -530,19 +541,19 @@ CONFIG_FREERAMDISK=y
 # CONFIG_FEATURE_MINIX2 is not set
 # CONFIG_MKFS_REISER is not set
 # CONFIG_MKFS_VFAT is not set
-# CONFIG_GETOPT is not set
-# CONFIG_FEATURE_GETOPT_LONG is not set
-# CONFIG_HEXDUMP is not set
-# CONFIG_FEATURE_HEXDUMP_REVERSE is not set
-# CONFIG_HD is not set
+CONFIG_GETOPT=y
+CONFIG_FEATURE_GETOPT_LONG=y
+CONFIG_HEXDUMP=y
+CONFIG_FEATURE_HEXDUMP_REVERSE=y
+CONFIG_HD=y
 # CONFIG_HWCLOCK is not set
 # CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set
 # CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set
 # CONFIG_IPCRM is not set
 # CONFIG_IPCS is not set
-# CONFIG_LOSETUP is not set
-# CONFIG_LSPCI is not set
-# CONFIG_LSUSB is not set
+CONFIG_LOSETUP=y
+CONFIG_LSPCI=y
+CONFIG_LSUSB=y
 # CONFIG_MDEV is not set
 # CONFIG_FEATURE_MDEV_CONF is not set
 # CONFIG_FEATURE_MDEV_RENAME is not set
@@ -552,97 +563,104 @@ CONFIG_FREERAMDISK=y
 CONFIG_MKSWAP=y
 CONFIG_FEATURE_MKSWAP_UUID=y
 CONFIG_MORE=y
-CONFIG_FEATURE_USE_TERMIOS=y
-CONFIG_MOUNT=y
+# CONFIG_MOUNT is not set
 # CONFIG_FEATURE_MOUNT_FAKE is not set
 # CONFIG_FEATURE_MOUNT_VERBOSE is not set
 # CONFIG_FEATURE_MOUNT_HELPERS is not set
 # CONFIG_FEATURE_MOUNT_LABEL is not set
 # CONFIG_FEATURE_MOUNT_NFS is not set
 # CONFIG_FEATURE_MOUNT_CIFS is not set
-CONFIG_FEATURE_MOUNT_FLAGS=y
-CONFIG_FEATURE_MOUNT_FSTAB=y
-CONFIG_PIVOT_ROOT=y
+# CONFIG_FEATURE_MOUNT_FLAGS is not set
+# CONFIG_FEATURE_MOUNT_FSTAB is not set
+# CONFIG_PIVOT_ROOT is not set
 # CONFIG_RDATE is not set
-# CONFIG_RDEV is not set
-# CONFIG_READPROFILE is not set
-# CONFIG_RTCWAKE is not set
-# CONFIG_SCRIPT is not set
-# CONFIG_SCRIPTREPLAY is not set
+CONFIG_RDEV=y
+CONFIG_READPROFILE=y
+CONFIG_RTCWAKE=y
+CONFIG_SCRIPT=y
+CONFIG_SCRIPTREPLAY=y
 # CONFIG_SETARCH is not set
-CONFIG_SWAPONOFF=y
+# CONFIG_SWAPONOFF is not set
 # CONFIG_FEATURE_SWAPON_PRI is not set
 # CONFIG_SWITCH_ROOT is not set
-CONFIG_UMOUNT=y
-CONFIG_FEATURE_UMOUNT_ALL=y
-
-#
-# Common options for mount/umount
-#
-CONFIG_FEATURE_MOUNT_LOOP=y
-CONFIG_FEATURE_MOUNT_LOOP_CREATE=y
+# CONFIG_UMOUNT is not set
+# CONFIG_FEATURE_UMOUNT_ALL is not set
+# CONFIG_FEATURE_MOUNT_LOOP is not set
+# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set
 # CONFIG_FEATURE_MTAB_SUPPORT is not set
-# CONFIG_VOLUMEID is not set
-# CONFIG_FEATURE_VOLUMEID_EXT is not set
-# CONFIG_FEATURE_VOLUMEID_BTRFS is not set
-# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
-# CONFIG_FEATURE_VOLUMEID_FAT is not set
-# CONFIG_FEATURE_VOLUMEID_HFS is not set
-# CONFIG_FEATURE_VOLUMEID_JFS is not set
-# CONFIG_FEATURE_VOLUMEID_XFS is not set
-# CONFIG_FEATURE_VOLUMEID_NTFS is not set
-# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set
-# CONFIG_FEATURE_VOLUMEID_UDF is not set
-# CONFIG_FEATURE_VOLUMEID_LUKS is not set
-# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set
-# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set
-# CONFIG_FEATURE_VOLUMEID_ROMFS is not set
-# CONFIG_FEATURE_VOLUMEID_SYSV is not set
-# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set
-# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set
+CONFIG_VOLUMEID=y
+
+#
+# Filesystem/Volume identification
+#
+CONFIG_FEATURE_VOLUMEID_EXT=y
+CONFIG_FEATURE_VOLUMEID_BTRFS=y
+CONFIG_FEATURE_VOLUMEID_REISERFS=y
+CONFIG_FEATURE_VOLUMEID_FAT=y
+CONFIG_FEATURE_VOLUMEID_HFS=y
+CONFIG_FEATURE_VOLUMEID_JFS=y
+CONFIG_FEATURE_VOLUMEID_XFS=y
+CONFIG_FEATURE_VOLUMEID_NTFS=y
+CONFIG_FEATURE_VOLUMEID_ISO9660=y
+CONFIG_FEATURE_VOLUMEID_UDF=y
+CONFIG_FEATURE_VOLUMEID_LUKS=y
+CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y
+CONFIG_FEATURE_VOLUMEID_CRAMFS=y
+CONFIG_FEATURE_VOLUMEID_ROMFS=y
+CONFIG_FEATURE_VOLUMEID_SYSV=y
+CONFIG_FEATURE_VOLUMEID_OCFS2=y
+CONFIG_FEATURE_VOLUMEID_LINUXRAID=y
 
 #
 # Miscellaneous Utilities
 #
 # CONFIG_CONSPY is not set
+# CONFIG_NANDWRITE is not set
+CONFIG_NANDDUMP=y
+CONFIG_SETSERIAL=y
 # CONFIG_UBIATTACH is not set
 # CONFIG_UBIDETACH is not set
+# CONFIG_UBIMKVOL is not set
+# CONFIG_UBIRMVOL is not set
+# CONFIG_UBIRSVOL is not set
+# CONFIG_UBIUPDATEVOL is not set
 # CONFIG_ADJTIMEX is not set
 # CONFIG_BBCONFIG is not set
-# CONFIG_BEEP is not set
-CONFIG_FEATURE_BEEP_FREQ=0
-CONFIG_FEATURE_BEEP_LENGTH_MS=0
-# CONFIG_CHAT is not set
-# CONFIG_FEATURE_CHAT_NOFAIL is not set
+# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
+CONFIG_BEEP=y
+CONFIG_FEATURE_BEEP_FREQ=4000
+CONFIG_FEATURE_BEEP_LENGTH_MS=30
+CONFIG_CHAT=y
+CONFIG_FEATURE_CHAT_NOFAIL=y
 # CONFIG_FEATURE_CHAT_TTY_HIFI is not set
-# CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set
-# CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set
-# CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set
-# CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set
-# CONFIG_FEATURE_CHAT_CLR_ABORT is not set
-# CONFIG_CHRT is not set
+CONFIG_FEATURE_CHAT_IMPLICIT_CR=y
+CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y
+CONFIG_FEATURE_CHAT_SEND_ESCAPES=y
+CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y
+CONFIG_FEATURE_CHAT_CLR_ABORT=y
+CONFIG_CHRT=y
 # CONFIG_CROND is not set
 # CONFIG_FEATURE_CROND_D is not set
 # CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
 CONFIG_FEATURE_CROND_DIR=""
 # CONFIG_CRONTAB is not set
-# CONFIG_DC is not set
-# CONFIG_FEATURE_DC_LIBM is not set
+CONFIG_DC=y
+CONFIG_FEATURE_DC_LIBM=y
 # CONFIG_DEVFSD is not set
 # CONFIG_DEVFSD_MODLOAD is not set
 # CONFIG_DEVFSD_FG_NP is not set
 # CONFIG_DEVFSD_VERBOSE is not set
 # CONFIG_FEATURE_DEVFS is not set
-# CONFIG_DEVMEM is not set
+CONFIG_DEVMEM=y
 # CONFIG_EJECT is not set
 # CONFIG_FEATURE_EJECT_SCSI is not set
-# CONFIG_FBSPLASH is not set
-# CONFIG_FLASHCP is not set
-# CONFIG_FLASH_LOCK is not set
-# CONFIG_FLASH_UNLOCK is not set
+CONFIG_FBSPLASH=y
+CONFIG_FLASHCP=y
+CONFIG_FLASH_LOCK=y
+CONFIG_FLASH_UNLOCK=y
 # CONFIG_FLASH_ERASEALL is not set
 # CONFIG_IONICE is not set
-# CONFIG_INOTIFYD is not set
+CONFIG_INOTIFYD=y
 # CONFIG_LAST is not set
 # CONFIG_FEATURE_LAST_SMALL is not set
 # CONFIG_FEATURE_LAST_FANCY is not set
@@ -655,90 +673,98 @@ CONFIG_FEATURE_LESS_MAXLINES=0
 # CONFIG_FEATURE_LESS_WINCH is not set
 # CONFIG_FEATURE_LESS_DASHCMD is not set
 # CONFIG_FEATURE_LESS_LINENUMS is not set
-# CONFIG_HDPARM is not set
-# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set
-# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set
-# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set
-# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set
-# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set
-# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set
-# CONFIG_MAKEDEVS is not set
+CONFIG_HDPARM=y
+CONFIG_FEATURE_HDPARM_GET_IDENTITY=y
+CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y
+CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y
+CONFIG_MAKEDEVS=y
 # CONFIG_FEATURE_MAKEDEVS_LEAF is not set
-# CONFIG_FEATURE_MAKEDEVS_TABLE is not set
-# CONFIG_MAN is not set
+CONFIG_FEATURE_MAKEDEVS_TABLE=y
+CONFIG_MAN=y
 # CONFIG_MICROCOM is not set
 # CONFIG_MOUNTPOINT is not set
 # CONFIG_MT is not set
-# CONFIG_RAIDAUTORUN is not set
+CONFIG_RAIDAUTORUN=y
 # CONFIG_READAHEAD is not set
 # CONFIG_RFKILL is not set
 # CONFIG_RUNLEVEL is not set
-# CONFIG_RX is not set
-# CONFIG_SETSID is not set
-# CONFIG_STRINGS is not set
+CONFIG_RX=y
+CONFIG_SETSID=y
+CONFIG_STRINGS=y
 # CONFIG_TASKSET is not set
 # CONFIG_FEATURE_TASKSET_FANCY is not set
-# CONFIG_TIME is not set
-# CONFIG_TIMEOUT is not set
-# CONFIG_TTYSIZE is not set
-# CONFIG_VOLNAME is not set
+CONFIG_TIME=y
+CONFIG_TIMEOUT=y
+CONFIG_TTYSIZE=y
+CONFIG_VOLNAME=y
 # CONFIG_WALL is not set
 # CONFIG_WATCHDOG is not set
 
 #
 # Networking Utilities
 #
+# CONFIG_NAMEIF is not set
+# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
+CONFIG_NBDCLIENT=y
 CONFIG_NC=y
 CONFIG_NC_SERVER=y
 CONFIG_NC_EXTRA=y
 # CONFIG_NC_110_COMPAT is not set
-CONFIG_FEATURE_IPV6=y
+# CONFIG_PING is not set
+# CONFIG_PING6 is not set
+# CONFIG_FEATURE_FANCY_PING is not set
+CONFIG_WHOIS=y
+# CONFIG_FEATURE_IPV6 is not set
 # CONFIG_FEATURE_UNIX_LOCAL is not set
 # CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set
 # CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
-# CONFIG_ARP is not set
-CONFIG_ARPING=y
+CONFIG_ARP=y
+# CONFIG_ARPING is not set
 # CONFIG_BRCTL is not set
 # CONFIG_FEATURE_BRCTL_FANCY is not set
 # CONFIG_FEATURE_BRCTL_SHOW is not set
-# CONFIG_DNSD is not set
+CONFIG_DNSD=y
 # CONFIG_ETHER_WAKE is not set
-# CONFIG_FAKEIDENTD is not set
-# CONFIG_FTPD is not set
-# CONFIG_FEATURE_FTP_WRITE is not set
-# CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set
-# CONFIG_FTPGET is not set
-# CONFIG_FTPPUT is not set
+CONFIG_FAKEIDENTD=y
+CONFIG_FTPD=y
+CONFIG_FEATURE_FTP_WRITE=y
+CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y
+CONFIG_FTPGET=y
+CONFIG_FTPPUT=y
 # CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set
-CONFIG_HOSTNAME=y
-# CONFIG_HTTPD is not set
-# CONFIG_FEATURE_HTTPD_RANGES is not set
-# CONFIG_FEATURE_HTTPD_USE_SENDFILE is not set
-# CONFIG_FEATURE_HTTPD_SETUID is not set
-# CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set
+# CONFIG_HOSTNAME is not set
+CONFIG_HTTPD=y
+CONFIG_FEATURE_HTTPD_RANGES=y
+CONFIG_FEATURE_HTTPD_USE_SENDFILE=y
+CONFIG_FEATURE_HTTPD_SETUID=y
+CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
 # CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
-# CONFIG_FEATURE_HTTPD_CGI is not set
-# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
-# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
-# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set
-# CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set
-# CONFIG_FEATURE_HTTPD_PROXY is not set
-# CONFIG_IFCONFIG is not set
-# CONFIG_FEATURE_IFCONFIG_STATUS is not set
+CONFIG_FEATURE_HTTPD_CGI=y
+CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y
+CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y
+CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
+CONFIG_FEATURE_HTTPD_ERROR_PAGES=y
+CONFIG_FEATURE_HTTPD_PROXY=y
+CONFIG_FEATURE_HTTPD_GZIP=y
+CONFIG_IFCONFIG=y
+CONFIG_FEATURE_IFCONFIG_STATUS=y
 # CONFIG_FEATURE_IFCONFIG_SLIP is not set
-# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
-# CONFIG_FEATURE_IFCONFIG_HW is not set
-# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
+CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y
+CONFIG_FEATURE_IFCONFIG_HW=y
+CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y
 # CONFIG_IFENSLAVE is not set
 # CONFIG_IFPLUGD is not set
-# CONFIG_IFUPDOWN is not set
-CONFIG_IFUPDOWN_IFSTATE_PATH=""
-# CONFIG_FEATURE_IFUPDOWN_IP is not set
-# CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN is not set
+CONFIG_IFUPDOWN=y
+CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate"
+CONFIG_FEATURE_IFUPDOWN_IP=y
+CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y
 # CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set
-# CONFIG_FEATURE_IFUPDOWN_IPV4 is not set
+CONFIG_FEATURE_IFUPDOWN_IPV4=y
 # CONFIG_FEATURE_IFUPDOWN_IPV6 is not set
-# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set
+CONFIG_FEATURE_IFUPDOWN_MAPPING=y
 # CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set
 # CONFIG_INETD is not set
 # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set
@@ -751,47 +777,38 @@ CONFIG_IP=y
 CONFIG_FEATURE_IP_ADDRESS=y
 CONFIG_FEATURE_IP_LINK=y
 CONFIG_FEATURE_IP_ROUTE=y
-# CONFIG_FEATURE_IP_TUNNEL is not set
-# CONFIG_FEATURE_IP_RULE is not set
-# CONFIG_FEATURE_IP_SHORT_FORMS is not set
+CONFIG_FEATURE_IP_TUNNEL=y
+CONFIG_FEATURE_IP_RULE=y
+CONFIG_FEATURE_IP_SHORT_FORMS=y
 # CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
-# CONFIG_IPADDR is not set
-# CONFIG_IPLINK is not set
-# CONFIG_IPROUTE is not set
-# CONFIG_IPTUNNEL is not set
-# CONFIG_IPRULE is not set
-# CONFIG_IPCALC is not set
-# CONFIG_FEATURE_IPCALC_FANCY is not set
+CONFIG_IPADDR=y
+CONFIG_IPLINK=y
+CONFIG_IPROUTE=y
+CONFIG_IPTUNNEL=y
+CONFIG_IPRULE=y
+CONFIG_IPCALC=y
+CONFIG_FEATURE_IPCALC_FANCY=y
 # CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set
-# CONFIG_NAMEIF is not set
-# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
-# CONFIG_NETSTAT is not set
-# CONFIG_FEATURE_NETSTAT_WIDE is not set
-# CONFIG_FEATURE_NETSTAT_PRG is not set
+CONFIG_NETSTAT=y
+CONFIG_FEATURE_NETSTAT_WIDE=y
+CONFIG_FEATURE_NETSTAT_PRG=y
 # CONFIG_NSLOOKUP is not set
 # CONFIG_NTPD is not set
 # CONFIG_FEATURE_NTPD_SERVER is not set
-# CONFIG_PING is not set
-# CONFIG_PING6 is not set
-# CONFIG_FEATURE_FANCY_PING is not set
-# CONFIG_PSCAN is not set
+CONFIG_PSCAN=y
 CONFIG_ROUTE=y
 # CONFIG_SLATTACH is not set
-# CONFIG_TCPSVD is not set
+CONFIG_TCPSVD=y
 # CONFIG_TELNET is not set
 # CONFIG_FEATURE_TELNET_TTYPE is not set
 # CONFIG_FEATURE_TELNET_AUTOLOGIN is not set
 # CONFIG_TELNETD is not set
 # CONFIG_FEATURE_TELNETD_STANDALONE is not set
 # CONFIG_FEATURE_TELNETD_INETD_WAIT is not set
-CONFIG_TFTP=y
+# CONFIG_TFTP is not set
 # CONFIG_TFTPD is not set
-
-#
-# Common options for tftp/tftpd
-#
-CONFIG_FEATURE_TFTP_GET=y
-CONFIG_FEATURE_TFTP_PUT=y
+# CONFIG_FEATURE_TFTP_GET is not set
+# CONFIG_FEATURE_TFTP_PUT is not set
 # CONFIG_FEATURE_TFTP_BLOCKSIZE is not set
 # CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set
 # CONFIG_TFTP_DEBUG is not set
@@ -800,94 +817,103 @@ CONFIG_FEATURE_TFTP_PUT=y
 # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
 # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
 # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
-# CONFIG_TUNCTL is not set
-# CONFIG_FEATURE_TUNCTL_UG is not set
+CONFIG_TUNCTL=y
+CONFIG_FEATURE_TUNCTL_UG=y
 # CONFIG_UDHCPD is not set
 # CONFIG_DHCPRELAY is not set
 # CONFIG_DUMPLEASES is not set
 # CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
-CONFIG_DHCPD_LEASES_FILE="/var/lib/misc/udhcpd.leases"
+# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set
+CONFIG_DHCPD_LEASES_FILE=""
 CONFIG_UDHCPC=y
 CONFIG_FEATURE_UDHCPC_ARPING=y
 # CONFIG_FEATURE_UDHCP_PORT is not set
-CONFIG_UDHCP_DEBUG=0
+CONFIG_UDHCP_DEBUG=9
 CONFIG_FEATURE_UDHCP_RFC3397=y
+CONFIG_FEATURE_UDHCP_8021Q=y
 CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script"
 CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80
-CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS=""
+CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n"
 # CONFIG_UDPSVD is not set
 # CONFIG_VCONFIG is not set
 CONFIG_WGET=y
 CONFIG_FEATURE_WGET_STATUSBAR=y
 CONFIG_FEATURE_WGET_AUTHENTICATION=y
 # CONFIG_FEATURE_WGET_LONG_OPTIONS is not set
+CONFIG_FEATURE_WGET_TIMEOUT=y
 # CONFIG_ZCIP is not set
 
 #
 # Print Utilities
 #
-# CONFIG_LPD is not set
-# CONFIG_LPR is not set
-# CONFIG_LPQ is not set
+CONFIG_LPD=y
+CONFIG_LPR=y
+CONFIG_LPQ=y
 
 #
 # Mail Utilities
 #
-# CONFIG_MAKEMIME is not set
-CONFIG_FEATURE_MIME_CHARSET=""
-# CONFIG_POPMAILDIR is not set
-# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set
-# CONFIG_REFORMIME is not set
-# CONFIG_FEATURE_REFORMIME_COMPAT is not set
-# CONFIG_SENDMAIL is not set
+CONFIG_MAKEMIME=y
+CONFIG_FEATURE_MIME_CHARSET="us-ascii"
+CONFIG_POPMAILDIR=y
+CONFIG_FEATURE_POPMAILDIR_DELIVERY=y
+CONFIG_REFORMIME=y
+CONFIG_FEATURE_REFORMIME_COMPAT=y
+CONFIG_SENDMAIL=y
 
 #
 # Process Utilities
 #
-# CONFIG_SMEMCAP is not set
-CONFIG_FREE=y
-# CONFIG_FUSER is not set
-CONFIG_KILL=y
+CONFIG_IOSTAT=y
+CONFIG_MPSTAT=y
+CONFIG_NMETER=y
+CONFIG_PMAP=y
+CONFIG_POWERTOP=y
+CONFIG_PSTREE=y
+CONFIG_PWDX=y
+CONFIG_SMEMCAP=y
+# CONFIG_FREE is not set
+CONFIG_FUSER=y
+# CONFIG_KILL is not set
 # CONFIG_KILLALL is not set
 # CONFIG_KILLALL5 is not set
-# CONFIG_NMETER is not set
 # CONFIG_PGREP is not set
 CONFIG_PIDOF=y
-# CONFIG_FEATURE_PIDOF_SINGLE is not set
-# CONFIG_FEATURE_PIDOF_OMIT is not set
+CONFIG_FEATURE_PIDOF_SINGLE=y
+CONFIG_FEATURE_PIDOF_OMIT=y
 # CONFIG_PKILL is not set
-CONFIG_PS=y
+# CONFIG_PS is not set
 # CONFIG_FEATURE_PS_WIDE is not set
 # CONFIG_FEATURE_PS_TIME is not set
 # CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set
 # CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
-# CONFIG_RENICE is not set
-# CONFIG_BB_SYSCTL is not set
-# CONFIG_TOP is not set
-# CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set
-# CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set
-# CONFIG_FEATURE_TOP_SMP_CPU is not set
-# CONFIG_FEATURE_TOP_DECIMALS is not set
-# CONFIG_FEATURE_TOP_SMP_PROCESS is not set
-# CONFIG_FEATURE_TOPMEM is not set
+CONFIG_RENICE=y
+CONFIG_BB_SYSCTL=y
+CONFIG_TOP=y
+CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y
+CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y
+CONFIG_FEATURE_TOP_SMP_CPU=y
+CONFIG_FEATURE_TOP_DECIMALS=y
+CONFIG_FEATURE_TOP_SMP_PROCESS=y
+CONFIG_FEATURE_TOPMEM=y
 CONFIG_FEATURE_SHOW_THREADS=y
 # CONFIG_UPTIME is not set
-# CONFIG_WATCH is not set
+CONFIG_WATCH=y
 
 #
 # Runit Utilities
 #
-# CONFIG_RUNSV is not set
-# CONFIG_RUNSVDIR is not set
+CONFIG_RUNSV=y
+CONFIG_RUNSVDIR=y
 # CONFIG_FEATURE_RUNSVDIR_LOG is not set
-# CONFIG_SV is not set
-CONFIG_SV_DEFAULT_SERVICE_DIR=""
-# CONFIG_SVLOGD is not set
-# CONFIG_CHPST is not set
-# CONFIG_SETUIDGID is not set
-# CONFIG_ENVUIDGID is not set
-# CONFIG_ENVDIR is not set
-# CONFIG_SOFTLIMIT is not set
+CONFIG_SV=y
+CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service"
+CONFIG_SVLOGD=y
+CONFIG_CHPST=y
+CONFIG_SETUIDGID=y
+CONFIG_ENVUIDGID=y
+CONFIG_ENVDIR=y
+CONFIG_SOFTLIMIT=y
 # CONFIG_CHCON is not set
 # CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set
 # CONFIG_GETENFORCE is not set
@@ -907,23 +933,27 @@ CONFIG_SV_DEFAULT_SERVICE_DIR=""
 #
 # Shells
 #
-CONFIG_ASH=y
+# CONFIG_ASH is not set
 # CONFIG_ASH_BASH_COMPAT is not set
-CONFIG_ASH_JOB_CONTROL=y
+# CONFIG_ASH_IDLE_TIMEOUT is not set
+# CONFIG_ASH_JOB_CONTROL is not set
 # CONFIG_ASH_ALIAS is not set
-CONFIG_ASH_GETOPTS=y
-CONFIG_ASH_BUILTIN_ECHO=y
-CONFIG_ASH_BUILTIN_PRINTF=y
-CONFIG_ASH_BUILTIN_TEST=y
-CONFIG_ASH_CMDCMD=y
+# CONFIG_ASH_GETOPTS is not set
+# CONFIG_ASH_BUILTIN_ECHO is not set
+# CONFIG_ASH_BUILTIN_PRINTF is not set
+# CONFIG_ASH_BUILTIN_TEST is not set
+# CONFIG_ASH_CMDCMD is not set
 # CONFIG_ASH_MAIL is not set
-CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
+# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set
 # CONFIG_ASH_RANDOM_SUPPORT is not set
 # CONFIG_ASH_EXPAND_PRMT is not set
+CONFIG_CTTYHACK=y
 # CONFIG_HUSH is not set
 # CONFIG_HUSH_BASH_COMPAT is not set
+# CONFIG_HUSH_BRACE_EXPANSION is not set
 # CONFIG_HUSH_HELP is not set
 # CONFIG_HUSH_INTERACTIVE is not set
+# CONFIG_HUSH_SAVEHISTORY is not set
 # CONFIG_HUSH_JOB is not set
 # CONFIG_HUSH_TICK is not set
 # CONFIG_HUSH_IF is not set
@@ -931,35 +961,36 @@ CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
 # CONFIG_HUSH_CASE is not set
 # CONFIG_HUSH_FUNCTIONS is not set
 # CONFIG_HUSH_LOCAL is not set
-# CONFIG_HUSH_EXPORT_N is not set
 # CONFIG_HUSH_RANDOM_SUPPORT is not set
-CONFIG_FEATURE_SH_IS_ASH=y
+# CONFIG_HUSH_EXPORT_N is not set
+# CONFIG_HUSH_MODE_X is not set
+# CONFIG_MSH is not set
+# CONFIG_FEATURE_SH_IS_ASH is not set
 # CONFIG_FEATURE_SH_IS_HUSH is not set
-# CONFIG_FEATURE_SH_IS_NONE is not set
+CONFIG_FEATURE_SH_IS_NONE=y
 # CONFIG_FEATURE_BASH_IS_ASH is not set
 # CONFIG_FEATURE_BASH_IS_HUSH is not set
 CONFIG_FEATURE_BASH_IS_NONE=y
-# CONFIG_LASH is not set
-# CONFIG_MSH is not set
-CONFIG_SH_MATH_SUPPORT=y
-CONFIG_SH_MATH_SUPPORT_64=y
+# CONFIG_SH_MATH_SUPPORT is not set
+# CONFIG_SH_MATH_SUPPORT_64 is not set
 # CONFIG_FEATURE_SH_EXTRA_QUIET is not set
 # CONFIG_FEATURE_SH_STANDALONE is not set
 # CONFIG_FEATURE_SH_NOFORK is not set
-# CONFIG_CTTYHACK is not set
+# CONFIG_FEATURE_SH_HISTFILESIZE is not set
 
 #
 # System Logging Utilities
 #
-CONFIG_SYSLOGD=y
+# CONFIG_SYSLOGD is not set
 # CONFIG_FEATURE_ROTATE_LOGFILE is not set
 # CONFIG_FEATURE_REMOTE_LOG is not set
 # CONFIG_FEATURE_SYSLOGD_DUP is not set
-CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256
+# CONFIG_FEATURE_SYSLOGD_CFG is not set
+CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0
 # CONFIG_FEATURE_IPC_SYSLOG is not set
 CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0
 # CONFIG_LOGREAD is not set
 # CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
 CONFIG_KLOGD=y
 CONFIG_FEATURE_KLOGD_KLOGCTL=y
-CONFIG_LOGGER=y
+# CONFIG_LOGGER is not set
diff --git a/configs/android_defconfig b/configs/android_defconfig
new file mode 100644 (file)
index 0000000..e35830e
--- /dev/null
@@ -0,0 +1,1028 @@
+#
+# Automatically generated make config: don't edit
+# Busybox version: 1.20.0.git
+# Sun Nov  6 07:51:38 2011
+#
+CONFIG_HAVE_DOT_CONFIG=y
+
+#
+# Busybox Settings
+#
+
+#
+# General Configuration
+#
+CONFIG_DESKTOP=y
+# CONFIG_EXTRA_COMPAT is not set
+# CONFIG_INCLUDE_SUSv2 is not set
+# CONFIG_USE_PORTABLE_CODE is not set
+CONFIG_PLATFORM_LINUX=y
+CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
+# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
+# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
+# CONFIG_SHOW_USAGE is not set
+# CONFIG_FEATURE_VERBOSE_USAGE is not set
+# CONFIG_FEATURE_COMPRESS_USAGE is not set
+# CONFIG_FEATURE_INSTALLER is not set
+# CONFIG_INSTALL_NO_USR is not set
+# CONFIG_LOCALE_SUPPORT is not set
+# CONFIG_UNICODE_SUPPORT is not set
+# CONFIG_UNICODE_USING_LOCALE is not set
+# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
+CONFIG_SUBST_WCHAR=0
+CONFIG_LAST_SUPPORTED_WCHAR=0
+# CONFIG_UNICODE_COMBINING_WCHARS is not set
+# CONFIG_UNICODE_WIDE_WCHARS is not set
+# CONFIG_UNICODE_BIDI_SUPPORT is not set
+# CONFIG_UNICODE_NEUTRAL_TABLE is not set
+# CONFIG_UNICODE_PRESERVE_BROKEN is not set
+# CONFIG_LONG_OPTS is not set
+# CONFIG_FEATURE_DEVPTS is not set
+# CONFIG_FEATURE_CLEAN_UP is not set
+# CONFIG_FEATURE_UTMP is not set
+# CONFIG_FEATURE_WTMP is not set
+# CONFIG_FEATURE_PIDFILE is not set
+# CONFIG_FEATURE_SUID is not set
+# CONFIG_FEATURE_SUID_CONFIG is not set
+# CONFIG_FEATURE_SUID_CONFIG_QUIET is not set
+# CONFIG_SELINUX is not set
+# CONFIG_FEATURE_PREFER_APPLETS is not set
+CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
+CONFIG_FEATURE_SYSLOG=y
+# CONFIG_FEATURE_HAVE_RPC is not set
+
+#
+# Build Options
+#
+# CONFIG_STATIC is not set
+# CONFIG_PIE is not set
+# CONFIG_NOMMU is not set
+# CONFIG_BUILD_LIBBUSYBOX is not set
+# CONFIG_FEATURE_INDIVIDUAL is not set
+# CONFIG_FEATURE_SHARED_BUSYBOX is not set
+# CONFIG_LFS is not set
+CONFIG_CROSS_COMPILER_PREFIX="arm-eabi-"
+#
+# Removed:
+# warning flags:
+# -Wno-multichar -W -Wall -Wno-unused -Winit-self -Wpointer-arith
+# -Werror=return-type -Werror=non-virtual-dtor -Werror=address
+# -Werror=sequence-point -Wstrict-aliasing=2 -Wno-undef -Wno-shadow
+# bbox already adds these:
+# -ffunction-sections -fomit-frame-pointer
+# enabled implicitly by -Os:
+# -frerun-cse-after-loop
+# should be not needed, or even increases code size:
+# -finline-functions -fno-inline-functions-called-once -finline-limit=64
+# -fstack-protector -fno-strict-aliasing -fno-exceptions -funwind-tables
+# -fmessage-length=0 (only affects error message format)
+# todo: do we need these? -
+# -fno-short-enums
+# -fgcse-after-reload
+# -frename-registers
+CONFIG_EXTRA_CFLAGS="-I$A/system/core/include -I$A/bionic/libc/arch-arm/include -I$A/bionic/libc/include -I$A/bionic/libc/kernel/common -I$A/bionic/libc/kernel/arch-arm -I$A/bionic/libm/include -I$A/bionic/libm/include/arch/arm -include $A/system/core/include/arch/linux-arm/AndroidConfig.h -I$A/system/core/include/arch/linux-arm/ -D__ANDROID__ -DSK_RELEASE -nostdlib -march=armv7-a -msoft-float -mfloat-abi=softfp -mfpu=neon -mthumb -mthumb-interwork -fpic -fno-short-enums -fgcse-after-reload -frename-registers"
+
+#
+# Debugging Options
+#
+# CONFIG_DEBUG is not set
+# CONFIG_DEBUG_PESSIMIZE is not set
+# CONFIG_WERROR is not set
+CONFIG_NO_DEBUG_LIB=y
+# CONFIG_DMALLOC is not set
+# CONFIG_EFENCE is not set
+
+#
+# Installation Options ("make install" behavior)
+#
+CONFIG_INSTALL_APPLET_SYMLINKS=y
+# CONFIG_INSTALL_APPLET_HARDLINKS is not set
+# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
+# CONFIG_INSTALL_APPLET_DONT is not set
+# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
+# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
+# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
+CONFIG_PREFIX="./_install"
+
+#
+# Busybox Library Tuning
+#
+# CONFIG_FEATURE_SYSTEMD is not set
+# CONFIG_FEATURE_RTMINMAX is not set
+CONFIG_PASSWORD_MINLEN=6
+CONFIG_MD5_SMALL=1
+# CONFIG_FEATURE_FAST_TOP is not set
+# CONFIG_FEATURE_ETC_NETWORKS is not set
+CONFIG_FEATURE_USE_TERMIOS=y
+# CONFIG_FEATURE_EDITING is not set
+CONFIG_FEATURE_EDITING_MAX_LEN=0
+# CONFIG_FEATURE_EDITING_VI is not set
+CONFIG_FEATURE_EDITING_HISTORY=0
+# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set
+# CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set
+# CONFIG_FEATURE_REVERSE_SEARCH is not set
+# CONFIG_FEATURE_TAB_COMPLETION is not set
+# CONFIG_FEATURE_USERNAME_COMPLETION is not set
+# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set
+# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
+# CONFIG_FEATURE_NON_POSIX_CP is not set
+# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
+CONFIG_FEATURE_COPYBUF_KB=4
+# CONFIG_FEATURE_SKIP_ROOTFS is not set
+# CONFIG_MONOTONIC_SYSCALL is not set
+# CONFIG_IOCTL_HEX2STR_ERROR is not set
+# CONFIG_FEATURE_HWIB is not set
+
+#
+# Applets
+#
+
+#
+# Archival Utilities
+#
+CONFIG_FEATURE_SEAMLESS_XZ=y
+CONFIG_FEATURE_SEAMLESS_LZMA=y
+CONFIG_FEATURE_SEAMLESS_BZ2=y
+CONFIG_FEATURE_SEAMLESS_GZ=y
+CONFIG_FEATURE_SEAMLESS_Z=y
+CONFIG_AR=y
+CONFIG_FEATURE_AR_LONG_FILENAMES=y
+CONFIG_FEATURE_AR_CREATE=y
+CONFIG_BUNZIP2=y
+CONFIG_BZIP2=y
+CONFIG_CPIO=y
+CONFIG_FEATURE_CPIO_O=y
+CONFIG_FEATURE_CPIO_P=y
+CONFIG_DPKG=y
+CONFIG_DPKG_DEB=y
+# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
+CONFIG_GUNZIP=y
+CONFIG_GZIP=y
+# CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set
+CONFIG_GZIP_FAST=0
+CONFIG_LZOP=y
+CONFIG_LZOP_COMPR_HIGH=y
+CONFIG_RPM2CPIO=y
+CONFIG_RPM=y
+CONFIG_TAR=y
+CONFIG_FEATURE_TAR_CREATE=y
+CONFIG_FEATURE_TAR_AUTODETECT=y
+CONFIG_FEATURE_TAR_FROM=y
+CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y
+CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y
+CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
+# CONFIG_FEATURE_TAR_LONG_OPTIONS is not set
+# CONFIG_FEATURE_TAR_TO_COMMAND is not set
+CONFIG_FEATURE_TAR_UNAME_GNAME=y
+CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
+# CONFIG_FEATURE_TAR_SELINUX is not set
+CONFIG_UNCOMPRESS=y
+CONFIG_UNLZMA=y
+CONFIG_FEATURE_LZMA_FAST=y
+CONFIG_LZMA=y
+CONFIG_UNXZ=y
+CONFIG_XZ=y
+CONFIG_UNZIP=y
+
+#
+# Coreutils
+#
+CONFIG_BASENAME=y
+CONFIG_CAT=y
+# CONFIG_DATE is not set
+# CONFIG_FEATURE_DATE_ISOFMT is not set
+# CONFIG_FEATURE_DATE_NANO is not set
+# CONFIG_FEATURE_DATE_COMPAT is not set
+# CONFIG_HOSTID is not set
+# CONFIG_ID is not set
+# CONFIG_GROUPS is not set
+CONFIG_TEST=y
+CONFIG_FEATURE_TEST_64=y
+CONFIG_TOUCH=y
+CONFIG_FEATURE_TOUCH_SUSV3=y
+CONFIG_TR=y
+CONFIG_FEATURE_TR_CLASSES=y
+CONFIG_FEATURE_TR_EQUIV=y
+CONFIG_BASE64=y
+# CONFIG_WHO is not set
+# CONFIG_USERS is not set
+CONFIG_CAL=y
+CONFIG_CATV=y
+CONFIG_CHGRP=y
+CONFIG_CHMOD=y
+CONFIG_CHOWN=y
+# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set
+CONFIG_CHROOT=y
+CONFIG_CKSUM=y
+CONFIG_COMM=y
+CONFIG_CP=y
+# CONFIG_FEATURE_CP_LONG_OPTIONS is not set
+CONFIG_CUT=y
+CONFIG_DD=y
+CONFIG_FEATURE_DD_SIGNAL_HANDLING=y
+CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y
+CONFIG_FEATURE_DD_IBS_OBS=y
+# CONFIG_DF is not set
+# CONFIG_FEATURE_DF_FANCY is not set
+CONFIG_DIRNAME=y
+CONFIG_DOS2UNIX=y
+CONFIG_UNIX2DOS=y
+CONFIG_DU=y
+CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y
+CONFIG_ECHO=y
+CONFIG_FEATURE_FANCY_ECHO=y
+CONFIG_ENV=y
+# CONFIG_FEATURE_ENV_LONG_OPTIONS is not set
+CONFIG_EXPAND=y
+# CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set
+CONFIG_EXPR=y
+CONFIG_EXPR_MATH_SUPPORT_64=y
+CONFIG_FALSE=y
+CONFIG_FOLD=y
+CONFIG_FSYNC=y
+CONFIG_HEAD=y
+CONFIG_FEATURE_FANCY_HEAD=y
+CONFIG_INSTALL=y
+# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set
+CONFIG_LN=y
+# CONFIG_LOGNAME is not set
+CONFIG_LS=y
+CONFIG_FEATURE_LS_FILETYPES=y
+CONFIG_FEATURE_LS_FOLLOWLINKS=y
+CONFIG_FEATURE_LS_RECURSIVE=y
+CONFIG_FEATURE_LS_SORTFILES=y
+CONFIG_FEATURE_LS_TIMESTAMPS=y
+CONFIG_FEATURE_LS_USERNAME=y
+# CONFIG_FEATURE_LS_COLOR is not set
+# CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set
+CONFIG_MD5SUM=y
+CONFIG_MKDIR=y
+# CONFIG_FEATURE_MKDIR_LONG_OPTIONS is not set
+CONFIG_MKFIFO=y
+CONFIG_MKNOD=y
+CONFIG_MV=y
+# CONFIG_FEATURE_MV_LONG_OPTIONS is not set
+CONFIG_NICE=y
+CONFIG_NOHUP=y
+CONFIG_OD=y
+CONFIG_PRINTENV=y
+CONFIG_PRINTF=y
+CONFIG_PWD=y
+CONFIG_READLINK=y
+CONFIG_FEATURE_READLINK_FOLLOW=y
+CONFIG_REALPATH=y
+CONFIG_RM=y
+CONFIG_RMDIR=y
+# CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set
+CONFIG_SEQ=y
+CONFIG_SHA1SUM=y
+CONFIG_SHA256SUM=y
+CONFIG_SHA512SUM=y
+CONFIG_SLEEP=y
+CONFIG_FEATURE_FANCY_SLEEP=y
+CONFIG_FEATURE_FLOAT_SLEEP=y
+CONFIG_SORT=y
+CONFIG_FEATURE_SORT_BIG=y
+CONFIG_SPLIT=y
+CONFIG_FEATURE_SPLIT_FANCY=y
+# CONFIG_STAT is not set
+# CONFIG_FEATURE_STAT_FORMAT is not set
+CONFIG_STTY=y
+CONFIG_SUM=y
+CONFIG_SYNC=y
+CONFIG_TAC=y
+CONFIG_TAIL=y
+CONFIG_FEATURE_FANCY_TAIL=y
+CONFIG_TEE=y
+CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
+CONFIG_TRUE=y
+# CONFIG_TTY is not set
+CONFIG_UNAME=y
+CONFIG_UNEXPAND=y
+# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set
+CONFIG_UNIQ=y
+CONFIG_USLEEP=y
+CONFIG_UUDECODE=y
+CONFIG_UUENCODE=y
+CONFIG_WC=y
+CONFIG_FEATURE_WC_LARGE=y
+CONFIG_WHOAMI=y
+CONFIG_YES=y
+
+#
+# Common options for cp and mv
+#
+CONFIG_FEATURE_PRESERVE_HARDLINKS=y
+
+#
+# Common options for ls, more and telnet
+#
+CONFIG_FEATURE_AUTOWIDTH=y
+
+#
+# Common options for df, du, ls
+#
+CONFIG_FEATURE_HUMAN_READABLE=y
+
+#
+# Common options for md5sum, sha1sum, sha256sum, sha512sum
+#
+CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
+
+#
+# Console Utilities
+#
+CONFIG_CHVT=y
+CONFIG_FGCONSOLE=y
+CONFIG_CLEAR=y
+CONFIG_DEALLOCVT=y
+CONFIG_DUMPKMAP=y
+# CONFIG_KBD_MODE is not set
+# CONFIG_LOADFONT is not set
+CONFIG_LOADKMAP=y
+CONFIG_OPENVT=y
+CONFIG_RESET=y
+CONFIG_RESIZE=y
+CONFIG_FEATURE_RESIZE_PRINT=y
+CONFIG_SETCONSOLE=y
+# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
+# CONFIG_SETFONT is not set
+# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
+CONFIG_DEFAULT_SETFONT_DIR=""
+CONFIG_SETKEYCODES=y
+CONFIG_SETLOGCONS=y
+CONFIG_SHOWKEY=y
+# CONFIG_FEATURE_LOADFONT_PSF2 is not set
+# CONFIG_FEATURE_LOADFONT_RAW is not set
+
+#
+# Debian Utilities
+#
+CONFIG_MKTEMP=y
+CONFIG_PIPE_PROGRESS=y
+CONFIG_RUN_PARTS=y
+# CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set
+CONFIG_FEATURE_RUN_PARTS_FANCY=y
+CONFIG_START_STOP_DAEMON=y
+CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y
+# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set
+CONFIG_WHICH=y
+
+#
+# Editors
+#
+CONFIG_PATCH=y
+CONFIG_VI=y
+CONFIG_FEATURE_VI_MAX_LEN=4096
+CONFIG_FEATURE_VI_8BIT=y
+CONFIG_FEATURE_VI_COLON=y
+CONFIG_FEATURE_VI_YANKMARK=y
+CONFIG_FEATURE_VI_SEARCH=y
+# CONFIG_FEATURE_VI_REGEX_SEARCH is not set
+CONFIG_FEATURE_VI_USE_SIGNALS=y
+CONFIG_FEATURE_VI_DOT_CMD=y
+CONFIG_FEATURE_VI_READONLY=y
+CONFIG_FEATURE_VI_SETOPTS=y
+CONFIG_FEATURE_VI_SET=y
+CONFIG_FEATURE_VI_WIN_RESIZE=y
+CONFIG_FEATURE_VI_ASK_TERMINAL=y
+CONFIG_AWK=y
+CONFIG_FEATURE_AWK_LIBM=y
+CONFIG_CMP=y
+CONFIG_DIFF=y
+# CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set
+CONFIG_FEATURE_DIFF_DIR=y
+CONFIG_ED=y
+CONFIG_SED=y
+CONFIG_FEATURE_ALLOW_EXEC=y
+
+#
+# Finding Utilities
+#
+CONFIG_FIND=y
+CONFIG_FEATURE_FIND_PRINT0=y
+CONFIG_FEATURE_FIND_MTIME=y
+CONFIG_FEATURE_FIND_MMIN=y
+CONFIG_FEATURE_FIND_PERM=y
+CONFIG_FEATURE_FIND_TYPE=y
+CONFIG_FEATURE_FIND_XDEV=y
+CONFIG_FEATURE_FIND_MAXDEPTH=y
+CONFIG_FEATURE_FIND_NEWER=y
+CONFIG_FEATURE_FIND_INUM=y
+CONFIG_FEATURE_FIND_EXEC=y
+CONFIG_FEATURE_FIND_USER=y
+CONFIG_FEATURE_FIND_GROUP=y
+CONFIG_FEATURE_FIND_NOT=y
+CONFIG_FEATURE_FIND_DEPTH=y
+CONFIG_FEATURE_FIND_PAREN=y
+CONFIG_FEATURE_FIND_SIZE=y
+CONFIG_FEATURE_FIND_PRUNE=y
+CONFIG_FEATURE_FIND_DELETE=y
+CONFIG_FEATURE_FIND_PATH=y
+CONFIG_FEATURE_FIND_REGEX=y
+# CONFIG_FEATURE_FIND_CONTEXT is not set
+CONFIG_FEATURE_FIND_LINKS=y
+CONFIG_GREP=y
+CONFIG_FEATURE_GREP_EGREP_ALIAS=y
+CONFIG_FEATURE_GREP_FGREP_ALIAS=y
+CONFIG_FEATURE_GREP_CONTEXT=y
+CONFIG_XARGS=y
+CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
+CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
+CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
+CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
+
+#
+# Init Utilities
+#
+CONFIG_BOOTCHARTD=y
+CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER=y
+CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE=y
+CONFIG_HALT=y
+# CONFIG_FEATURE_CALL_TELINIT is not set
+CONFIG_TELINIT_PATH=""
+CONFIG_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+# CONFIG_FEATURE_KILL_REMOVED is not set
+CONFIG_FEATURE_KILL_DELAY=0
+CONFIG_FEATURE_INIT_SCTTY=y
+CONFIG_FEATURE_INIT_SYSLOG=y
+CONFIG_FEATURE_EXTRA_QUIET=y
+CONFIG_FEATURE_INIT_COREDUMPS=y
+CONFIG_FEATURE_INITRD=y
+CONFIG_INIT_TERMINAL_TYPE="linux"
+CONFIG_MESG=y
+CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y
+
+#
+# Login/Password Management Utilities
+#
+# CONFIG_ADD_SHELL is not set
+# CONFIG_REMOVE_SHELL is not set
+# CONFIG_FEATURE_SHADOWPASSWDS is not set
+# CONFIG_USE_BB_PWD_GRP is not set
+# CONFIG_USE_BB_SHADOW is not set
+# CONFIG_USE_BB_CRYPT is not set
+# CONFIG_USE_BB_CRYPT_SHA is not set
+# CONFIG_ADDUSER is not set
+# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set
+# CONFIG_FEATURE_CHECK_NAMES is not set
+CONFIG_FIRST_SYSTEM_ID=0
+CONFIG_LAST_SYSTEM_ID=0
+# CONFIG_ADDGROUP is not set
+# CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set
+# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
+# CONFIG_DELUSER is not set
+# CONFIG_DELGROUP is not set
+# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
+# CONFIG_GETTY is not set
+# CONFIG_LOGIN is not set
+# CONFIG_LOGIN_SESSION_AS_CHILD is not set
+# CONFIG_PAM is not set
+# CONFIG_LOGIN_SCRIPTS is not set
+# CONFIG_FEATURE_NOLOGIN is not set
+# CONFIG_FEATURE_SECURETTY is not set
+# CONFIG_PASSWD is not set
+# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set
+# CONFIG_CRYPTPW is not set
+# CONFIG_CHPASSWD is not set
+# CONFIG_SU is not set
+# CONFIG_FEATURE_SU_SYSLOG is not set
+# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set
+# CONFIG_SULOGIN is not set
+# CONFIG_VLOCK is not set
+
+#
+# Linux Ext2 FS Progs
+#
+CONFIG_CHATTR=y
+# CONFIG_FSCK is not set
+CONFIG_LSATTR=y
+CONFIG_TUNE2FS=y
+
+#
+# Linux Module Utilities
+#
+CONFIG_MODINFO=y
+CONFIG_MODPROBE_SMALL=y
+CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE=y
+CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y
+# CONFIG_INSMOD is not set
+# CONFIG_RMMOD is not set
+# CONFIG_LSMOD is not set
+# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set
+# CONFIG_MODPROBE is not set
+# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set
+# CONFIG_DEPMOD is not set
+
+#
+# Options common to multiple modutils
+#
+# CONFIG_FEATURE_2_4_MODULES is not set
+# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set
+# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
+# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
+# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
+# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
+# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set
+# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
+# CONFIG_FEATURE_MODUTILS_ALIAS is not set
+# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set
+CONFIG_DEFAULT_MODULES_DIR="/lib/modules"
+CONFIG_DEFAULT_DEPMOD_FILE="modules.dep"
+
+#
+# Linux System Utilities
+#
+CONFIG_BLOCKDEV=y
+CONFIG_MDEV=y
+CONFIG_FEATURE_MDEV_CONF=y
+CONFIG_FEATURE_MDEV_RENAME=y
+CONFIG_FEATURE_MDEV_RENAME_REGEXP=y
+CONFIG_FEATURE_MDEV_EXEC=y
+CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y
+CONFIG_REV=y
+# CONFIG_ACPID is not set
+# CONFIG_FEATURE_ACPID_COMPAT is not set
+CONFIG_BLKID=y
+CONFIG_FEATURE_BLKID_TYPE=y
+CONFIG_DMESG=y
+CONFIG_FEATURE_DMESG_PRETTY=y
+CONFIG_FBSET=y
+CONFIG_FEATURE_FBSET_FANCY=y
+CONFIG_FEATURE_FBSET_READMODE=y
+CONFIG_FDFLUSH=y
+CONFIG_FDFORMAT=y
+CONFIG_FDISK=y
+CONFIG_FDISK_SUPPORT_LARGE_DISKS=y
+CONFIG_FEATURE_FDISK_WRITABLE=y
+# CONFIG_FEATURE_AIX_LABEL is not set
+# CONFIG_FEATURE_SGI_LABEL is not set
+# CONFIG_FEATURE_SUN_LABEL is not set
+# CONFIG_FEATURE_OSF_LABEL is not set
+# CONFIG_FEATURE_GPT_LABEL is not set
+CONFIG_FEATURE_FDISK_ADVANCED=y
+CONFIG_FINDFS=y
+CONFIG_FLOCK=y
+CONFIG_FREERAMDISK=y
+# CONFIG_FSCK_MINIX is not set
+# CONFIG_MKFS_EXT2 is not set
+# CONFIG_MKFS_MINIX is not set
+# CONFIG_FEATURE_MINIX2 is not set
+# CONFIG_MKFS_REISER is not set
+# CONFIG_MKFS_VFAT is not set
+CONFIG_GETOPT=y
+CONFIG_FEATURE_GETOPT_LONG=y
+CONFIG_HEXDUMP=y
+CONFIG_FEATURE_HEXDUMP_REVERSE=y
+CONFIG_HD=y
+CONFIG_HWCLOCK=y
+# CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set
+# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set
+# CONFIG_IPCRM is not set
+# CONFIG_IPCS is not set
+CONFIG_LOSETUP=y
+CONFIG_LSPCI=y
+CONFIG_LSUSB=y
+CONFIG_MKSWAP=y
+CONFIG_FEATURE_MKSWAP_UUID=y
+CONFIG_MORE=y
+# CONFIG_MOUNT is not set
+# CONFIG_FEATURE_MOUNT_FAKE is not set
+# CONFIG_FEATURE_MOUNT_VERBOSE is not set
+# CONFIG_FEATURE_MOUNT_HELPERS is not set
+# CONFIG_FEATURE_MOUNT_LABEL is not set
+# CONFIG_FEATURE_MOUNT_NFS is not set
+# CONFIG_FEATURE_MOUNT_CIFS is not set
+# CONFIG_FEATURE_MOUNT_FLAGS is not set
+# CONFIG_FEATURE_MOUNT_FSTAB is not set
+# CONFIG_PIVOT_ROOT is not set
+# CONFIG_RDATE is not set
+CONFIG_RDEV=y
+CONFIG_READPROFILE=y
+CONFIG_RTCWAKE=y
+CONFIG_SCRIPT=y
+CONFIG_SCRIPTREPLAY=y
+# CONFIG_SETARCH is not set
+# CONFIG_SWAPONOFF is not set
+# CONFIG_FEATURE_SWAPON_PRI is not set
+CONFIG_SWITCH_ROOT=y
+# CONFIG_UMOUNT is not set
+# CONFIG_FEATURE_UMOUNT_ALL is not set
+# CONFIG_FEATURE_MOUNT_LOOP is not set
+# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set
+# CONFIG_FEATURE_MTAB_SUPPORT is not set
+CONFIG_VOLUMEID=y
+
+#
+# Filesystem/Volume identification
+#
+CONFIG_FEATURE_VOLUMEID_EXT=y
+CONFIG_FEATURE_VOLUMEID_BTRFS=y
+CONFIG_FEATURE_VOLUMEID_REISERFS=y
+CONFIG_FEATURE_VOLUMEID_FAT=y
+CONFIG_FEATURE_VOLUMEID_HFS=y
+CONFIG_FEATURE_VOLUMEID_JFS=y
+CONFIG_FEATURE_VOLUMEID_XFS=y
+CONFIG_FEATURE_VOLUMEID_NTFS=y
+CONFIG_FEATURE_VOLUMEID_ISO9660=y
+CONFIG_FEATURE_VOLUMEID_UDF=y
+CONFIG_FEATURE_VOLUMEID_LUKS=y
+CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y
+CONFIG_FEATURE_VOLUMEID_CRAMFS=y
+CONFIG_FEATURE_VOLUMEID_ROMFS=y
+CONFIG_FEATURE_VOLUMEID_SYSV=y
+CONFIG_FEATURE_VOLUMEID_OCFS2=y
+CONFIG_FEATURE_VOLUMEID_LINUXRAID=y
+
+#
+# Miscellaneous Utilities
+#
+# CONFIG_CONSPY is not set
+CONFIG_LESS=y
+CONFIG_FEATURE_LESS_MAXLINES=9999999
+CONFIG_FEATURE_LESS_BRACKETS=y
+CONFIG_FEATURE_LESS_FLAGS=y
+CONFIG_FEATURE_LESS_MARKS=y
+CONFIG_FEATURE_LESS_REGEXP=y
+CONFIG_FEATURE_LESS_WINCH=y
+CONFIG_FEATURE_LESS_ASK_TERMINAL=y
+CONFIG_FEATURE_LESS_DASHCMD=y
+CONFIG_FEATURE_LESS_LINENUMS=y
+# CONFIG_NANDWRITE is not set
+CONFIG_NANDDUMP=y
+CONFIG_SETSERIAL=y
+# CONFIG_UBIATTACH is not set
+# CONFIG_UBIDETACH is not set
+# CONFIG_UBIMKVOL is not set
+# CONFIG_UBIRMVOL is not set
+# CONFIG_UBIRSVOL is not set
+# CONFIG_UBIUPDATEVOL is not set
+# CONFIG_ADJTIMEX is not set
+# CONFIG_BBCONFIG is not set
+# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
+CONFIG_BEEP=y
+CONFIG_FEATURE_BEEP_FREQ=4000
+CONFIG_FEATURE_BEEP_LENGTH_MS=30
+CONFIG_CHAT=y
+CONFIG_FEATURE_CHAT_NOFAIL=y
+# CONFIG_FEATURE_CHAT_TTY_HIFI is not set
+CONFIG_FEATURE_CHAT_IMPLICIT_CR=y
+CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y
+CONFIG_FEATURE_CHAT_SEND_ESCAPES=y
+CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y
+CONFIG_FEATURE_CHAT_CLR_ABORT=y
+CONFIG_CHRT=y
+CONFIG_CROND=y
+CONFIG_FEATURE_CROND_D=y
+CONFIG_FEATURE_CROND_CALL_SENDMAIL=y
+CONFIG_FEATURE_CROND_DIR="/var/spool/cron"
+CONFIG_CRONTAB=y
+CONFIG_DC=y
+CONFIG_FEATURE_DC_LIBM=y
+# CONFIG_DEVFSD is not set
+# CONFIG_DEVFSD_MODLOAD is not set
+# CONFIG_DEVFSD_FG_NP is not set
+# CONFIG_DEVFSD_VERBOSE is not set
+# CONFIG_FEATURE_DEVFS is not set
+CONFIG_DEVMEM=y
+# CONFIG_EJECT is not set
+# CONFIG_FEATURE_EJECT_SCSI is not set
+CONFIG_FBSPLASH=y
+CONFIG_FLASHCP=y
+CONFIG_FLASH_LOCK=y
+CONFIG_FLASH_UNLOCK=y
+# CONFIG_FLASH_ERASEALL is not set
+# CONFIG_IONICE is not set
+CONFIG_INOTIFYD=y
+# CONFIG_LAST is not set
+# CONFIG_FEATURE_LAST_SMALL is not set
+# CONFIG_FEATURE_LAST_FANCY is not set
+CONFIG_HDPARM=y
+CONFIG_FEATURE_HDPARM_GET_IDENTITY=y
+CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y
+CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y
+CONFIG_MAKEDEVS=y
+# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
+CONFIG_FEATURE_MAKEDEVS_TABLE=y
+CONFIG_MAN=y
+# CONFIG_MICROCOM is not set
+# CONFIG_MOUNTPOINT is not set
+# CONFIG_MT is not set
+CONFIG_RAIDAUTORUN=y
+# CONFIG_READAHEAD is not set
+# CONFIG_RFKILL is not set
+# CONFIG_RUNLEVEL is not set
+CONFIG_RX=y
+CONFIG_SETSID=y
+CONFIG_STRINGS=y
+# CONFIG_TASKSET is not set
+# CONFIG_FEATURE_TASKSET_FANCY is not set
+CONFIG_TIME=y
+CONFIG_TIMEOUT=y
+CONFIG_TTYSIZE=y
+CONFIG_VOLNAME=y
+# CONFIG_WALL is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Networking Utilities
+#
+# CONFIG_NAMEIF is not set
+# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
+CONFIG_NBDCLIENT=y
+CONFIG_NC=y
+CONFIG_NC_SERVER=y
+CONFIG_NC_EXTRA=y
+# CONFIG_NC_110_COMPAT is not set
+CONFIG_PING=y
+# CONFIG_PING6 is not set
+CONFIG_FEATURE_FANCY_PING=y
+CONFIG_WHOIS=y
+# CONFIG_FEATURE_IPV6 is not set
+# CONFIG_FEATURE_UNIX_LOCAL is not set
+# CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set
+# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
+CONFIG_ARP=y
+# CONFIG_ARPING is not set
+# CONFIG_BRCTL is not set
+# CONFIG_FEATURE_BRCTL_FANCY is not set
+# CONFIG_FEATURE_BRCTL_SHOW is not set
+CONFIG_DNSD=y
+# CONFIG_ETHER_WAKE is not set
+CONFIG_FAKEIDENTD=y
+CONFIG_FTPD=y
+CONFIG_FEATURE_FTP_WRITE=y
+CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y
+CONFIG_FTPGET=y
+CONFIG_FTPPUT=y
+# CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set
+# CONFIG_HOSTNAME is not set
+CONFIG_HTTPD=y
+CONFIG_FEATURE_HTTPD_RANGES=y
+CONFIG_FEATURE_HTTPD_USE_SENDFILE=y
+CONFIG_FEATURE_HTTPD_SETUID=y
+CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
+# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
+CONFIG_FEATURE_HTTPD_CGI=y
+CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y
+CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y
+CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
+CONFIG_FEATURE_HTTPD_ERROR_PAGES=y
+CONFIG_FEATURE_HTTPD_PROXY=y
+CONFIG_FEATURE_HTTPD_GZIP=y
+CONFIG_IFCONFIG=y
+CONFIG_FEATURE_IFCONFIG_STATUS=y
+# CONFIG_FEATURE_IFCONFIG_SLIP is not set
+CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y
+CONFIG_FEATURE_IFCONFIG_HW=y
+CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y
+# CONFIG_IFENSLAVE is not set
+# CONFIG_IFPLUGD is not set
+CONFIG_IFUPDOWN=y
+CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate"
+CONFIG_FEATURE_IFUPDOWN_IP=y
+CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y
+# CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set
+CONFIG_FEATURE_IFUPDOWN_IPV4=y
+# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set
+CONFIG_FEATURE_IFUPDOWN_MAPPING=y
+CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP=y
+# CONFIG_INETD is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set
+# CONFIG_FEATURE_INETD_RPC is not set
+CONFIG_IP=y
+CONFIG_FEATURE_IP_ADDRESS=y
+CONFIG_FEATURE_IP_LINK=y
+CONFIG_FEATURE_IP_ROUTE=y
+CONFIG_FEATURE_IP_TUNNEL=y
+CONFIG_FEATURE_IP_RULE=y
+CONFIG_FEATURE_IP_SHORT_FORMS=y
+# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
+CONFIG_IPADDR=y
+CONFIG_IPLINK=y
+CONFIG_IPROUTE=y
+CONFIG_IPTUNNEL=y
+CONFIG_IPRULE=y
+CONFIG_IPCALC=y
+CONFIG_FEATURE_IPCALC_FANCY=y
+# CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set
+CONFIG_NETSTAT=y
+CONFIG_FEATURE_NETSTAT_WIDE=y
+CONFIG_FEATURE_NETSTAT_PRG=y
+# CONFIG_NSLOOKUP is not set
+# CONFIG_NTPD is not set
+# CONFIG_FEATURE_NTPD_SERVER is not set
+CONFIG_PSCAN=y
+CONFIG_ROUTE=y
+# CONFIG_SLATTACH is not set
+CONFIG_TCPSVD=y
+CONFIG_TELNET=y
+CONFIG_FEATURE_TELNET_TTYPE=y
+CONFIG_FEATURE_TELNET_AUTOLOGIN=y
+CONFIG_TELNETD=y
+CONFIG_FEATURE_TELNETD_STANDALONE=y
+CONFIG_FEATURE_TELNETD_INETD_WAIT=y
+CONFIG_TFTP=y
+CONFIG_TFTPD=y
+
+#
+# Common options for tftp/tftpd
+#
+CONFIG_FEATURE_TFTP_GET=y
+CONFIG_FEATURE_TFTP_PUT=y
+CONFIG_FEATURE_TFTP_BLOCKSIZE=y
+CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
+# CONFIG_TFTP_DEBUG is not set
+CONFIG_TRACEROUTE=y
+# CONFIG_TRACEROUTE6 is not set
+CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
+# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
+# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
+CONFIG_TUNCTL=y
+CONFIG_FEATURE_TUNCTL_UG=y
+# CONFIG_UDHCPC6 is not set
+# CONFIG_UDHCPD is not set
+# CONFIG_DHCPRELAY is not set
+# CONFIG_DUMPLEASES is not set
+# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
+# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set
+CONFIG_DHCPD_LEASES_FILE=""
+CONFIG_UDHCPC=y
+CONFIG_FEATURE_UDHCPC_ARPING=y
+CONFIG_FEATURE_UDHCP_PORT=y
+CONFIG_UDHCP_DEBUG=9
+CONFIG_FEATURE_UDHCP_RFC3397=y
+CONFIG_FEATURE_UDHCP_8021Q=y
+CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script"
+CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80
+CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n"
+CONFIG_UDPSVD=y
+CONFIG_VCONFIG=y
+CONFIG_WGET=y
+CONFIG_FEATURE_WGET_STATUSBAR=y
+CONFIG_FEATURE_WGET_AUTHENTICATION=y
+# CONFIG_FEATURE_WGET_LONG_OPTIONS is not set
+CONFIG_FEATURE_WGET_TIMEOUT=y
+# CONFIG_ZCIP is not set
+
+#
+# Print Utilities
+#
+CONFIG_LPD=y
+CONFIG_LPR=y
+CONFIG_LPQ=y
+
+#
+# Mail Utilities
+#
+CONFIG_MAKEMIME=y
+CONFIG_FEATURE_MIME_CHARSET="us-ascii"
+CONFIG_POPMAILDIR=y
+CONFIG_FEATURE_POPMAILDIR_DELIVERY=y
+CONFIG_REFORMIME=y
+CONFIG_FEATURE_REFORMIME_COMPAT=y
+CONFIG_SENDMAIL=y
+
+#
+# Process Utilities
+#
+CONFIG_IOSTAT=y
+CONFIG_MPSTAT=y
+CONFIG_NMETER=y
+CONFIG_PMAP=y
+CONFIG_POWERTOP=y
+CONFIG_PSTREE=y
+CONFIG_PWDX=y
+CONFIG_SMEMCAP=y
+CONFIG_UPTIME=y
+# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
+CONFIG_FREE=y
+CONFIG_FUSER=y
+# CONFIG_KILL is not set
+# CONFIG_KILLALL is not set
+# CONFIG_KILLALL5 is not set
+# CONFIG_PGREP is not set
+CONFIG_PIDOF=y
+CONFIG_FEATURE_PIDOF_SINGLE=y
+CONFIG_FEATURE_PIDOF_OMIT=y
+# CONFIG_PKILL is not set
+CONFIG_PS=y
+# CONFIG_FEATURE_PS_WIDE is not set
+# CONFIG_FEATURE_PS_LONG is not set
+CONFIG_FEATURE_PS_TIME=y
+CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y
+# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
+CONFIG_RENICE=y
+CONFIG_BB_SYSCTL=y
+CONFIG_TOP=y
+CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y
+CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y
+CONFIG_FEATURE_TOP_SMP_CPU=y
+CONFIG_FEATURE_TOP_DECIMALS=y
+CONFIG_FEATURE_TOP_SMP_PROCESS=y
+CONFIG_FEATURE_TOPMEM=y
+CONFIG_FEATURE_SHOW_THREADS=y
+CONFIG_WATCH=y
+
+#
+# Runit Utilities
+#
+CONFIG_RUNSV=y
+CONFIG_RUNSVDIR=y
+# CONFIG_FEATURE_RUNSVDIR_LOG is not set
+CONFIG_SV=y
+CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service"
+CONFIG_SVLOGD=y
+CONFIG_CHPST=y
+CONFIG_SETUIDGID=y
+CONFIG_ENVUIDGID=y
+CONFIG_ENVDIR=y
+CONFIG_SOFTLIMIT=y
+# CONFIG_CHCON is not set
+# CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set
+# CONFIG_GETENFORCE is not set
+# CONFIG_GETSEBOOL is not set
+# CONFIG_LOAD_POLICY is not set
+# CONFIG_MATCHPATHCON is not set
+# CONFIG_RESTORECON is not set
+# CONFIG_RUNCON is not set
+# CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set
+# CONFIG_SELINUXENABLED is not set
+# CONFIG_SETENFORCE is not set
+# CONFIG_SETFILES is not set
+# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set
+# CONFIG_SETSEBOOL is not set
+# CONFIG_SESTATUS is not set
+
+#
+# Shells
+#
+# CONFIG_ASH is not set
+# CONFIG_ASH_BASH_COMPAT is not set
+# CONFIG_ASH_IDLE_TIMEOUT is not set
+# CONFIG_ASH_JOB_CONTROL is not set
+# CONFIG_ASH_ALIAS is not set
+# CONFIG_ASH_GETOPTS is not set
+# CONFIG_ASH_BUILTIN_ECHO is not set
+# CONFIG_ASH_BUILTIN_PRINTF is not set
+# CONFIG_ASH_BUILTIN_TEST is not set
+# CONFIG_ASH_CMDCMD is not set
+# CONFIG_ASH_MAIL is not set
+# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set
+# CONFIG_ASH_RANDOM_SUPPORT is not set
+# CONFIG_ASH_EXPAND_PRMT is not set
+CONFIG_CTTYHACK=y
+# CONFIG_HUSH is not set
+# CONFIG_HUSH_BASH_COMPAT is not set
+# CONFIG_HUSH_BRACE_EXPANSION is not set
+# CONFIG_HUSH_HELP is not set
+# CONFIG_HUSH_INTERACTIVE is not set
+# CONFIG_HUSH_SAVEHISTORY is not set
+# CONFIG_HUSH_JOB is not set
+# CONFIG_HUSH_TICK is not set
+# CONFIG_HUSH_IF is not set
+# CONFIG_HUSH_LOOPS is not set
+# CONFIG_HUSH_CASE is not set
+# CONFIG_HUSH_FUNCTIONS is not set
+# CONFIG_HUSH_LOCAL is not set
+# CONFIG_HUSH_RANDOM_SUPPORT is not set
+# CONFIG_HUSH_EXPORT_N is not set
+# CONFIG_HUSH_MODE_X is not set
+# CONFIG_MSH is not set
+# CONFIG_FEATURE_SH_IS_ASH is not set
+# CONFIG_FEATURE_SH_IS_HUSH is not set
+CONFIG_FEATURE_SH_IS_NONE=y
+# CONFIG_FEATURE_BASH_IS_ASH is not set
+# CONFIG_FEATURE_BASH_IS_HUSH is not set
+CONFIG_FEATURE_BASH_IS_NONE=y
+# CONFIG_SH_MATH_SUPPORT is not set
+# CONFIG_SH_MATH_SUPPORT_64 is not set
+# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
+# CONFIG_FEATURE_SH_STANDALONE is not set
+# CONFIG_FEATURE_SH_NOFORK is not set
+# CONFIG_FEATURE_SH_HISTFILESIZE is not set
+
+#
+# System Logging Utilities
+#
+# CONFIG_SYSLOGD is not set
+# CONFIG_FEATURE_ROTATE_LOGFILE is not set
+# CONFIG_FEATURE_REMOTE_LOG is not set
+# CONFIG_FEATURE_SYSLOGD_DUP is not set
+# CONFIG_FEATURE_SYSLOGD_CFG is not set
+CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0
+# CONFIG_FEATURE_IPC_SYSLOG is not set
+CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0
+# CONFIG_LOGREAD is not set
+# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
+CONFIG_KLOGD=y
+CONFIG_FEATURE_KLOGD_KLOGCTL=y
+# CONFIG_LOGGER is not set
similarity index 60%
rename from packaging/busybox-dahlia.config
rename to configs/android_ndk_defconfig
index 3b3be76..01cc2dd 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Busybox version: 1.17.1
-# Fri Apr 15 11:03:42 2011
+# Busybox version: 1.21.0.git
+# Mon May 28 21:51:18 2012
 #
 CONFIG_HAVE_DOT_CONFIG=y
 
@@ -12,9 +12,9 @@ CONFIG_HAVE_DOT_CONFIG=y
 #
 # General Configuration
 #
-# CONFIG_DESKTOP is not set
-CONFIG_EXTRA_COMPAT=y
-CONFIG_INCLUDE_SUSv2=y
+CONFIG_DESKTOP=y
+# CONFIG_EXTRA_COMPAT is not set
+# CONFIG_INCLUDE_SUSv2 is not set
 # CONFIG_USE_PORTABLE_CODE is not set
 CONFIG_PLATFORM_LINUX=y
 CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
@@ -23,32 +23,33 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
 CONFIG_SHOW_USAGE=y
 CONFIG_FEATURE_VERBOSE_USAGE=y
 CONFIG_FEATURE_COMPRESS_USAGE=y
-# CONFIG_FEATURE_INSTALLER is not set
+CONFIG_FEATURE_INSTALLER=y
+CONFIG_INSTALL_NO_USR=y
 # CONFIG_LOCALE_SUPPORT is not set
-CONFIG_UNICODE_SUPPORT=y
+# CONFIG_UNICODE_SUPPORT is not set
 # CONFIG_UNICODE_USING_LOCALE is not set
-CONFIG_FEATURE_CHECK_UNICODE_IN_ENV=y
-CONFIG_SUBST_WCHAR=63
-CONFIG_LAST_SUPPORTED_WCHAR=767
+# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
+CONFIG_SUBST_WCHAR=0
+CONFIG_LAST_SUPPORTED_WCHAR=0
 # CONFIG_UNICODE_COMBINING_WCHARS is not set
 # CONFIG_UNICODE_WIDE_WCHARS is not set
 # CONFIG_UNICODE_BIDI_SUPPORT is not set
 # CONFIG_UNICODE_NEUTRAL_TABLE is not set
 # CONFIG_UNICODE_PRESERVE_BROKEN is not set
 CONFIG_LONG_OPTS=y
-CONFIG_FEATURE_DEVPTS=y
+# CONFIG_FEATURE_DEVPTS is not set
 # CONFIG_FEATURE_CLEAN_UP is not set
-CONFIG_FEATURE_UTMP=y
-CONFIG_FEATURE_WTMP=y
-CONFIG_FEATURE_PIDFILE=y
-CONFIG_FEATURE_SUID=y
+# CONFIG_FEATURE_UTMP is not set
+# CONFIG_FEATURE_WTMP is not set
+# CONFIG_FEATURE_PIDFILE is not set
+# CONFIG_FEATURE_SUID is not set
 # CONFIG_FEATURE_SUID_CONFIG is not set
 # CONFIG_FEATURE_SUID_CONFIG_QUIET is not set
 # CONFIG_SELINUX is not set
 # CONFIG_FEATURE_PREFER_APPLETS is not set
 CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
 CONFIG_FEATURE_SYSLOG=y
-CONFIG_FEATURE_HAVE_RPC=y
+# CONFIG_FEATURE_HAVE_RPC is not set
 
 #
 # Build Options
@@ -59,9 +60,12 @@ CONFIG_FEATURE_HAVE_RPC=y
 # CONFIG_BUILD_LIBBUSYBOX is not set
 # CONFIG_FEATURE_INDIVIDUAL is not set
 # CONFIG_FEATURE_SHARED_BUSYBOX is not set
-CONFIG_LFS=y
-CONFIG_CROSS_COMPILER_PREFIX=""
-CONFIG_EXTRA_CFLAGS=""
+# CONFIG_LFS is not set
+CONFIG_CROSS_COMPILER_PREFIX="arm-linux-androideabi-"
+CONFIG_SYSROOT="/opt/android-ndk/platforms/android-9/arch-arm"
+CONFIG_EXTRA_CFLAGS="-DANDROID -D__ANDROID__ -DSK_RELEASE -nostdlib -march=armv7-a -msoft-float -mfloat-abi=softfp -mfpu=neon -mthumb -mthumb-interwork -fpic -fno-short-enums -fgcse-after-reload -frename-registers"
+CONFIG_EXTRA_LDFLAGS="-Xlinker -z -Xlinker muldefs -nostdlib -Bdynamic -Xlinker -dynamic-linker -Xlinker /system/bin/linker -Xlinker -z -Xlinker nocopyreloc -Xlinker --no-undefined ${SYSROOT}/usr/lib/crtbegin_dynamic.o ${SYSROOT}/usr/lib/crtend_android.o"
+CONFIG_EXTRA_LDLIBS="dl m c gcc"
 
 #
 # Debugging Options
@@ -74,9 +78,8 @@ CONFIG_NO_DEBUG_LIB=y
 # CONFIG_EFENCE is not set
 
 #
-# Installation Options
+# Installation Options ("make install" behavior)
 #
-# CONFIG_INSTALL_NO_USR is not set
 CONFIG_INSTALL_APPLET_SYMLINKS=y
 # CONFIG_INSTALL_APPLET_HARDLINKS is not set
 # CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
@@ -89,25 +92,31 @@ CONFIG_PREFIX="./_install"
 #
 # Busybox Library Tuning
 #
+# CONFIG_FEATURE_SYSTEMD is not set
+# CONFIG_FEATURE_RTMINMAX is not set
 CONFIG_PASSWORD_MINLEN=6
-CONFIG_MD5_SIZE_VS_SPEED=0
-CONFIG_FEATURE_FAST_TOP=y
+CONFIG_MD5_SMALL=1
+# CONFIG_FEATURE_FAST_TOP is not set
 # CONFIG_FEATURE_ETC_NETWORKS is not set
-CONFIG_FEATURE_EDITING=y
-CONFIG_FEATURE_EDITING_MAX_LEN=1024
+CONFIG_FEATURE_USE_TERMIOS=y
+# CONFIG_FEATURE_EDITING is not set
+CONFIG_FEATURE_EDITING_MAX_LEN=0
 # CONFIG_FEATURE_EDITING_VI is not set
-CONFIG_FEATURE_EDITING_HISTORY=15
-CONFIG_FEATURE_EDITING_SAVEHISTORY=y
-CONFIG_FEATURE_TAB_COMPLETION=y
+CONFIG_FEATURE_EDITING_HISTORY=0
+# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set
+# CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set
+# CONFIG_FEATURE_REVERSE_SEARCH is not set
+# CONFIG_FEATURE_TAB_COMPLETION is not set
 # CONFIG_FEATURE_USERNAME_COMPLETION is not set
-CONFIG_FEATURE_EDITING_FANCY_PROMPT=y
-CONFIG_FEATURE_EDITING_ASK_TERMINAL=y
+# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set
+# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
 # CONFIG_FEATURE_NON_POSIX_CP is not set
 CONFIG_FEATURE_VERBOSE_CP_MESSAGE=y
 CONFIG_FEATURE_COPYBUF_KB=4
-CONFIG_MONOTONIC_SYSCALL=y
-CONFIG_IOCTL_HEX2STR_ERROR=y
-CONFIG_FEATURE_HWIB=y
+# CONFIG_FEATURE_SKIP_ROOTFS is not set
+# CONFIG_MONOTONIC_SYSCALL is not set
+# CONFIG_IOCTL_HEX2STR_ERROR is not set
+# CONFIG_FEATURE_HWIB is not set
 
 #
 # Applets
@@ -129,12 +138,13 @@ CONFIG_BZIP2=y
 CONFIG_CPIO=y
 CONFIG_FEATURE_CPIO_O=y
 CONFIG_FEATURE_CPIO_P=y
-# CONFIG_DPKG is not set
-# CONFIG_DPKG_DEB is not set
+CONFIG_DPKG=y
+CONFIG_DPKG_DEB=y
 # CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
 CONFIG_GUNZIP=y
 CONFIG_GZIP=y
-CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
+# CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set
+CONFIG_GZIP_FAST=0
 CONFIG_LZOP=y
 CONFIG_LZOP_COMPR_HIGH=y
 CONFIG_RPM2CPIO=y
@@ -146,8 +156,8 @@ CONFIG_FEATURE_TAR_FROM=y
 CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y
 CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y
 CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
-CONFIG_FEATURE_TAR_LONG_OPTIONS=y
-CONFIG_FEATURE_TAR_TO_COMMAND=y
+# CONFIG_FEATURE_TAR_LONG_OPTIONS is not set
+# CONFIG_FEATURE_TAR_TO_COMMAND is not set
 CONFIG_FEATURE_TAR_UNAME_GNAME=y
 CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
 # CONFIG_FEATURE_TAR_SELINUX is not set
@@ -155,8 +165,8 @@ CONFIG_UNCOMPRESS=y
 CONFIG_UNLZMA=y
 CONFIG_FEATURE_LZMA_FAST=y
 CONFIG_LZMA=y
-# CONFIG_UNXZ is not set
-# CONFIG_XZ is not set
+CONFIG_UNXZ=y
+CONFIG_XZ=y
 CONFIG_UNZIP=y
 
 #
@@ -164,33 +174,41 @@ CONFIG_UNZIP=y
 #
 CONFIG_BASENAME=y
 CONFIG_CAT=y
-CONFIG_DATE=y
-CONFIG_FEATURE_DATE_ISOFMT=y
-CONFIG_FEATURE_DATE_NANO=y
-CONFIG_FEATURE_DATE_COMPAT=y
+# CONFIG_DATE is not set
+# CONFIG_FEATURE_DATE_ISOFMT is not set
+# CONFIG_FEATURE_DATE_NANO is not set
+# CONFIG_FEATURE_DATE_COMPAT is not set
+# CONFIG_HOSTID is not set
+# CONFIG_ID is not set
+# CONFIG_GROUPS is not set
 CONFIG_TEST=y
 CONFIG_FEATURE_TEST_64=y
+CONFIG_TOUCH=y
+CONFIG_FEATURE_TOUCH_SUSV3=y
 CONFIG_TR=y
 CONFIG_FEATURE_TR_CLASSES=y
 CONFIG_FEATURE_TR_EQUIV=y
+CONFIG_BASE64=y
+# CONFIG_WHO is not set
+# CONFIG_USERS is not set
 CONFIG_CAL=y
 CONFIG_CATV=y
 CONFIG_CHGRP=y
 CONFIG_CHMOD=y
 CONFIG_CHOWN=y
-CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y
+# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set
 CONFIG_CHROOT=y
 CONFIG_CKSUM=y
 CONFIG_COMM=y
 CONFIG_CP=y
-CONFIG_FEATURE_CP_LONG_OPTIONS=y
+# CONFIG_FEATURE_CP_LONG_OPTIONS is not set
 CONFIG_CUT=y
 CONFIG_DD=y
 CONFIG_FEATURE_DD_SIGNAL_HANDLING=y
 CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y
 CONFIG_FEATURE_DD_IBS_OBS=y
-CONFIG_DF=y
-CONFIG_FEATURE_DF_FANCY=y
+# CONFIG_DF is not set
+# CONFIG_FEATURE_DF_FANCY is not set
 CONFIG_DIRNAME=y
 CONFIG_DOS2UNIX=y
 CONFIG_UNIX2DOS=y
@@ -199,9 +217,9 @@ CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y
 CONFIG_ECHO=y
 CONFIG_FEATURE_FANCY_ECHO=y
 CONFIG_ENV=y
-CONFIG_FEATURE_ENV_LONG_OPTIONS=y
+# CONFIG_FEATURE_ENV_LONG_OPTIONS is not set
 CONFIG_EXPAND=y
-CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y
+# CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set
 CONFIG_EXPR=y
 CONFIG_EXPR_MATH_SUPPORT_64=y
 CONFIG_FALSE=y
@@ -209,13 +227,10 @@ CONFIG_FOLD=y
 CONFIG_FSYNC=y
 CONFIG_HEAD=y
 CONFIG_FEATURE_FANCY_HEAD=y
-CONFIG_HOSTID=y
-CONFIG_ID=y
 CONFIG_INSTALL=y
-CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y
-CONFIG_LENGTH=y
+# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set
 CONFIG_LN=y
-CONFIG_LOGNAME=y
+# CONFIG_LOGNAME is not set
 CONFIG_LS=y
 CONFIG_FEATURE_LS_FILETYPES=y
 CONFIG_FEATURE_LS_FOLLOWLINKS=y
@@ -227,11 +242,11 @@ CONFIG_FEATURE_LS_COLOR=y
 # CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set
 CONFIG_MD5SUM=y
 CONFIG_MKDIR=y
-CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y
+# CONFIG_FEATURE_MKDIR_LONG_OPTIONS is not set
 CONFIG_MKFIFO=y
 CONFIG_MKNOD=y
 CONFIG_MV=y
-CONFIG_FEATURE_MV_LONG_OPTIONS=y
+# CONFIG_FEATURE_MV_LONG_OPTIONS is not set
 CONFIG_NICE=y
 CONFIG_NOHUP=y
 CONFIG_OD=y
@@ -243,7 +258,7 @@ CONFIG_FEATURE_READLINK_FOLLOW=y
 CONFIG_REALPATH=y
 CONFIG_RM=y
 CONFIG_RMDIR=y
-CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y
+# CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set
 CONFIG_SEQ=y
 CONFIG_SHA1SUM=y
 CONFIG_SHA256SUM=y
@@ -255,8 +270,8 @@ CONFIG_SORT=y
 CONFIG_FEATURE_SORT_BIG=y
 CONFIG_SPLIT=y
 CONFIG_FEATURE_SPLIT_FANCY=y
-CONFIG_STAT=y
-CONFIG_FEATURE_STAT_FORMAT=y
+# CONFIG_STAT is not set
+# CONFIG_FEATURE_STAT_FORMAT is not set
 CONFIG_STTY=y
 CONFIG_SUM=y
 CONFIG_SYNC=y
@@ -265,19 +280,17 @@ CONFIG_TAIL=y
 CONFIG_FEATURE_FANCY_TAIL=y
 CONFIG_TEE=y
 CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
-CONFIG_TOUCH=y
 CONFIG_TRUE=y
-CONFIG_TTY=y
+# CONFIG_TTY is not set
 CONFIG_UNAME=y
 CONFIG_UNEXPAND=y
-CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y
+# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set
 CONFIG_UNIQ=y
 CONFIG_USLEEP=y
 CONFIG_UUDECODE=y
 CONFIG_UUENCODE=y
 CONFIG_WC=y
 CONFIG_FEATURE_WC_LARGE=y
-CONFIG_WHO=y
 CONFIG_WHOAMI=y
 CONFIG_YES=y
 
@@ -306,63 +319,51 @@ CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
 #
 CONFIG_CHVT=y
 CONFIG_FGCONSOLE=y
-# CONFIG_CLEAR is not set
+CONFIG_CLEAR=y
 CONFIG_DEALLOCVT=y
 CONFIG_DUMPKMAP=y
-CONFIG_KBD_MODE=y
-CONFIG_LOADFONT=y
+# CONFIG_KBD_MODE is not set
+# CONFIG_LOADFONT is not set
 CONFIG_LOADKMAP=y
 CONFIG_OPENVT=y
-# CONFIG_RESET is not set
+CONFIG_RESET=y
 CONFIG_RESIZE=y
 CONFIG_FEATURE_RESIZE_PRINT=y
 CONFIG_SETCONSOLE=y
-CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS=y
+# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
 # CONFIG_SETFONT is not set
 # CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
 CONFIG_DEFAULT_SETFONT_DIR=""
 CONFIG_SETKEYCODES=y
 CONFIG_SETLOGCONS=y
 CONFIG_SHOWKEY=y
-
-#
-# Common options for loadfont and setfont
-#
-CONFIG_FEATURE_LOADFONT_PSF2=y
-CONFIG_FEATURE_LOADFONT_RAW=y
+# CONFIG_FEATURE_LOADFONT_PSF2 is not set
+# CONFIG_FEATURE_LOADFONT_RAW is not set
 
 #
 # Debian Utilities
 #
-# CONFIG_MKTEMP is not set
 CONFIG_MKTEMP=y
-# CONFIG_PIPE_PROGRESS is not set
-# CONFIG_RUN_PARTS is not set
+CONFIG_PIPE_PROGRESS=y
+CONFIG_RUN_PARTS=y
 # CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set
-# CONFIG_FEATURE_RUN_PARTS_FANCY is not set
-# CONFIG_START_STOP_DAEMON is not set
-# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set
+CONFIG_FEATURE_RUN_PARTS_FANCY=y
+CONFIG_START_STOP_DAEMON=y
+CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y
 # CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set
-# CONFIG_WHICH is not set
+CONFIG_WHICH=y
 
 #
 # Editors
 #
-CONFIG_AWK=y
-CONFIG_FEATURE_AWK_LIBM=y
-CONFIG_CMP=y
-CONFIG_DIFF=y
-CONFIG_FEATURE_DIFF_LONG_OPTIONS=y
-CONFIG_FEATURE_DIFF_DIR=y
-CONFIG_ED=y
 CONFIG_PATCH=y
-CONFIG_SED=y
 CONFIG_VI=y
 CONFIG_FEATURE_VI_MAX_LEN=4096
 CONFIG_FEATURE_VI_8BIT=y
 CONFIG_FEATURE_VI_COLON=y
 CONFIG_FEATURE_VI_YANKMARK=y
 CONFIG_FEATURE_VI_SEARCH=y
+# CONFIG_FEATURE_VI_REGEX_SEARCH is not set
 CONFIG_FEATURE_VI_USE_SIGNALS=y
 CONFIG_FEATURE_VI_DOT_CMD=y
 CONFIG_FEATURE_VI_READONLY=y
@@ -370,7 +371,14 @@ CONFIG_FEATURE_VI_SETOPTS=y
 CONFIG_FEATURE_VI_SET=y
 CONFIG_FEATURE_VI_WIN_RESIZE=y
 CONFIG_FEATURE_VI_ASK_TERMINAL=y
-CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y
+CONFIG_AWK=y
+CONFIG_FEATURE_AWK_LIBM=y
+CONFIG_CMP=y
+CONFIG_DIFF=y
+# CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set
+CONFIG_FEATURE_DIFF_DIR=y
+CONFIG_ED=y
+CONFIG_SED=y
 CONFIG_FEATURE_ALLOW_EXEC=y
 
 #
@@ -412,81 +420,86 @@ CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
 #
 # Init Utilities
 #
-# CONFIG_BOOTCHARTD is not set
-# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set
-# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set
-# CONFIG_INIT is not set
-# CONFIG_FEATURE_USE_INITTAB is not set
-# CONFIG_FEATURE_KILL_REMOVED is not set
-CONFIG_FEATURE_KILL_DELAY=0
-# CONFIG_FEATURE_INIT_SCTTY is not set
-# CONFIG_FEATURE_INIT_SYSLOG is not set
-# CONFIG_FEATURE_EXTRA_QUIET is not set
-# CONFIG_FEATURE_INIT_COREDUMPS is not set
-# CONFIG_FEATURE_INITRD is not set
-CONFIG_INIT_TERMINAL_TYPE=""
-# CONFIG_HALT is not set
+CONFIG_BOOTCHARTD=y
+CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER=y
+CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE=y
+CONFIG_HALT=y
 # CONFIG_FEATURE_CALL_TELINIT is not set
 CONFIG_TELINIT_PATH=""
-# CONFIG_MESG is not set
+CONFIG_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+# CONFIG_FEATURE_KILL_REMOVED is not set
+CONFIG_FEATURE_KILL_DELAY=0
+CONFIG_FEATURE_INIT_SCTTY=y
+CONFIG_FEATURE_INIT_SYSLOG=y
+CONFIG_FEATURE_EXTRA_QUIET=y
+CONFIG_FEATURE_INIT_COREDUMPS=y
+CONFIG_FEATURE_INITRD=y
+CONFIG_INIT_TERMINAL_TYPE="linux"
+CONFIG_MESG=y
+CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y
 
 #
 # Login/Password Management Utilities
 #
-CONFIG_FEATURE_SHADOWPASSWDS=y
+# CONFIG_ADD_SHELL is not set
+# CONFIG_REMOVE_SHELL is not set
+# CONFIG_FEATURE_SHADOWPASSWDS is not set
 # CONFIG_USE_BB_PWD_GRP is not set
 # CONFIG_USE_BB_SHADOW is not set
 # CONFIG_USE_BB_CRYPT is not set
 # CONFIG_USE_BB_CRYPT_SHA is not set
-CONFIG_ADDGROUP=y
-CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y
-CONFIG_FEATURE_ADDUSER_TO_GROUP=y
-CONFIG_DELGROUP=y
-CONFIG_FEATURE_DEL_USER_FROM_GROUP=y
-CONFIG_FEATURE_CHECK_NAMES=y
-CONFIG_ADDUSER=y
-CONFIG_FEATURE_ADDUSER_LONG_OPTIONS=y
+# CONFIG_ADDUSER is not set
+# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set
+# CONFIG_FEATURE_CHECK_NAMES is not set
 CONFIG_FIRST_SYSTEM_ID=0
-CONFIG_LAST_SYSTEM_ID=64900
-CONFIG_DELUSER=y
-CONFIG_GETTY=y
+CONFIG_LAST_SYSTEM_ID=0
+# CONFIG_ADDGROUP is not set
+# CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set
+# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
+# CONFIG_DELUSER is not set
+# CONFIG_DELGROUP is not set
+# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
+# CONFIG_GETTY is not set
 # CONFIG_LOGIN is not set
+# CONFIG_LOGIN_SESSION_AS_CHILD is not set
 # CONFIG_PAM is not set
 # CONFIG_LOGIN_SCRIPTS is not set
 # CONFIG_FEATURE_NOLOGIN is not set
 # CONFIG_FEATURE_SECURETTY is not set
-CONFIG_PASSWD=y
-CONFIG_FEATURE_PASSWD_WEAK_CHECK=y
+# CONFIG_PASSWD is not set
+# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set
 # CONFIG_CRYPTPW is not set
-CONFIG_CHPASSWD=y
+# CONFIG_CHPASSWD is not set
+CONFIG_FEATURE_DEFAULT_PASSWD_ALGO=""
 # CONFIG_SU is not set
 # CONFIG_FEATURE_SU_SYSLOG is not set
 # CONFIG_FEATURE_SU_CHECKS_SHELLS is not set
 # CONFIG_SULOGIN is not set
-CONFIG_VLOCK=y
+# CONFIG_VLOCK is not set
 
 #
 # Linux Ext2 FS Progs
 #
-# CONFIG_CHATTR is not set
+CONFIG_CHATTR=y
 # CONFIG_FSCK is not set
-# CONFIG_LSATTR is not set
-# CONFIG_TUNE2FS is not set
+CONFIG_LSATTR=y
+CONFIG_TUNE2FS=y
 
 #
 # Linux Module Utilities
 #
 CONFIG_MODINFO=y
-# CONFIG_MODPROBE_SMALL is not set
-# CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set
-# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set
-CONFIG_INSMOD=y
-CONFIG_RMMOD=y
-CONFIG_LSMOD=y
-CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT=y
-CONFIG_MODPROBE=y
-CONFIG_FEATURE_MODPROBE_BLACKLIST=y
-CONFIG_DEPMOD=y
+CONFIG_MODPROBE_SMALL=y
+CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE=y
+CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y
+# CONFIG_INSMOD is not set
+# CONFIG_RMMOD is not set
+# CONFIG_LSMOD is not set
+# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set
+# CONFIG_MODPROBE is not set
+# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set
+# CONFIG_DEPMOD is not set
 
 #
 # Options common to multiple modutils
@@ -498,20 +511,27 @@ CONFIG_DEPMOD=y
 # CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
 # CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
 # CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set
-CONFIG_FEATURE_CHECK_TAINTED_MODULE=y
-CONFIG_FEATURE_MODUTILS_ALIAS=y
-CONFIG_FEATURE_MODUTILS_SYMBOLS=y
-CONFIG_DEFAULT_MODULES_DIR="/lib/modules"
+# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
+# CONFIG_FEATURE_MODUTILS_ALIAS is not set
+# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set
+CONFIG_DEFAULT_MODULES_DIR="/system/lib/modules"
 CONFIG_DEFAULT_DEPMOD_FILE="modules.dep"
 
 #
 # Linux System Utilities
 #
 CONFIG_BLOCKDEV=y
+CONFIG_MDEV=y
+CONFIG_FEATURE_MDEV_CONF=y
+CONFIG_FEATURE_MDEV_RENAME=y
+CONFIG_FEATURE_MDEV_RENAME_REGEXP=y
+CONFIG_FEATURE_MDEV_EXEC=y
+CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y
 CONFIG_REV=y
 # CONFIG_ACPID is not set
 # CONFIG_FEATURE_ACPID_COMPAT is not set
 CONFIG_BLKID=y
+CONFIG_FEATURE_BLKID_TYPE=y
 CONFIG_DMESG=y
 CONFIG_FEATURE_DMESG_PRETTY=y
 CONFIG_FBSET=y
@@ -526,67 +546,57 @@ CONFIG_FEATURE_FDISK_WRITABLE=y
 # CONFIG_FEATURE_SGI_LABEL is not set
 # CONFIG_FEATURE_SUN_LABEL is not set
 # CONFIG_FEATURE_OSF_LABEL is not set
+# CONFIG_FEATURE_GPT_LABEL is not set
 CONFIG_FEATURE_FDISK_ADVANCED=y
-# CONFIG_FINDFS is not set
+CONFIG_FINDFS=y
 CONFIG_FLOCK=y
 CONFIG_FREERAMDISK=y
-CONFIG_FSCK_MINIX=y
+# CONFIG_FSCK_MINIX is not set
 # CONFIG_MKFS_EXT2 is not set
-CONFIG_MKFS_MINIX=y
-CONFIG_FEATURE_MINIX2=y
+# CONFIG_MKFS_MINIX is not set
+# CONFIG_FEATURE_MINIX2 is not set
 # CONFIG_MKFS_REISER is not set
-CONFIG_MKFS_VFAT=y
+# CONFIG_MKFS_VFAT is not set
 CONFIG_GETOPT=y
 CONFIG_FEATURE_GETOPT_LONG=y
 CONFIG_HEXDUMP=y
 CONFIG_FEATURE_HEXDUMP_REVERSE=y
 CONFIG_HD=y
 CONFIG_HWCLOCK=y
-CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS=y
-CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS=y
-CONFIG_IPCRM=y
-CONFIG_IPCS=y
+# CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set
+# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set
+# CONFIG_IPCRM is not set
+# CONFIG_IPCS is not set
 CONFIG_LOSETUP=y
-# CONFIG_LSPCI is not set
-# CONFIG_LSUSB is not set
-CONFIG_MDEV=y
-CONFIG_FEATURE_MDEV_CONF=y
-CONFIG_FEATURE_MDEV_RENAME=y
-CONFIG_FEATURE_MDEV_RENAME_REGEXP=y
-CONFIG_FEATURE_MDEV_EXEC=y
-CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y
+CONFIG_LSPCI=y
+CONFIG_LSUSB=y
 CONFIG_MKSWAP=y
 CONFIG_FEATURE_MKSWAP_UUID=y
 CONFIG_MORE=y
-CONFIG_FEATURE_USE_TERMIOS=y
-CONFIG_MOUNT=y
-CONFIG_FEATURE_MOUNT_FAKE=y
-CONFIG_FEATURE_MOUNT_VERBOSE=y
-CONFIG_FEATURE_MOUNT_HELPERS=y
-CONFIG_FEATURE_MOUNT_LABEL=y
-CONFIG_FEATURE_MOUNT_NFS=y
-CONFIG_FEATURE_MOUNT_CIFS=y
-CONFIG_FEATURE_MOUNT_FLAGS=y
-CONFIG_FEATURE_MOUNT_FSTAB=y
-CONFIG_PIVOT_ROOT=y
-CONFIG_RDATE=y
+# CONFIG_MOUNT is not set
+# CONFIG_FEATURE_MOUNT_FAKE is not set
+# CONFIG_FEATURE_MOUNT_VERBOSE is not set
+# CONFIG_FEATURE_MOUNT_HELPERS is not set
+# CONFIG_FEATURE_MOUNT_LABEL is not set
+# CONFIG_FEATURE_MOUNT_NFS is not set
+# CONFIG_FEATURE_MOUNT_CIFS is not set
+# CONFIG_FEATURE_MOUNT_FLAGS is not set
+# CONFIG_FEATURE_MOUNT_FSTAB is not set
+# CONFIG_PIVOT_ROOT is not set
+# CONFIG_RDATE is not set
 CONFIG_RDEV=y
 CONFIG_READPROFILE=y
 CONFIG_RTCWAKE=y
 CONFIG_SCRIPT=y
 CONFIG_SCRIPTREPLAY=y
-CONFIG_SETARCH=y
-CONFIG_SWAPONOFF=y
-CONFIG_FEATURE_SWAPON_PRI=y
+# CONFIG_SETARCH is not set
+# CONFIG_SWAPONOFF is not set
+# CONFIG_FEATURE_SWAPON_PRI is not set
 CONFIG_SWITCH_ROOT=y
-CONFIG_UMOUNT=y
-CONFIG_FEATURE_UMOUNT_ALL=y
-
-#
-# Common options for mount/umount
-#
-CONFIG_FEATURE_MOUNT_LOOP=y
-CONFIG_FEATURE_MOUNT_LOOP_CREATE=y
+# CONFIG_UMOUNT is not set
+# CONFIG_FEATURE_UMOUNT_ALL is not set
+# CONFIG_FEATURE_MOUNT_LOOP is not set
+# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set
 # CONFIG_FEATURE_MTAB_SUPPORT is not set
 CONFIG_VOLUMEID=y
 
@@ -615,13 +625,31 @@ CONFIG_FEATURE_VOLUMEID_LINUXRAID=y
 # Miscellaneous Utilities
 #
 # CONFIG_CONSPY is not set
-CONFIG_UBIATTACH=y
-CONFIG_UBIDETACH=y
-CONFIG_ADJTIMEX=y
-# CONFIG_BBCONFIG is not set
-# CONFIG_BEEP is not set
-CONFIG_FEATURE_BEEP_FREQ=0
-CONFIG_FEATURE_BEEP_LENGTH_MS=0
+CONFIG_LESS=y
+CONFIG_FEATURE_LESS_MAXLINES=9999999
+CONFIG_FEATURE_LESS_BRACKETS=y
+CONFIG_FEATURE_LESS_FLAGS=y
+CONFIG_FEATURE_LESS_MARKS=y
+CONFIG_FEATURE_LESS_REGEXP=y
+CONFIG_FEATURE_LESS_WINCH=y
+CONFIG_FEATURE_LESS_ASK_TERMINAL=y
+CONFIG_FEATURE_LESS_DASHCMD=y
+CONFIG_FEATURE_LESS_LINENUMS=y
+# CONFIG_NANDWRITE is not set
+CONFIG_NANDDUMP=y
+CONFIG_SETSERIAL=y
+# CONFIG_UBIATTACH is not set
+# CONFIG_UBIDETACH is not set
+# CONFIG_UBIMKVOL is not set
+# CONFIG_UBIRMVOL is not set
+# CONFIG_UBIRSVOL is not set
+# CONFIG_UBIUPDATEVOL is not set
+# CONFIG_ADJTIMEX is not set
+CONFIG_BBCONFIG=y
+CONFIG_FEATURE_COMPRESS_BBCONFIG=y
+CONFIG_BEEP=y
+CONFIG_FEATURE_BEEP_FREQ=4000
+CONFIG_FEATURE_BEEP_LENGTH_MS=30
 CONFIG_CHAT=y
 CONFIG_FEATURE_CHAT_NOFAIL=y
 # CONFIG_FEATURE_CHAT_TTY_HIFI is not set
@@ -643,28 +671,19 @@ CONFIG_FEATURE_DC_LIBM=y
 # CONFIG_DEVFSD_FG_NP is not set
 # CONFIG_DEVFSD_VERBOSE is not set
 # CONFIG_FEATURE_DEVFS is not set
-# CONFIG_DEVMEM is not set
-CONFIG_EJECT=y
-CONFIG_FEATURE_EJECT_SCSI=y
+CONFIG_DEVMEM=y
+# CONFIG_EJECT is not set
+# CONFIG_FEATURE_EJECT_SCSI is not set
 CONFIG_FBSPLASH=y
 CONFIG_FLASHCP=y
 CONFIG_FLASH_LOCK=y
 CONFIG_FLASH_UNLOCK=y
-CONFIG_FLASH_ERASEALL=y
-CONFIG_IONICE=y
+# CONFIG_FLASH_ERASEALL is not set
+# CONFIG_IONICE is not set
 CONFIG_INOTIFYD=y
 # CONFIG_LAST is not set
 # CONFIG_FEATURE_LAST_SMALL is not set
 # CONFIG_FEATURE_LAST_FANCY is not set
-CONFIG_LESS=y
-CONFIG_FEATURE_LESS_MAXLINES=9999999
-CONFIG_FEATURE_LESS_BRACKETS=y
-CONFIG_FEATURE_LESS_FLAGS=y
-CONFIG_FEATURE_LESS_MARKS=y
-CONFIG_FEATURE_LESS_REGEXP=y
-CONFIG_FEATURE_LESS_WINCH=y
-CONFIG_FEATURE_LESS_DASHCMD=y
-CONFIG_FEATURE_LESS_LINENUMS=y
 CONFIG_HDPARM=y
 CONFIG_FEATURE_HDPARM_GET_IDENTITY=y
 CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y
@@ -676,70 +695,78 @@ CONFIG_MAKEDEVS=y
 # CONFIG_FEATURE_MAKEDEVS_LEAF is not set
 CONFIG_FEATURE_MAKEDEVS_TABLE=y
 CONFIG_MAN=y
-CONFIG_MICROCOM=y
-CONFIG_MOUNTPOINT=y
-CONFIG_MT=y
+# CONFIG_MICROCOM is not set
+# CONFIG_MOUNTPOINT is not set
+# CONFIG_MT is not set
 CONFIG_RAIDAUTORUN=y
-CONFIG_READAHEAD=y
+# CONFIG_READAHEAD is not set
 # CONFIG_RFKILL is not set
 # CONFIG_RUNLEVEL is not set
 CONFIG_RX=y
 CONFIG_SETSID=y
 CONFIG_STRINGS=y
-CONFIG_TASKSET=y
-CONFIG_FEATURE_TASKSET_FANCY=y
+# CONFIG_TASKSET is not set
+# CONFIG_FEATURE_TASKSET_FANCY is not set
 CONFIG_TIME=y
 CONFIG_TIMEOUT=y
 CONFIG_TTYSIZE=y
 CONFIG_VOLNAME=y
-CONFIG_WALL=y
-CONFIG_WATCHDOG=y
+# CONFIG_WALL is not set
+# CONFIG_WATCHDOG is not set
 
 #
 # Networking Utilities
 #
+# CONFIG_NAMEIF is not set
+# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
+CONFIG_NBDCLIENT=y
 CONFIG_NC=y
 CONFIG_NC_SERVER=y
 CONFIG_NC_EXTRA=y
-CONFIG_NC_110_COMPAT=y
-CONFIG_FEATURE_IPV6=y
+# CONFIG_NC_110_COMPAT is not set
+CONFIG_PING=y
+# CONFIG_PING6 is not set
+CONFIG_FEATURE_FANCY_PING=y
+CONFIG_WHOIS=y
+# CONFIG_FEATURE_IPV6 is not set
 # CONFIG_FEATURE_UNIX_LOCAL is not set
 # CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set
 # CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
 CONFIG_ARP=y
-CONFIG_ARPING=y
-CONFIG_BRCTL=y
-CONFIG_FEATURE_BRCTL_FANCY=y
-CONFIG_FEATURE_BRCTL_SHOW=y
+# CONFIG_ARPING is not set
+# CONFIG_BRCTL is not set
+# CONFIG_FEATURE_BRCTL_FANCY is not set
+# CONFIG_FEATURE_BRCTL_SHOW is not set
 CONFIG_DNSD=y
-CONFIG_ETHER_WAKE=y
+# CONFIG_ETHER_WAKE is not set
 CONFIG_FAKEIDENTD=y
-# CONFIG_FTPD is not set
-# CONFIG_FEATURE_FTP_WRITE is not set
-# CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set
+CONFIG_FTPD=y
+CONFIG_FEATURE_FTP_WRITE=y
+CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y
 CONFIG_FTPGET=y
 CONFIG_FTPPUT=y
-CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
-CONFIG_HOSTNAME=y
+# CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set
+# CONFIG_HOSTNAME is not set
 CONFIG_HTTPD=y
 CONFIG_FEATURE_HTTPD_RANGES=y
 CONFIG_FEATURE_HTTPD_USE_SENDFILE=y
 CONFIG_FEATURE_HTTPD_SETUID=y
 CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
-CONFIG_FEATURE_HTTPD_AUTH_MD5=y
+# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
 CONFIG_FEATURE_HTTPD_CGI=y
 CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y
 CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y
 CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
 CONFIG_FEATURE_HTTPD_ERROR_PAGES=y
 CONFIG_FEATURE_HTTPD_PROXY=y
+CONFIG_FEATURE_HTTPD_GZIP=y
 CONFIG_IFCONFIG=y
 CONFIG_FEATURE_IFCONFIG_STATUS=y
-CONFIG_FEATURE_IFCONFIG_SLIP=y
+# CONFIG_FEATURE_IFCONFIG_SLIP is not set
 CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y
 CONFIG_FEATURE_IFCONFIG_HW=y
 CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y
-CONFIG_IFENSLAVE=y
+# CONFIG_IFENSLAVE is not set
 # CONFIG_IFPLUGD is not set
 CONFIG_IFUPDOWN=y
 CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate"
@@ -747,16 +774,16 @@ CONFIG_FEATURE_IFUPDOWN_IP=y
 CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y
 # CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set
 CONFIG_FEATURE_IFUPDOWN_IPV4=y
-CONFIG_FEATURE_IFUPDOWN_IPV6=y
+# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set
 CONFIG_FEATURE_IFUPDOWN_MAPPING=y
-# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set
-CONFIG_INETD=y
-CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO=y
-CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD=y
-CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME=y
-CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME=y
-CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN=y
-CONFIG_FEATURE_INETD_RPC=y
+CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP=y
+# CONFIG_INETD is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set
+# CONFIG_FEATURE_INETD_RPC is not set
 CONFIG_IP=y
 CONFIG_FEATURE_IP_ADDRESS=y
 CONFIG_FEATURE_IP_LINK=y
@@ -764,7 +791,7 @@ CONFIG_FEATURE_IP_ROUTE=y
 CONFIG_FEATURE_IP_TUNNEL=y
 CONFIG_FEATURE_IP_RULE=y
 CONFIG_FEATURE_IP_SHORT_FORMS=y
-CONFIG_FEATURE_IP_RARE_PROTOCOLS=y
+# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
 CONFIG_IPADDR=y
 CONFIG_IPLINK=y
 CONFIG_IPROUTE=y
@@ -772,21 +799,16 @@ CONFIG_IPTUNNEL=y
 CONFIG_IPRULE=y
 CONFIG_IPCALC=y
 CONFIG_FEATURE_IPCALC_FANCY=y
-CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y
-CONFIG_NAMEIF=y
-CONFIG_FEATURE_NAMEIF_EXTENDED=y
+# CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set
 CONFIG_NETSTAT=y
 CONFIG_FEATURE_NETSTAT_WIDE=y
 CONFIG_FEATURE_NETSTAT_PRG=y
-CONFIG_NSLOOKUP=y
+# CONFIG_NSLOOKUP is not set
 # CONFIG_NTPD is not set
 # CONFIG_FEATURE_NTPD_SERVER is not set
-CONFIG_PING=y
-CONFIG_PING6=y
-CONFIG_FEATURE_FANCY_PING=y
 CONFIG_PSCAN=y
 CONFIG_ROUTE=y
-CONFIG_SLATTACH=y
+# CONFIG_SLATTACH is not set
 CONFIG_TCPSVD=y
 CONFIG_TELNET=y
 CONFIG_FEATURE_TELNET_TTYPE=y
@@ -804,73 +826,86 @@ CONFIG_FEATURE_TFTP_GET=y
 CONFIG_FEATURE_TFTP_PUT=y
 CONFIG_FEATURE_TFTP_BLOCKSIZE=y
 CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
-CONFIG_TFTP_DEBUG=y
+# CONFIG_TFTP_DEBUG is not set
 CONFIG_TRACEROUTE=y
 # CONFIG_TRACEROUTE6 is not set
 CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
-CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE=y
-CONFIG_FEATURE_TRACEROUTE_USE_ICMP=y
-# CONFIG_TUNCTL is not set
-# CONFIG_FEATURE_TUNCTL_UG is not set
-CONFIG_UDHCPD=y
-CONFIG_DHCPRELAY=y
-CONFIG_DUMPLEASES=y
-CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY=y
-CONFIG_DHCPD_LEASES_FILE="/var/lib/misc/udhcpd.leases"
+# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
+# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
+CONFIG_TUNCTL=y
+CONFIG_FEATURE_TUNCTL_UG=y
+# CONFIG_UDHCPC6 is not set
+# CONFIG_UDHCPD is not set
+# CONFIG_DHCPRELAY is not set
+# CONFIG_DUMPLEASES is not set
+# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
+# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set
+CONFIG_DHCPD_LEASES_FILE=""
 CONFIG_UDHCPC=y
 CONFIG_FEATURE_UDHCPC_ARPING=y
 CONFIG_FEATURE_UDHCP_PORT=y
-CONFIG_UDHCP_DEBUG=0
+CONFIG_UDHCP_DEBUG=9
 CONFIG_FEATURE_UDHCP_RFC3397=y
+CONFIG_FEATURE_UDHCP_8021Q=y
 CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script"
 CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80
-CONFIG_FEATURE_UDHCPC_FAST_REQUEST=y
 CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n"
 CONFIG_UDPSVD=y
 CONFIG_VCONFIG=y
 CONFIG_WGET=y
 CONFIG_FEATURE_WGET_STATUSBAR=y
 CONFIG_FEATURE_WGET_AUTHENTICATION=y
-CONFIG_FEATURE_WGET_LONG_OPTIONS=y
-CONFIG_ZCIP=y
+# CONFIG_FEATURE_WGET_LONG_OPTIONS is not set
+CONFIG_FEATURE_WGET_TIMEOUT=y
+# CONFIG_ZCIP is not set
 
 #
 # Print Utilities
 #
-# CONFIG_LPD is not set
-# CONFIG_LPR is not set
-# CONFIG_LPQ is not set
+CONFIG_LPD=y
+CONFIG_LPR=y
+CONFIG_LPQ=y
 
 #
 # Mail Utilities
 #
-# CONFIG_MAKEMIME is not set
+CONFIG_MAKEMIME=y
 CONFIG_FEATURE_MIME_CHARSET="us-ascii"
-# CONFIG_POPMAILDIR is not set
-# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set
-# CONFIG_REFORMIME is not set
-# CONFIG_FEATURE_REFORMIME_COMPAT is not set
+CONFIG_POPMAILDIR=y
+CONFIG_FEATURE_POPMAILDIR_DELIVERY=y
+CONFIG_REFORMIME=y
+CONFIG_FEATURE_REFORMIME_COMPAT=y
 CONFIG_SENDMAIL=y
 
 #
 # Process Utilities
 #
-# CONFIG_SMEMCAP is not set
+CONFIG_IOSTAT=y
+CONFIG_LSOF=y
+CONFIG_MPSTAT=y
+CONFIG_NMETER=y
+CONFIG_PMAP=y
+CONFIG_POWERTOP=y
+CONFIG_PSTREE=y
+CONFIG_PWDX=y
+CONFIG_SMEMCAP=y
+CONFIG_UPTIME=y
+# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
 CONFIG_FREE=y
 CONFIG_FUSER=y
-CONFIG_KILL=y
-CONFIG_KILLALL=y
+# CONFIG_KILL is not set
+# CONFIG_KILLALL is not set
 # CONFIG_KILLALL5 is not set
-CONFIG_NMETER=y
-CONFIG_PGREP=y
-# CONFIG_PIDOF is not set
-# CONFIG_FEATURE_PIDOF_SINGLE is not set
-# CONFIG_FEATURE_PIDOF_OMIT is not set
-CONFIG_PKILL=y
+# CONFIG_PGREP is not set
+CONFIG_PIDOF=y
+CONFIG_FEATURE_PIDOF_SINGLE=y
+CONFIG_FEATURE_PIDOF_OMIT=y
+# CONFIG_PKILL is not set
 CONFIG_PS=y
-CONFIG_FEATURE_PS_WIDE=y
-# CONFIG_FEATURE_PS_TIME is not set
-# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set
+# CONFIG_FEATURE_PS_WIDE is not set
+# CONFIG_FEATURE_PS_LONG is not set
+CONFIG_FEATURE_PS_TIME=y
+CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y
 # CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
 CONFIG_RENICE=y
 CONFIG_BB_SYSCTL=y
@@ -882,7 +917,6 @@ CONFIG_FEATURE_TOP_DECIMALS=y
 CONFIG_FEATURE_TOP_SMP_PROCESS=y
 CONFIG_FEATURE_TOPMEM=y
 CONFIG_FEATURE_SHOW_THREADS=y
-CONFIG_UPTIME=y
 CONFIG_WATCH=y
 
 #
@@ -890,7 +924,7 @@ CONFIG_WATCH=y
 #
 CONFIG_RUNSV=y
 CONFIG_RUNSVDIR=y
-CONFIG_FEATURE_RUNSVDIR_LOG=y
+# CONFIG_FEATURE_RUNSVDIR_LOG is not set
 CONFIG_SV=y
 CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service"
 CONFIG_SVLOGD=y
@@ -918,23 +952,27 @@ CONFIG_SOFTLIMIT=y
 #
 # Shells
 #
-CONFIG_ASH=y
-CONFIG_ASH_BASH_COMPAT=y
-CONFIG_ASH_JOB_CONTROL=y
-CONFIG_ASH_ALIAS=y
-CONFIG_ASH_GETOPTS=y
-CONFIG_ASH_BUILTIN_ECHO=y
-CONFIG_ASH_BUILTIN_PRINTF=y
-CONFIG_ASH_BUILTIN_TEST=y
-CONFIG_ASH_CMDCMD=y
+# CONFIG_ASH is not set
+# CONFIG_ASH_BASH_COMPAT is not set
+# CONFIG_ASH_IDLE_TIMEOUT is not set
+# CONFIG_ASH_JOB_CONTROL is not set
+# CONFIG_ASH_ALIAS is not set
+# CONFIG_ASH_GETOPTS is not set
+# CONFIG_ASH_BUILTIN_ECHO is not set
+# CONFIG_ASH_BUILTIN_PRINTF is not set
+# CONFIG_ASH_BUILTIN_TEST is not set
+# CONFIG_ASH_CMDCMD is not set
 # CONFIG_ASH_MAIL is not set
-CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
-CONFIG_ASH_RANDOM_SUPPORT=y
-CONFIG_ASH_EXPAND_PRMT=y
+# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set
+# CONFIG_ASH_RANDOM_SUPPORT is not set
+# CONFIG_ASH_EXPAND_PRMT is not set
+CONFIG_CTTYHACK=y
 # CONFIG_HUSH is not set
 # CONFIG_HUSH_BASH_COMPAT is not set
+# CONFIG_HUSH_BRACE_EXPANSION is not set
 # CONFIG_HUSH_HELP is not set
 # CONFIG_HUSH_INTERACTIVE is not set
+# CONFIG_HUSH_SAVEHISTORY is not set
 # CONFIG_HUSH_JOB is not set
 # CONFIG_HUSH_TICK is not set
 # CONFIG_HUSH_IF is not set
@@ -942,35 +980,36 @@ CONFIG_ASH_EXPAND_PRMT=y
 # CONFIG_HUSH_CASE is not set
 # CONFIG_HUSH_FUNCTIONS is not set
 # CONFIG_HUSH_LOCAL is not set
-# CONFIG_HUSH_EXPORT_N is not set
 # CONFIG_HUSH_RANDOM_SUPPORT is not set
-CONFIG_FEATURE_SH_IS_ASH=y
+# CONFIG_HUSH_EXPORT_N is not set
+# CONFIG_HUSH_MODE_X is not set
+# CONFIG_MSH is not set
+# CONFIG_FEATURE_SH_IS_ASH is not set
 # CONFIG_FEATURE_SH_IS_HUSH is not set
-# CONFIG_FEATURE_SH_IS_NONE is not set
+CONFIG_FEATURE_SH_IS_NONE=y
 # CONFIG_FEATURE_BASH_IS_ASH is not set
 # CONFIG_FEATURE_BASH_IS_HUSH is not set
 CONFIG_FEATURE_BASH_IS_NONE=y
-# CONFIG_LASH is not set
-# CONFIG_MSH is not set
-CONFIG_SH_MATH_SUPPORT=y
-CONFIG_SH_MATH_SUPPORT_64=y
-CONFIG_FEATURE_SH_EXTRA_QUIET=y
+# CONFIG_SH_MATH_SUPPORT is not set
+# CONFIG_SH_MATH_SUPPORT_64 is not set
+# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
 # CONFIG_FEATURE_SH_STANDALONE is not set
 # CONFIG_FEATURE_SH_NOFORK is not set
-# CONFIG_CTTYHACK is not set
+# CONFIG_FEATURE_SH_HISTFILESIZE is not set
 
 #
 # System Logging Utilities
 #
-CONFIG_SYSLOGD=y
-CONFIG_FEATURE_ROTATE_LOGFILE=y
-CONFIG_FEATURE_REMOTE_LOG=y
-CONFIG_FEATURE_SYSLOGD_DUP=y
-CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256
-CONFIG_FEATURE_IPC_SYSLOG=y
-CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16
-CONFIG_LOGREAD=y
-CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y
+# CONFIG_SYSLOGD is not set
+# CONFIG_FEATURE_ROTATE_LOGFILE is not set
+# CONFIG_FEATURE_REMOTE_LOG is not set
+# CONFIG_FEATURE_SYSLOGD_DUP is not set
+# CONFIG_FEATURE_SYSLOGD_CFG is not set
+CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0
+# CONFIG_FEATURE_IPC_SYSLOG is not set
+CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0
+# CONFIG_LOGREAD is not set
+# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
 CONFIG_KLOGD=y
 CONFIG_FEATURE_KLOGD_KLOGCTL=y
-CONFIG_LOGGER=y
+# CONFIG_LOGGER is not set
diff --git a/configs/cygwin_defconfig b/configs/cygwin_defconfig
new file mode 100644 (file)
index 0000000..aa346e3
--- /dev/null
@@ -0,0 +1,996 @@
+#
+# Automatically generated make config: don't edit
+# Busybox version: 1.19.0.git
+# Sun Jul 10 12:48:50 2011
+#
+CONFIG_HAVE_DOT_CONFIG=y
+
+#
+# Busybox Settings
+#
+
+#
+# General Configuration
+#
+CONFIG_DESKTOP=y
+# CONFIG_EXTRA_COMPAT is not set
+CONFIG_INCLUDE_SUSv2=y
+# CONFIG_USE_PORTABLE_CODE is not set
+CONFIG_PLATFORM_LINUX=y
+CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
+# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
+# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
+CONFIG_SHOW_USAGE=y
+CONFIG_FEATURE_VERBOSE_USAGE=y
+CONFIG_FEATURE_COMPRESS_USAGE=y
+CONFIG_FEATURE_INSTALLER=y
+# CONFIG_INSTALL_NO_USR is not set
+# CONFIG_LOCALE_SUPPORT is not set
+CONFIG_UNICODE_SUPPORT=y
+# CONFIG_UNICODE_USING_LOCALE is not set
+# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
+CONFIG_SUBST_WCHAR=65533
+CONFIG_LAST_SUPPORTED_WCHAR=0
+# CONFIG_UNICODE_COMBINING_WCHARS is not set
+# CONFIG_UNICODE_WIDE_WCHARS is not set
+# CONFIG_UNICODE_BIDI_SUPPORT is not set
+# CONFIG_UNICODE_NEUTRAL_TABLE is not set
+# CONFIG_UNICODE_PRESERVE_BROKEN is not set
+CONFIG_LONG_OPTS=y
+CONFIG_FEATURE_DEVPTS=y
+# CONFIG_FEATURE_CLEAN_UP is not set
+# CONFIG_FEATURE_UTMP is not set
+# CONFIG_FEATURE_WTMP is not set
+CONFIG_FEATURE_PIDFILE=y
+CONFIG_FEATURE_SUID=y
+# CONFIG_FEATURE_SUID_CONFIG is not set
+# CONFIG_FEATURE_SUID_CONFIG_QUIET is not set
+# CONFIG_SELINUX is not set
+# CONFIG_FEATURE_PREFER_APPLETS is not set
+CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
+CONFIG_FEATURE_SYSLOG=y
+# CONFIG_FEATURE_HAVE_RPC is not set
+
+#
+# Build Options
+#
+# CONFIG_STATIC is not set
+# CONFIG_PIE is not set
+# CONFIG_NOMMU is not set
+# CONFIG_BUILD_LIBBUSYBOX is not set
+# CONFIG_FEATURE_INDIVIDUAL is not set
+# CONFIG_FEATURE_SHARED_BUSYBOX is not set
+CONFIG_LFS=y
+CONFIG_CROSS_COMPILER_PREFIX=""
+CONFIG_EXTRA_CFLAGS=""
+
+#
+# Debugging Options
+#
+# CONFIG_DEBUG is not set
+# CONFIG_DEBUG_PESSIMIZE is not set
+# CONFIG_WERROR is not set
+CONFIG_NO_DEBUG_LIB=y
+# CONFIG_DMALLOC is not set
+# CONFIG_EFENCE is not set
+
+#
+# Installation Options ("make install" behavior)
+#
+CONFIG_INSTALL_APPLET_SYMLINKS=y
+# CONFIG_INSTALL_APPLET_HARDLINKS is not set
+# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
+# CONFIG_INSTALL_APPLET_DONT is not set
+# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
+# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
+# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
+CONFIG_PREFIX="./_install"
+
+#
+# Busybox Library Tuning
+#
+# CONFIG_FEATURE_SYSTEMD is not set
+CONFIG_FEATURE_RTMINMAX=y
+CONFIG_PASSWORD_MINLEN=6
+CONFIG_MD5_SMALL=1
+CONFIG_FEATURE_FAST_TOP=y
+# CONFIG_FEATURE_ETC_NETWORKS is not set
+CONFIG_FEATURE_USE_TERMIOS=y
+CONFIG_FEATURE_EDITING=y
+CONFIG_FEATURE_EDITING_MAX_LEN=1024
+# CONFIG_FEATURE_EDITING_VI is not set
+CONFIG_FEATURE_EDITING_HISTORY=255
+CONFIG_FEATURE_EDITING_SAVEHISTORY=y
+CONFIG_FEATURE_TAB_COMPLETION=y
+# CONFIG_FEATURE_USERNAME_COMPLETION is not set
+CONFIG_FEATURE_EDITING_FANCY_PROMPT=y
+# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
+CONFIG_FEATURE_NON_POSIX_CP=y
+# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
+CONFIG_FEATURE_COPYBUF_KB=4
+CONFIG_FEATURE_SKIP_ROOTFS=y
+# CONFIG_MONOTONIC_SYSCALL is not set
+CONFIG_IOCTL_HEX2STR_ERROR=y
+CONFIG_FEATURE_HWIB=y
+
+#
+# Applets
+#
+
+#
+# Archival Utilities
+#
+CONFIG_FEATURE_SEAMLESS_XZ=y
+CONFIG_FEATURE_SEAMLESS_LZMA=y
+CONFIG_FEATURE_SEAMLESS_BZ2=y
+CONFIG_FEATURE_SEAMLESS_GZ=y
+# CONFIG_FEATURE_SEAMLESS_Z is not set
+# CONFIG_AR is not set
+# CONFIG_FEATURE_AR_LONG_FILENAMES is not set
+# CONFIG_FEATURE_AR_CREATE is not set
+CONFIG_BUNZIP2=y
+CONFIG_BZIP2=y
+CONFIG_CPIO=y
+CONFIG_FEATURE_CPIO_O=y
+CONFIG_FEATURE_CPIO_P=y
+# CONFIG_DPKG is not set
+# CONFIG_DPKG_DEB is not set
+# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
+CONFIG_GUNZIP=y
+CONFIG_GZIP=y
+CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
+CONFIG_LZOP=y
+# CONFIG_LZOP_COMPR_HIGH is not set
+CONFIG_RPM2CPIO=y
+CONFIG_RPM=y
+CONFIG_TAR=y
+CONFIG_FEATURE_TAR_CREATE=y
+CONFIG_FEATURE_TAR_AUTODETECT=y
+CONFIG_FEATURE_TAR_FROM=y
+CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y
+CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y
+CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
+CONFIG_FEATURE_TAR_LONG_OPTIONS=y
+CONFIG_FEATURE_TAR_TO_COMMAND=y
+CONFIG_FEATURE_TAR_UNAME_GNAME=y
+CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
+# CONFIG_FEATURE_TAR_SELINUX is not set
+# CONFIG_UNCOMPRESS is not set
+CONFIG_UNLZMA=y
+CONFIG_FEATURE_LZMA_FAST=y
+CONFIG_LZMA=y
+CONFIG_UNXZ=y
+CONFIG_XZ=y
+CONFIG_UNZIP=y
+
+#
+# Coreutils
+#
+CONFIG_BASENAME=y
+CONFIG_CAT=y
+CONFIG_DATE=y
+CONFIG_FEATURE_DATE_ISOFMT=y
+# CONFIG_FEATURE_DATE_NANO is not set
+CONFIG_FEATURE_DATE_COMPAT=y
+CONFIG_ID=y
+CONFIG_GROUPS=y
+CONFIG_TEST=y
+CONFIG_FEATURE_TEST_64=y
+CONFIG_TOUCH=y
+CONFIG_TR=y
+CONFIG_FEATURE_TR_CLASSES=y
+CONFIG_FEATURE_TR_EQUIV=y
+CONFIG_BASE64=y
+CONFIG_CAL=y
+CONFIG_CATV=y
+CONFIG_CHGRP=y
+CONFIG_CHMOD=y
+CONFIG_CHOWN=y
+CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y
+CONFIG_CHROOT=y
+CONFIG_CKSUM=y
+CONFIG_COMM=y
+CONFIG_CP=y
+CONFIG_FEATURE_CP_LONG_OPTIONS=y
+CONFIG_CUT=y
+CONFIG_DD=y
+CONFIG_FEATURE_DD_SIGNAL_HANDLING=y
+CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y
+CONFIG_FEATURE_DD_IBS_OBS=y
+CONFIG_DF=y
+CONFIG_FEATURE_DF_FANCY=y
+CONFIG_DIRNAME=y
+CONFIG_DOS2UNIX=y
+CONFIG_UNIX2DOS=y
+CONFIG_DU=y
+# CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K is not set
+CONFIG_ECHO=y
+CONFIG_FEATURE_FANCY_ECHO=y
+CONFIG_ENV=y
+CONFIG_FEATURE_ENV_LONG_OPTIONS=y
+CONFIG_EXPAND=y
+CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y
+CONFIG_EXPR=y
+CONFIG_EXPR_MATH_SUPPORT_64=y
+CONFIG_FALSE=y
+CONFIG_FOLD=y
+CONFIG_FSYNC=y
+CONFIG_HEAD=y
+CONFIG_FEATURE_FANCY_HEAD=y
+CONFIG_HOSTID=y
+CONFIG_INSTALL=y
+CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y
+CONFIG_LN=y
+CONFIG_LOGNAME=y
+CONFIG_LS=y
+CONFIG_FEATURE_LS_FILETYPES=y
+CONFIG_FEATURE_LS_FOLLOWLINKS=y
+CONFIG_FEATURE_LS_RECURSIVE=y
+CONFIG_FEATURE_LS_SORTFILES=y
+CONFIG_FEATURE_LS_TIMESTAMPS=y
+CONFIG_FEATURE_LS_USERNAME=y
+CONFIG_FEATURE_LS_COLOR=y
+CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y
+CONFIG_MD5SUM=y
+CONFIG_MKDIR=y
+CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y
+CONFIG_MKFIFO=y
+CONFIG_MKNOD=y
+CONFIG_MV=y
+CONFIG_FEATURE_MV_LONG_OPTIONS=y
+CONFIG_NICE=y
+CONFIG_NOHUP=y
+CONFIG_OD=y
+CONFIG_PRINTENV=y
+CONFIG_PRINTF=y
+CONFIG_PWD=y
+CONFIG_READLINK=y
+CONFIG_FEATURE_READLINK_FOLLOW=y
+CONFIG_REALPATH=y
+CONFIG_RM=y
+CONFIG_RMDIR=y
+CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y
+CONFIG_SEQ=y
+CONFIG_SHA1SUM=y
+CONFIG_SHA256SUM=y
+CONFIG_SHA512SUM=y
+CONFIG_SLEEP=y
+CONFIG_FEATURE_FANCY_SLEEP=y
+CONFIG_FEATURE_FLOAT_SLEEP=y
+CONFIG_SORT=y
+CONFIG_FEATURE_SORT_BIG=y
+CONFIG_SPLIT=y
+CONFIG_FEATURE_SPLIT_FANCY=y
+# CONFIG_STAT is not set
+# CONFIG_FEATURE_STAT_FORMAT is not set
+CONFIG_STTY=y
+CONFIG_SUM=y
+CONFIG_SYNC=y
+CONFIG_TAC=y
+CONFIG_TAIL=y
+CONFIG_FEATURE_FANCY_TAIL=y
+CONFIG_TEE=y
+CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
+CONFIG_TRUE=y
+CONFIG_TTY=y
+CONFIG_UNAME=y
+CONFIG_UNEXPAND=y
+CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y
+CONFIG_UNIQ=y
+CONFIG_USLEEP=y
+CONFIG_UUDECODE=y
+CONFIG_UUENCODE=y
+CONFIG_WC=y
+CONFIG_FEATURE_WC_LARGE=y
+# CONFIG_WHO is not set
+CONFIG_WHOAMI=y
+CONFIG_YES=y
+
+#
+# Common options for cp and mv
+#
+CONFIG_FEATURE_PRESERVE_HARDLINKS=y
+
+#
+# Common options for ls, more and telnet
+#
+CONFIG_FEATURE_AUTOWIDTH=y
+
+#
+# Common options for df, du, ls
+#
+CONFIG_FEATURE_HUMAN_READABLE=y
+
+#
+# Common options for md5sum, sha1sum, sha256sum, sha512sum
+#
+CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
+
+#
+# Console Utilities
+#
+# CONFIG_CHVT is not set
+# CONFIG_FGCONSOLE is not set
+CONFIG_CLEAR=y
+# CONFIG_DEALLOCVT is not set
+# CONFIG_DUMPKMAP is not set
+# CONFIG_KBD_MODE is not set
+# CONFIG_LOADFONT is not set
+# CONFIG_LOADKMAP is not set
+# CONFIG_OPENVT is not set
+CONFIG_RESET=y
+CONFIG_RESIZE=y
+CONFIG_FEATURE_RESIZE_PRINT=y
+# CONFIG_SETCONSOLE is not set
+# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
+# CONFIG_SETFONT is not set
+# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
+CONFIG_DEFAULT_SETFONT_DIR=""
+# CONFIG_SETKEYCODES is not set
+# CONFIG_SETLOGCONS is not set
+# CONFIG_SHOWKEY is not set
+# CONFIG_FEATURE_LOADFONT_PSF2 is not set
+# CONFIG_FEATURE_LOADFONT_RAW is not set
+
+#
+# Debian Utilities
+#
+CONFIG_MKTEMP=y
+CONFIG_PIPE_PROGRESS=y
+CONFIG_RUN_PARTS=y
+CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y
+CONFIG_FEATURE_RUN_PARTS_FANCY=y
+CONFIG_START_STOP_DAEMON=y
+CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y
+CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS=y
+CONFIG_WHICH=y
+
+#
+# Editors
+#
+CONFIG_PATCH=y
+CONFIG_VI=y
+CONFIG_FEATURE_VI_MAX_LEN=4096
+# CONFIG_FEATURE_VI_8BIT is not set
+CONFIG_FEATURE_VI_COLON=y
+CONFIG_FEATURE_VI_YANKMARK=y
+CONFIG_FEATURE_VI_SEARCH=y
+# CONFIG_FEATURE_VI_REGEX_SEARCH is not set
+CONFIG_FEATURE_VI_USE_SIGNALS=y
+CONFIG_FEATURE_VI_DOT_CMD=y
+CONFIG_FEATURE_VI_READONLY=y
+CONFIG_FEATURE_VI_SETOPTS=y
+CONFIG_FEATURE_VI_SET=y
+CONFIG_FEATURE_VI_WIN_RESIZE=y
+CONFIG_FEATURE_VI_ASK_TERMINAL=y
+CONFIG_AWK=y
+CONFIG_FEATURE_AWK_LIBM=y
+CONFIG_CMP=y
+CONFIG_DIFF=y
+CONFIG_FEATURE_DIFF_LONG_OPTIONS=y
+CONFIG_FEATURE_DIFF_DIR=y
+CONFIG_ED=y
+CONFIG_SED=y
+CONFIG_FEATURE_ALLOW_EXEC=y
+
+#
+# Finding Utilities
+#
+CONFIG_FIND=y
+CONFIG_FEATURE_FIND_PRINT0=y
+CONFIG_FEATURE_FIND_MTIME=y
+CONFIG_FEATURE_FIND_MMIN=y
+CONFIG_FEATURE_FIND_PERM=y
+CONFIG_FEATURE_FIND_TYPE=y
+CONFIG_FEATURE_FIND_XDEV=y
+CONFIG_FEATURE_FIND_MAXDEPTH=y
+CONFIG_FEATURE_FIND_NEWER=y
+CONFIG_FEATURE_FIND_INUM=y
+CONFIG_FEATURE_FIND_EXEC=y
+CONFIG_FEATURE_FIND_USER=y
+CONFIG_FEATURE_FIND_GROUP=y
+CONFIG_FEATURE_FIND_NOT=y
+CONFIG_FEATURE_FIND_DEPTH=y
+CONFIG_FEATURE_FIND_PAREN=y
+CONFIG_FEATURE_FIND_SIZE=y
+CONFIG_FEATURE_FIND_PRUNE=y
+CONFIG_FEATURE_FIND_DELETE=y
+CONFIG_FEATURE_FIND_PATH=y
+CONFIG_FEATURE_FIND_REGEX=y
+# CONFIG_FEATURE_FIND_CONTEXT is not set
+CONFIG_FEATURE_FIND_LINKS=y
+CONFIG_GREP=y
+CONFIG_FEATURE_GREP_EGREP_ALIAS=y
+CONFIG_FEATURE_GREP_FGREP_ALIAS=y
+CONFIG_FEATURE_GREP_CONTEXT=y
+CONFIG_XARGS=y
+CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
+CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
+CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
+CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
+
+#
+# Init Utilities
+#
+# CONFIG_BOOTCHARTD is not set
+# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set
+# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set
+# CONFIG_HALT is not set
+# CONFIG_FEATURE_CALL_TELINIT is not set
+CONFIG_TELINIT_PATH=""
+# CONFIG_INIT is not set
+# CONFIG_FEATURE_USE_INITTAB is not set
+# CONFIG_FEATURE_KILL_REMOVED is not set
+CONFIG_FEATURE_KILL_DELAY=0
+# CONFIG_FEATURE_INIT_SCTTY is not set
+# CONFIG_FEATURE_INIT_SYSLOG is not set
+# CONFIG_FEATURE_EXTRA_QUIET is not set
+# CONFIG_FEATURE_INIT_COREDUMPS is not set
+# CONFIG_FEATURE_INITRD is not set
+CONFIG_INIT_TERMINAL_TYPE=""
+CONFIG_MESG=y
+CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y
+
+#
+# Login/Password Management Utilities
+#
+CONFIG_ADD_SHELL=y
+CONFIG_REMOVE_SHELL=y
+CONFIG_FEATURE_SHADOWPASSWDS=y
+CONFIG_USE_BB_PWD_GRP=y
+CONFIG_USE_BB_SHADOW=y
+CONFIG_USE_BB_CRYPT=y
+CONFIG_USE_BB_CRYPT_SHA=y
+CONFIG_ADDUSER=y
+CONFIG_FEATURE_ADDUSER_LONG_OPTIONS=y
+# CONFIG_FEATURE_CHECK_NAMES is not set
+CONFIG_FIRST_SYSTEM_ID=100
+CONFIG_LAST_SYSTEM_ID=999
+CONFIG_ADDGROUP=y
+CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y
+CONFIG_FEATURE_ADDUSER_TO_GROUP=y
+CONFIG_DELUSER=y
+CONFIG_DELGROUP=y
+CONFIG_FEATURE_DEL_USER_FROM_GROUP=y
+# CONFIG_GETTY is not set
+CONFIG_LOGIN=y
+# CONFIG_PAM is not set
+CONFIG_LOGIN_SCRIPTS=y
+CONFIG_FEATURE_NOLOGIN=y
+CONFIG_FEATURE_SECURETTY=y
+CONFIG_PASSWD=y
+CONFIG_FEATURE_PASSWD_WEAK_CHECK=y
+CONFIG_CRYPTPW=y
+CONFIG_CHPASSWD=y
+CONFIG_SU=y
+CONFIG_FEATURE_SU_SYSLOG=y
+CONFIG_FEATURE_SU_CHECKS_SHELLS=y
+CONFIG_SULOGIN=y
+CONFIG_VLOCK=y
+
+#
+# Linux Ext2 FS Progs
+#
+CONFIG_CHATTR=y
+# CONFIG_FSCK is not set
+# CONFIG_LSATTR is not set
+# CONFIG_TUNE2FS is not set
+
+#
+# Linux Module Utilities
+#
+# CONFIG_MODINFO is not set
+# CONFIG_MODPROBE_SMALL is not set
+# CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set
+# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set
+# CONFIG_INSMOD is not set
+# CONFIG_RMMOD is not set
+# CONFIG_LSMOD is not set
+# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set
+# CONFIG_MODPROBE is not set
+# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set
+# CONFIG_DEPMOD is not set
+
+#
+# Options common to multiple modutils
+#
+# CONFIG_FEATURE_2_4_MODULES is not set
+# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set
+# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
+# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
+# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
+# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
+# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set
+# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
+# CONFIG_FEATURE_MODUTILS_ALIAS is not set
+# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set
+CONFIG_DEFAULT_MODULES_DIR=""
+CONFIG_DEFAULT_DEPMOD_FILE=""
+
+#
+# Linux System Utilities
+#
+# CONFIG_BLOCKDEV is not set
+CONFIG_REV=y
+# CONFIG_ACPID is not set
+# CONFIG_FEATURE_ACPID_COMPAT is not set
+# CONFIG_BLKID is not set
+# CONFIG_FEATURE_BLKID_TYPE is not set
+# CONFIG_DMESG is not set
+# CONFIG_FEATURE_DMESG_PRETTY is not set
+# CONFIG_FBSET is not set
+# CONFIG_FEATURE_FBSET_FANCY is not set
+# CONFIG_FEATURE_FBSET_READMODE is not set
+# CONFIG_FDFLUSH is not set
+# CONFIG_FDFORMAT is not set
+# CONFIG_FDISK is not set
+# CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set
+# CONFIG_FEATURE_FDISK_WRITABLE is not set
+# CONFIG_FEATURE_AIX_LABEL is not set
+# CONFIG_FEATURE_SGI_LABEL is not set
+# CONFIG_FEATURE_SUN_LABEL is not set
+# CONFIG_FEATURE_OSF_LABEL is not set
+# CONFIG_FEATURE_GPT_LABEL is not set
+# CONFIG_FEATURE_FDISK_ADVANCED is not set
+# CONFIG_FINDFS is not set
+CONFIG_FLOCK=y
+# CONFIG_FREERAMDISK is not set
+CONFIG_FSCK_MINIX=y
+# CONFIG_MKFS_EXT2 is not set
+# CONFIG_MKFS_MINIX is not set
+CONFIG_FEATURE_MINIX2=y
+# CONFIG_MKFS_REISER is not set
+# CONFIG_MKFS_VFAT is not set
+CONFIG_GETOPT=y
+CONFIG_FEATURE_GETOPT_LONG=y
+CONFIG_HEXDUMP=y
+CONFIG_FEATURE_HEXDUMP_REVERSE=y
+CONFIG_HD=y
+# CONFIG_HWCLOCK is not set
+# CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set
+# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set
+CONFIG_IPCRM=y
+# CONFIG_IPCS is not set
+# CONFIG_LOSETUP is not set
+# CONFIG_LSPCI is not set
+# CONFIG_LSUSB is not set
+# CONFIG_MDEV is not set
+# CONFIG_FEATURE_MDEV_CONF is not set
+# CONFIG_FEATURE_MDEV_RENAME is not set
+# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set
+# CONFIG_FEATURE_MDEV_EXEC is not set
+# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set
+CONFIG_MKSWAP=y
+CONFIG_FEATURE_MKSWAP_UUID=y
+CONFIG_MORE=y
+# CONFIG_MOUNT is not set
+# CONFIG_FEATURE_MOUNT_FAKE is not set
+# CONFIG_FEATURE_MOUNT_VERBOSE is not set
+# CONFIG_FEATURE_MOUNT_HELPERS is not set
+# CONFIG_FEATURE_MOUNT_LABEL is not set
+# CONFIG_FEATURE_MOUNT_NFS is not set
+# CONFIG_FEATURE_MOUNT_CIFS is not set
+# CONFIG_FEATURE_MOUNT_FLAGS is not set
+# CONFIG_FEATURE_MOUNT_FSTAB is not set
+# CONFIG_PIVOT_ROOT is not set
+# CONFIG_RDATE is not set
+CONFIG_RDEV=y
+CONFIG_READPROFILE=y
+# CONFIG_RTCWAKE is not set
+CONFIG_SCRIPT=y
+CONFIG_SCRIPTREPLAY=y
+# CONFIG_SETARCH is not set
+# CONFIG_SWAPONOFF is not set
+# CONFIG_FEATURE_SWAPON_PRI is not set
+# CONFIG_SWITCH_ROOT is not set
+# CONFIG_UMOUNT is not set
+# CONFIG_FEATURE_UMOUNT_ALL is not set
+# CONFIG_FEATURE_MOUNT_LOOP is not set
+# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set
+# CONFIG_FEATURE_MTAB_SUPPORT is not set
+# CONFIG_VOLUMEID is not set
+# CONFIG_FEATURE_VOLUMEID_EXT is not set
+# CONFIG_FEATURE_VOLUMEID_BTRFS is not set
+# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
+# CONFIG_FEATURE_VOLUMEID_FAT is not set
+# CONFIG_FEATURE_VOLUMEID_HFS is not set
+# CONFIG_FEATURE_VOLUMEID_JFS is not set
+# CONFIG_FEATURE_VOLUMEID_XFS is not set
+# CONFIG_FEATURE_VOLUMEID_NTFS is not set
+# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set
+# CONFIG_FEATURE_VOLUMEID_UDF is not set
+# CONFIG_FEATURE_VOLUMEID_LUKS is not set
+# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set
+# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set
+# CONFIG_FEATURE_VOLUMEID_ROMFS is not set
+# CONFIG_FEATURE_VOLUMEID_SYSV is not set
+# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set
+# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set
+
+#
+# Miscellaneous Utilities
+#
+# CONFIG_CONSPY is not set
+# CONFIG_NANDWRITE is not set
+# CONFIG_NANDDUMP is not set
+# CONFIG_SETSERIAL is not set
+# CONFIG_UBIATTACH is not set
+# CONFIG_UBIDETACH is not set
+# CONFIG_UBIMKVOL is not set
+# CONFIG_UBIRMVOL is not set
+# CONFIG_UBIRSVOL is not set
+# CONFIG_UBIUPDATEVOL is not set
+# CONFIG_ADJTIMEX is not set
+# CONFIG_BBCONFIG is not set
+# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
+# CONFIG_BEEP is not set
+CONFIG_FEATURE_BEEP_FREQ=0
+CONFIG_FEATURE_BEEP_LENGTH_MS=0
+CONFIG_CHAT=y
+CONFIG_FEATURE_CHAT_NOFAIL=y
+# CONFIG_FEATURE_CHAT_TTY_HIFI is not set
+CONFIG_FEATURE_CHAT_IMPLICIT_CR=y
+CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y
+CONFIG_FEATURE_CHAT_SEND_ESCAPES=y
+CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y
+CONFIG_FEATURE_CHAT_CLR_ABORT=y
+CONFIG_CHRT=y
+CONFIG_CROND=y
+CONFIG_FEATURE_CROND_D=y
+CONFIG_FEATURE_CROND_CALL_SENDMAIL=y
+CONFIG_FEATURE_CROND_DIR="/var/spool/cron"
+CONFIG_CRONTAB=y
+CONFIG_DC=y
+CONFIG_FEATURE_DC_LIBM=y
+# CONFIG_DEVFSD is not set
+# CONFIG_DEVFSD_MODLOAD is not set
+# CONFIG_DEVFSD_FG_NP is not set
+# CONFIG_DEVFSD_VERBOSE is not set
+# CONFIG_FEATURE_DEVFS is not set
+CONFIG_DEVMEM=y
+# CONFIG_EJECT is not set
+# CONFIG_FEATURE_EJECT_SCSI is not set
+# CONFIG_FBSPLASH is not set
+# CONFIG_FLASHCP is not set
+# CONFIG_FLASH_LOCK is not set
+# CONFIG_FLASH_UNLOCK is not set
+# CONFIG_FLASH_ERASEALL is not set
+# CONFIG_IONICE is not set
+# CONFIG_INOTIFYD is not set
+# CONFIG_LAST is not set
+# CONFIG_FEATURE_LAST_SMALL is not set
+# CONFIG_FEATURE_LAST_FANCY is not set
+CONFIG_LESS=y
+CONFIG_FEATURE_LESS_MAXLINES=9999999
+CONFIG_FEATURE_LESS_BRACKETS=y
+CONFIG_FEATURE_LESS_FLAGS=y
+CONFIG_FEATURE_LESS_MARKS=y
+CONFIG_FEATURE_LESS_REGEXP=y
+CONFIG_FEATURE_LESS_WINCH=y
+CONFIG_FEATURE_LESS_DASHCMD=y
+CONFIG_FEATURE_LESS_LINENUMS=y
+# CONFIG_HDPARM is not set
+# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set
+# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set
+# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set
+# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set
+# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set
+# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set
+# CONFIG_MAKEDEVS is not set
+# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
+# CONFIG_FEATURE_MAKEDEVS_TABLE is not set
+CONFIG_MAN=y
+# CONFIG_MICROCOM is not set
+# CONFIG_MOUNTPOINT is not set
+CONFIG_MT=y
+# CONFIG_RAIDAUTORUN is not set
+# CONFIG_READAHEAD is not set
+# CONFIG_RFKILL is not set
+# CONFIG_RUNLEVEL is not set
+# CONFIG_RX is not set
+CONFIG_SETSID=y
+CONFIG_STRINGS=y
+# CONFIG_TASKSET is not set
+# CONFIG_FEATURE_TASKSET_FANCY is not set
+CONFIG_TIME=y
+CONFIG_TIMEOUT=y
+CONFIG_TTYSIZE=y
+CONFIG_VOLNAME=y
+# CONFIG_WALL is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Networking Utilities
+#
+# CONFIG_NAMEIF is not set
+# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
+# CONFIG_NBDCLIENT is not set
+CONFIG_NC=y
+CONFIG_NC_SERVER=y
+CONFIG_NC_EXTRA=y
+# CONFIG_NC_110_COMPAT is not set
+# CONFIG_PING is not set
+# CONFIG_PING6 is not set
+# CONFIG_FEATURE_FANCY_PING is not set
+CONFIG_WHOIS=y
+CONFIG_FEATURE_IPV6=y
+# CONFIG_FEATURE_UNIX_LOCAL is not set
+CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
+# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
+# CONFIG_ARP is not set
+# CONFIG_ARPING is not set
+# CONFIG_BRCTL is not set
+# CONFIG_FEATURE_BRCTL_FANCY is not set
+# CONFIG_FEATURE_BRCTL_SHOW is not set
+CONFIG_DNSD=y
+# CONFIG_ETHER_WAKE is not set
+CONFIG_FAKEIDENTD=y
+CONFIG_FTPD=y
+CONFIG_FEATURE_FTP_WRITE=y
+CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y
+CONFIG_FTPGET=y
+CONFIG_FTPPUT=y
+CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
+CONFIG_HOSTNAME=y
+CONFIG_HTTPD=y
+CONFIG_FEATURE_HTTPD_RANGES=y
+# CONFIG_FEATURE_HTTPD_USE_SENDFILE is not set
+CONFIG_FEATURE_HTTPD_SETUID=y
+CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
+CONFIG_FEATURE_HTTPD_AUTH_MD5=y
+CONFIG_FEATURE_HTTPD_CGI=y
+CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y
+CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y
+CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
+CONFIG_FEATURE_HTTPD_ERROR_PAGES=y
+CONFIG_FEATURE_HTTPD_PROXY=y
+CONFIG_FEATURE_HTTPD_GZIP=y
+# CONFIG_IFCONFIG is not set
+# CONFIG_FEATURE_IFCONFIG_STATUS is not set
+# CONFIG_FEATURE_IFCONFIG_SLIP is not set
+# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
+# CONFIG_FEATURE_IFCONFIG_HW is not set
+# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
+# CONFIG_IFENSLAVE is not set
+# CONFIG_IFPLUGD is not set
+# CONFIG_IFUPDOWN is not set
+CONFIG_IFUPDOWN_IFSTATE_PATH=""
+# CONFIG_FEATURE_IFUPDOWN_IP is not set
+# CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN is not set
+# CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set
+# CONFIG_FEATURE_IFUPDOWN_IPV4 is not set
+# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set
+# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set
+# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set
+CONFIG_INETD=y
+CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO=y
+CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD=y
+CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME=y
+CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME=y
+CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN=y
+# CONFIG_FEATURE_INETD_RPC is not set
+# CONFIG_IP is not set
+# CONFIG_FEATURE_IP_ADDRESS is not set
+# CONFIG_FEATURE_IP_LINK is not set
+# CONFIG_FEATURE_IP_ROUTE is not set
+# CONFIG_FEATURE_IP_TUNNEL is not set
+# CONFIG_FEATURE_IP_RULE is not set
+# CONFIG_FEATURE_IP_SHORT_FORMS is not set
+# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
+# CONFIG_IPADDR is not set
+# CONFIG_IPLINK is not set
+# CONFIG_IPROUTE is not set
+# CONFIG_IPTUNNEL is not set
+# CONFIG_IPRULE is not set
+CONFIG_IPCALC=y
+CONFIG_FEATURE_IPCALC_FANCY=y
+CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y
+# CONFIG_NETSTAT is not set
+# CONFIG_FEATURE_NETSTAT_WIDE is not set
+# CONFIG_FEATURE_NETSTAT_PRG is not set
+# CONFIG_NSLOOKUP is not set
+# CONFIG_NTPD is not set
+# CONFIG_FEATURE_NTPD_SERVER is not set
+CONFIG_PSCAN=y
+# CONFIG_ROUTE is not set
+# CONFIG_SLATTACH is not set
+CONFIG_TCPSVD=y
+CONFIG_TELNET=y
+CONFIG_FEATURE_TELNET_TTYPE=y
+CONFIG_FEATURE_TELNET_AUTOLOGIN=y
+CONFIG_TELNETD=y
+CONFIG_FEATURE_TELNETD_STANDALONE=y
+CONFIG_FEATURE_TELNETD_INETD_WAIT=y
+CONFIG_TFTP=y
+CONFIG_TFTPD=y
+
+#
+# Common options for tftp/tftpd
+#
+CONFIG_FEATURE_TFTP_GET=y
+CONFIG_FEATURE_TFTP_PUT=y
+CONFIG_FEATURE_TFTP_BLOCKSIZE=y
+CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
+# CONFIG_TFTP_DEBUG is not set
+# CONFIG_TRACEROUTE is not set
+# CONFIG_TRACEROUTE6 is not set
+# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
+# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
+# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
+# CONFIG_TUNCTL is not set
+# CONFIG_FEATURE_TUNCTL_UG is not set
+# CONFIG_UDHCPD is not set
+# CONFIG_DHCPRELAY is not set
+# CONFIG_DUMPLEASES is not set
+# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
+# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set
+CONFIG_DHCPD_LEASES_FILE=""
+# CONFIG_UDHCPC is not set
+# CONFIG_FEATURE_UDHCPC_ARPING is not set
+# CONFIG_FEATURE_UDHCP_PORT is not set
+CONFIG_UDHCP_DEBUG=0
+# CONFIG_FEATURE_UDHCP_RFC3397 is not set
+# CONFIG_FEATURE_UDHCP_8021Q is not set
+CONFIG_UDHCPC_DEFAULT_SCRIPT=""
+CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0
+CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS=""
+CONFIG_UDPSVD=y
+# CONFIG_VCONFIG is not set
+CONFIG_WGET=y
+CONFIG_FEATURE_WGET_STATUSBAR=y
+CONFIG_FEATURE_WGET_AUTHENTICATION=y
+CONFIG_FEATURE_WGET_LONG_OPTIONS=y
+CONFIG_FEATURE_WGET_TIMEOUT=y
+# CONFIG_ZCIP is not set
+
+#
+# Print Utilities
+#
+CONFIG_LPD=y
+CONFIG_LPR=y
+CONFIG_LPQ=y
+
+#
+# Mail Utilities
+#
+CONFIG_MAKEMIME=y
+CONFIG_FEATURE_MIME_CHARSET="us-ascii"
+CONFIG_POPMAILDIR=y
+CONFIG_FEATURE_POPMAILDIR_DELIVERY=y
+CONFIG_REFORMIME=y
+CONFIG_FEATURE_REFORMIME_COMPAT=y
+CONFIG_SENDMAIL=y
+
+#
+# Process Utilities
+#
+CONFIG_IOSTAT=y
+CONFIG_MPSTAT=y
+CONFIG_NMETER=y
+# CONFIG_PMAP is not set
+# CONFIG_POWERTOP is not set
+CONFIG_PSTREE=y
+CONFIG_PWDX=y
+CONFIG_SMEMCAP=y
+# CONFIG_FREE is not set
+CONFIG_FUSER=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+CONFIG_KILLALL5=y
+CONFIG_PGREP=y
+CONFIG_PIDOF=y
+CONFIG_FEATURE_PIDOF_SINGLE=y
+CONFIG_FEATURE_PIDOF_OMIT=y
+CONFIG_PKILL=y
+CONFIG_PS=y
+CONFIG_FEATURE_PS_WIDE=y
+# CONFIG_FEATURE_PS_TIME is not set
+CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y
+# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
+CONFIG_RENICE=y
+CONFIG_BB_SYSCTL=y
+CONFIG_TOP=y
+CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y
+CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y
+CONFIG_FEATURE_TOP_SMP_CPU=y
+CONFIG_FEATURE_TOP_DECIMALS=y
+CONFIG_FEATURE_TOP_SMP_PROCESS=y
+CONFIG_FEATURE_TOPMEM=y
+# CONFIG_FEATURE_SHOW_THREADS is not set
+# CONFIG_UPTIME is not set
+CONFIG_WATCH=y
+
+#
+# Runit Utilities
+#
+CONFIG_RUNSV=y
+CONFIG_RUNSVDIR=y
+# CONFIG_FEATURE_RUNSVDIR_LOG is not set
+CONFIG_SV=y
+CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service"
+CONFIG_SVLOGD=y
+CONFIG_CHPST=y
+CONFIG_SETUIDGID=y
+CONFIG_ENVUIDGID=y
+CONFIG_ENVDIR=y
+CONFIG_SOFTLIMIT=y
+# CONFIG_CHCON is not set
+# CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set
+# CONFIG_GETENFORCE is not set
+# CONFIG_GETSEBOOL is not set
+# CONFIG_LOAD_POLICY is not set
+# CONFIG_MATCHPATHCON is not set
+# CONFIG_RESTORECON is not set
+# CONFIG_RUNCON is not set
+# CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set
+# CONFIG_SELINUXENABLED is not set
+# CONFIG_SETENFORCE is not set
+# CONFIG_SETFILES is not set
+# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set
+# CONFIG_SETSEBOOL is not set
+# CONFIG_SESTATUS is not set
+
+#
+# Shells
+#
+CONFIG_ASH=y
+CONFIG_ASH_BASH_COMPAT=y
+# CONFIG_ASH_IDLE_TIMEOUT is not set
+CONFIG_ASH_JOB_CONTROL=y
+CONFIG_ASH_ALIAS=y
+CONFIG_ASH_GETOPTS=y
+CONFIG_ASH_BUILTIN_ECHO=y
+CONFIG_ASH_BUILTIN_PRINTF=y
+CONFIG_ASH_BUILTIN_TEST=y
+CONFIG_ASH_CMDCMD=y
+# CONFIG_ASH_MAIL is not set
+CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
+CONFIG_ASH_RANDOM_SUPPORT=y
+CONFIG_ASH_EXPAND_PRMT=y
+# CONFIG_CTTYHACK is not set
+CONFIG_HUSH=y
+CONFIG_HUSH_BASH_COMPAT=y
+CONFIG_HUSH_BRACE_EXPANSION=y
+CONFIG_HUSH_HELP=y
+CONFIG_HUSH_INTERACTIVE=y
+CONFIG_HUSH_SAVEHISTORY=y
+CONFIG_HUSH_JOB=y
+CONFIG_HUSH_TICK=y
+CONFIG_HUSH_IF=y
+CONFIG_HUSH_LOOPS=y
+CONFIG_HUSH_CASE=y
+CONFIG_HUSH_FUNCTIONS=y
+CONFIG_HUSH_LOCAL=y
+CONFIG_HUSH_RANDOM_SUPPORT=y
+CONFIG_HUSH_EXPORT_N=y
+CONFIG_HUSH_MODE_X=y
+# CONFIG_MSH is not set
+CONFIG_FEATURE_SH_IS_ASH=y
+# CONFIG_FEATURE_SH_IS_HUSH is not set
+# CONFIG_FEATURE_SH_IS_NONE is not set
+# CONFIG_FEATURE_BASH_IS_ASH is not set
+# CONFIG_FEATURE_BASH_IS_HUSH is not set
+CONFIG_FEATURE_BASH_IS_NONE=y
+CONFIG_SH_MATH_SUPPORT=y
+CONFIG_SH_MATH_SUPPORT_64=y
+CONFIG_FEATURE_SH_EXTRA_QUIET=y
+# CONFIG_FEATURE_SH_STANDALONE is not set
+# CONFIG_FEATURE_SH_NOFORK is not set
+CONFIG_FEATURE_SH_HISTFILESIZE=y
+
+#
+# System Logging Utilities
+#
+CONFIG_SYSLOGD=y
+CONFIG_FEATURE_ROTATE_LOGFILE=y
+CONFIG_FEATURE_REMOTE_LOG=y
+CONFIG_FEATURE_SYSLOGD_DUP=y
+CONFIG_FEATURE_SYSLOGD_CFG=y
+CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256
+CONFIG_FEATURE_IPC_SYSLOG=y
+CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16
+CONFIG_LOGREAD=y
+CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y
+# CONFIG_KLOGD is not set
+# CONFIG_FEATURE_KLOGD_KLOGCTL is not set
+CONFIG_LOGGER=y
similarity index 69%
rename from debian/config/pkg/deb
rename to configs/freebsd_defconfig
index fbd418b..ec3ed03 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Busybox version: 1.17.1
-# Tue Nov  9 10:34:49 2010
+# Busybox version: 1.18.1
+# Tue Dec 21 19:47:40 2010
 #
 CONFIG_HAVE_DOT_CONFIG=y
 
@@ -12,10 +12,11 @@ CONFIG_HAVE_DOT_CONFIG=y
 #
 # General Configuration
 #
-CONFIG_DESKTOP=y
-CONFIG_EXTRA_COMPAT=y
+# CONFIG_DESKTOP is not set
+# CONFIG_EXTRA_COMPAT is not set
 CONFIG_INCLUDE_SUSv2=y
-# CONFIG_USE_PORTABLE_CODE is not set
+CONFIG_USE_PORTABLE_CODE=y
+# CONFIG_PLATFORM_LINUX is not set
 CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
 # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
 # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
@@ -23,29 +24,30 @@ CONFIG_SHOW_USAGE=y
 CONFIG_FEATURE_VERBOSE_USAGE=y
 CONFIG_FEATURE_COMPRESS_USAGE=y
 CONFIG_FEATURE_INSTALLER=y
-# CONFIG_LOCALE_SUPPORT is not set
+# CONFIG_INSTALL_NO_USR is not set
+CONFIG_LOCALE_SUPPORT=y
 CONFIG_UNICODE_SUPPORT=y
 # CONFIG_UNICODE_USING_LOCALE is not set
-CONFIG_FEATURE_CHECK_UNICODE_IN_ENV=y
+# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
 CONFIG_SUBST_WCHAR=63
 CONFIG_LAST_SUPPORTED_WCHAR=767
-CONFIG_UNICODE_COMBINING_WCHARS=y
-CONFIG_UNICODE_WIDE_WCHARS=y
+# CONFIG_UNICODE_COMBINING_WCHARS is not set
+# CONFIG_UNICODE_WIDE_WCHARS is not set
 # CONFIG_UNICODE_BIDI_SUPPORT is not set
 # CONFIG_UNICODE_NEUTRAL_TABLE is not set
 # CONFIG_UNICODE_PRESERVE_BROKEN is not set
 CONFIG_LONG_OPTS=y
 CONFIG_FEATURE_DEVPTS=y
 # CONFIG_FEATURE_CLEAN_UP is not set
-CONFIG_FEATURE_UTMP=y
-CONFIG_FEATURE_WTMP=y
+# CONFIG_FEATURE_WTMP is not set
+# CONFIG_FEATURE_UTMP is not set
 CONFIG_FEATURE_PIDFILE=y
 CONFIG_FEATURE_SUID=y
 CONFIG_FEATURE_SUID_CONFIG=y
 CONFIG_FEATURE_SUID_CONFIG_QUIET=y
 # CONFIG_SELINUX is not set
-CONFIG_FEATURE_PREFER_APPLETS=y
-CONFIG_BUSYBOX_EXEC_PATH="/bin/busybox"
+# CONFIG_FEATURE_PREFER_APPLETS is not set
+CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
 CONFIG_FEATURE_SYSLOG=y
 # CONFIG_FEATURE_HAVE_RPC is not set
 
@@ -73,13 +75,12 @@ CONFIG_NO_DEBUG_LIB=y
 # CONFIG_EFENCE is not set
 
 #
-# Installation Options
+# Installation Options ("make install" behavior)
 #
-# CONFIG_INSTALL_NO_USR is not set
-# CONFIG_INSTALL_APPLET_SYMLINKS is not set
+CONFIG_INSTALL_APPLET_SYMLINKS=y
 # CONFIG_INSTALL_APPLET_HARDLINKS is not set
 # CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
-CONFIG_INSTALL_APPLET_DONT=y
+# CONFIG_INSTALL_APPLET_DONT is not set
 # CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
 # CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
 # CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
@@ -89,22 +90,23 @@ CONFIG_PREFIX="./_install"
 # Busybox Library Tuning
 #
 CONFIG_PASSWORD_MINLEN=6
-CONFIG_MD5_SIZE_VS_SPEED=1
-# CONFIG_FEATURE_FAST_TOP is not set
+CONFIG_MD5_SMALL=1
+CONFIG_FEATURE_FAST_TOP=y
 # CONFIG_FEATURE_ETC_NETWORKS is not set
+CONFIG_FEATURE_USE_TERMIOS=y
 CONFIG_FEATURE_EDITING=y
 CONFIG_FEATURE_EDITING_MAX_LEN=1024
 # CONFIG_FEATURE_EDITING_VI is not set
-CONFIG_FEATURE_EDITING_HISTORY=15
+CONFIG_FEATURE_EDITING_HISTORY=30
 # CONFIG_FEATURE_EDITING_SAVEHISTORY is not set
 CONFIG_FEATURE_TAB_COMPLETION=y
-CONFIG_FEATURE_USERNAME_COMPLETION=y
-CONFIG_FEATURE_EDITING_FANCY_PROMPT=y
-CONFIG_FEATURE_EDITING_ASK_TERMINAL=y
+# CONFIG_FEATURE_USERNAME_COMPLETION is not set
+# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set
+# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
 CONFIG_FEATURE_NON_POSIX_CP=y
-CONFIG_FEATURE_VERBOSE_CP_MESSAGE=y
+# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
 CONFIG_FEATURE_COPYBUF_KB=4
-CONFIG_MONOTONIC_SYSCALL=y
+# CONFIG_MONOTONIC_SYSCALL is not set
 CONFIG_IOCTL_HEX2STR_ERROR=y
 CONFIG_FEATURE_HWIB=y
 
@@ -115,18 +117,18 @@ CONFIG_FEATURE_HWIB=y
 #
 # Archival Utilities
 #
-# CONFIG_FEATURE_SEAMLESS_XZ is not set
+CONFIG_FEATURE_SEAMLESS_XZ=y
 CONFIG_FEATURE_SEAMLESS_LZMA=y
 CONFIG_FEATURE_SEAMLESS_BZ2=y
 CONFIG_FEATURE_SEAMLESS_GZ=y
 CONFIG_FEATURE_SEAMLESS_Z=y
-# CONFIG_AR is not set
-# CONFIG_FEATURE_AR_LONG_FILENAMES is not set
-# CONFIG_FEATURE_AR_CREATE is not set
+CONFIG_AR=y
+CONFIG_FEATURE_AR_LONG_FILENAMES=y
+CONFIG_FEATURE_AR_CREATE=y
 CONFIG_BUNZIP2=y
 CONFIG_BZIP2=y
 CONFIG_CPIO=y
-CONFIG_FEATURE_CPIO_O=y
+# CONFIG_FEATURE_CPIO_O is not set
 # CONFIG_FEATURE_CPIO_P is not set
 # CONFIG_DPKG is not set
 # CONFIG_DPKG_DEB is not set
@@ -134,20 +136,20 @@ CONFIG_FEATURE_CPIO_O=y
 CONFIG_GUNZIP=y
 CONFIG_GZIP=y
 CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
-# CONFIG_LZOP is not set
+CONFIG_LZOP=y
 # CONFIG_LZOP_COMPR_HIGH is not set
 CONFIG_RPM2CPIO=y
 CONFIG_RPM=y
 CONFIG_TAR=y
 CONFIG_FEATURE_TAR_CREATE=y
-# CONFIG_FEATURE_TAR_AUTODETECT is not set
-# CONFIG_FEATURE_TAR_FROM is not set
-# CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY is not set
-# CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set
+CONFIG_FEATURE_TAR_AUTODETECT=y
+CONFIG_FEATURE_TAR_FROM=y
+CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y
+CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y
 CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
 CONFIG_FEATURE_TAR_LONG_OPTIONS=y
 CONFIG_FEATURE_TAR_TO_COMMAND=y
-CONFIG_FEATURE_TAR_UNAME_GNAME=y
+# CONFIG_FEATURE_TAR_UNAME_GNAME is not set
 CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
 # CONFIG_FEATURE_TAR_SELINUX is not set
 CONFIG_UNCOMPRESS=y
@@ -163,33 +165,34 @@ CONFIG_UNZIP=y
 #
 CONFIG_BASENAME=y
 CONFIG_CAT=y
-CONFIG_DATE=y
-CONFIG_FEATURE_DATE_ISOFMT=y
+# CONFIG_DATE is not set
+# CONFIG_FEATURE_DATE_ISOFMT is not set
 # CONFIG_FEATURE_DATE_NANO is not set
-CONFIG_FEATURE_DATE_COMPAT=y
+# CONFIG_FEATURE_DATE_COMPAT is not set
 CONFIG_TEST=y
 CONFIG_FEATURE_TEST_64=y
 CONFIG_TR=y
-# CONFIG_FEATURE_TR_CLASSES is not set
-# CONFIG_FEATURE_TR_EQUIV is not set
+CONFIG_FEATURE_TR_CLASSES=y
+CONFIG_FEATURE_TR_EQUIV=y
+# CONFIG_BASE64 is not set
 CONFIG_CAL=y
-# CONFIG_CATV is not set
+CONFIG_CATV=y
 CONFIG_CHGRP=y
 CONFIG_CHMOD=y
 CONFIG_CHOWN=y
 CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y
 CONFIG_CHROOT=y
-# CONFIG_CKSUM is not set
-# CONFIG_COMM is not set
+CONFIG_CKSUM=y
+CONFIG_COMM=y
 CONFIG_CP=y
 CONFIG_FEATURE_CP_LONG_OPTIONS=y
 CONFIG_CUT=y
 CONFIG_DD=y
 CONFIG_FEATURE_DD_SIGNAL_HANDLING=y
-# CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set
+CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y
 CONFIG_FEATURE_DD_IBS_OBS=y
-CONFIG_DF=y
-CONFIG_FEATURE_DF_FANCY=y
+# CONFIG_DF is not set
+# CONFIG_FEATURE_DF_FANCY is not set
 CONFIG_DIRNAME=y
 CONFIG_DOS2UNIX=y
 CONFIG_UNIX2DOS=y
@@ -199,19 +202,19 @@ CONFIG_ECHO=y
 CONFIG_FEATURE_FANCY_ECHO=y
 CONFIG_ENV=y
 CONFIG_FEATURE_ENV_LONG_OPTIONS=y
-# CONFIG_EXPAND is not set
-# CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set
+CONFIG_EXPAND=y
+CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y
 CONFIG_EXPR=y
 CONFIG_EXPR_MATH_SUPPORT_64=y
 CONFIG_FALSE=y
 CONFIG_FOLD=y
-# CONFIG_FSYNC is not set
+CONFIG_FSYNC=y
 CONFIG_HEAD=y
 CONFIG_FEATURE_FANCY_HEAD=y
 CONFIG_HOSTID=y
 CONFIG_ID=y
-# CONFIG_INSTALL is not set
-# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set
+CONFIG_INSTALL=y
+CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y
 CONFIG_LENGTH=y
 CONFIG_LN=y
 CONFIG_LOGNAME=y
@@ -223,18 +226,18 @@ CONFIG_FEATURE_LS_SORTFILES=y
 CONFIG_FEATURE_LS_TIMESTAMPS=y
 CONFIG_FEATURE_LS_USERNAME=y
 CONFIG_FEATURE_LS_COLOR=y
-# CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set
+CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y
 CONFIG_MD5SUM=y
 CONFIG_MKDIR=y
 CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y
 CONFIG_MKFIFO=y
-CONFIG_MKNOD=y
+# CONFIG_MKNOD is not set
 CONFIG_MV=y
 CONFIG_FEATURE_MV_LONG_OPTIONS=y
-# CONFIG_NICE is not set
-# CONFIG_NOHUP is not set
+CONFIG_NICE=y
+CONFIG_NOHUP=y
 CONFIG_OD=y
-# CONFIG_PRINTENV is not set
+CONFIG_PRINTENV=y
 CONFIG_PRINTF=y
 CONFIG_PWD=y
 CONFIG_READLINK=y
@@ -242,24 +245,24 @@ CONFIG_FEATURE_READLINK_FOLLOW=y
 CONFIG_REALPATH=y
 CONFIG_RM=y
 CONFIG_RMDIR=y
-CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y
-# CONFIG_SEQ is not set
+# CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set
+CONFIG_SEQ=y
 CONFIG_SHA1SUM=y
 CONFIG_SHA256SUM=y
 CONFIG_SHA512SUM=y
 CONFIG_SLEEP=y
 CONFIG_FEATURE_FANCY_SLEEP=y
-# CONFIG_FEATURE_FLOAT_SLEEP is not set
+CONFIG_FEATURE_FLOAT_SLEEP=y
 CONFIG_SORT=y
 CONFIG_FEATURE_SORT_BIG=y
-# CONFIG_SPLIT is not set
-# CONFIG_FEATURE_SPLIT_FANCY is not set
+CONFIG_SPLIT=y
+CONFIG_FEATURE_SPLIT_FANCY=y
 # CONFIG_STAT is not set
 # CONFIG_FEATURE_STAT_FORMAT is not set
-CONFIG_STTY=y
-# CONFIG_SUM is not set
+# CONFIG_STTY is not set
+CONFIG_SUM=y
 CONFIG_SYNC=y
-CONFIG_TAC=y
+# CONFIG_TAC is not set
 CONFIG_TAIL=y
 CONFIG_FEATURE_FANCY_TAIL=y
 CONFIG_TEE=y
@@ -268,15 +271,15 @@ CONFIG_TOUCH=y
 CONFIG_TRUE=y
 CONFIG_TTY=y
 CONFIG_UNAME=y
-# CONFIG_UNEXPAND is not set
-# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set
+CONFIG_UNEXPAND=y
+CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y
 CONFIG_UNIQ=y
 CONFIG_USLEEP=y
 CONFIG_UUDECODE=y
 CONFIG_UUENCODE=y
 CONFIG_WC=y
-# CONFIG_FEATURE_WC_LARGE is not set
-CONFIG_WHO=y
+CONFIG_FEATURE_WC_LARGE=y
+# CONFIG_WHO is not set
 CONFIG_WHOAMI=y
 CONFIG_YES=y
 
@@ -298,35 +301,31 @@ CONFIG_FEATURE_HUMAN_READABLE=y
 #
 # Common options for md5sum, sha1sum, sha256sum, sha512sum
 #
-# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set
+CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
 
 #
 # Console Utilities
 #
-CONFIG_CHVT=y
+# CONFIG_CHVT is not set
 # CONFIG_FGCONSOLE is not set
 CONFIG_CLEAR=y
-CONFIG_DEALLOCVT=y
-CONFIG_DUMPKMAP=y
+# CONFIG_DEALLOCVT is not set
+# CONFIG_DUMPKMAP is not set
 # CONFIG_KBD_MODE is not set
-CONFIG_LOADFONT=y
-CONFIG_LOADKMAP=y
-CONFIG_OPENVT=y
+# CONFIG_LOADFONT is not set
+# CONFIG_LOADKMAP is not set
+# CONFIG_OPENVT is not set
 CONFIG_RESET=y
-# CONFIG_RESIZE is not set
-# CONFIG_FEATURE_RESIZE_PRINT is not set
+CONFIG_RESIZE=y
+CONFIG_FEATURE_RESIZE_PRINT=y
 # CONFIG_SETCONSOLE is not set
 # CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
 # CONFIG_SETFONT is not set
 # CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
 CONFIG_DEFAULT_SETFONT_DIR=""
-CONFIG_SETKEYCODES=y
+# CONFIG_SETKEYCODES is not set
 # CONFIG_SETLOGCONS is not set
 # CONFIG_SHOWKEY is not set
-
-#
-# Common options for loadfont and setfont
-#
 # CONFIG_FEATURE_LOADFONT_PSF2 is not set
 # CONFIG_FEATURE_LOADFONT_RAW is not set
 
@@ -334,26 +333,26 @@ CONFIG_SETKEYCODES=y
 # Debian Utilities
 #
 CONFIG_MKTEMP=y
-# CONFIG_PIPE_PROGRESS is not set
+CONFIG_PIPE_PROGRESS=y
 CONFIG_RUN_PARTS=y
 CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y
-# CONFIG_FEATURE_RUN_PARTS_FANCY is not set
-CONFIG_START_STOP_DAEMON=y
-CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y
-CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS=y
+CONFIG_FEATURE_RUN_PARTS_FANCY=y
+# CONFIG_START_STOP_DAEMON is not set
+# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set
+# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set
 CONFIG_WHICH=y
 
 #
 # Editors
 #
+CONFIG_PATCH=y
 CONFIG_AWK=y
 CONFIG_FEATURE_AWK_LIBM=y
 CONFIG_CMP=y
-# CONFIG_DIFF is not set
-# CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set
-# CONFIG_FEATURE_DIFF_DIR is not set
-# CONFIG_ED is not set
-CONFIG_PATCH=y
+CONFIG_DIFF=y
+CONFIG_FEATURE_DIFF_LONG_OPTIONS=y
+CONFIG_FEATURE_DIFF_DIR=y
+CONFIG_ED=y
 CONFIG_SED=y
 CONFIG_VI=y
 CONFIG_FEATURE_VI_MAX_LEN=1024
@@ -368,7 +367,6 @@ CONFIG_FEATURE_VI_SETOPTS=y
 CONFIG_FEATURE_VI_SET=y
 CONFIG_FEATURE_VI_WIN_RESIZE=y
 CONFIG_FEATURE_VI_ASK_TERMINAL=y
-CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y
 CONFIG_FEATURE_ALLOW_EXEC=y
 
 #
@@ -392,7 +390,7 @@ CONFIG_FEATURE_FIND_DEPTH=y
 CONFIG_FEATURE_FIND_PAREN=y
 CONFIG_FEATURE_FIND_SIZE=y
 CONFIG_FEATURE_FIND_PRUNE=y
-# CONFIG_FEATURE_FIND_DELETE is not set
+CONFIG_FEATURE_FIND_DELETE=y
 CONFIG_FEATURE_FIND_PATH=y
 CONFIG_FEATURE_FIND_REGEX=y
 # CONFIG_FEATURE_FIND_CONTEXT is not set
@@ -402,10 +400,10 @@ CONFIG_FEATURE_GREP_EGREP_ALIAS=y
 CONFIG_FEATURE_GREP_FGREP_ALIAS=y
 CONFIG_FEATURE_GREP_CONTEXT=y
 CONFIG_XARGS=y
-# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set
-# CONFIG_FEATURE_XARGS_SUPPORT_QUOTES is not set
-# CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT is not set
-# CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM is not set
+CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
+CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
+CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
+CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
 
 #
 # Init Utilities
@@ -413,6 +411,9 @@ CONFIG_XARGS=y
 # CONFIG_BOOTCHARTD is not set
 # CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set
 # CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set
+# CONFIG_HALT is not set
+# CONFIG_FEATURE_CALL_TELINIT is not set
+CONFIG_TELINIT_PATH=""
 # CONFIG_INIT is not set
 # CONFIG_FEATURE_USE_INITTAB is not set
 # CONFIG_FEATURE_KILL_REMOVED is not set
@@ -422,30 +423,30 @@ CONFIG_FEATURE_KILL_DELAY=0
 # CONFIG_FEATURE_EXTRA_QUIET is not set
 # CONFIG_FEATURE_INIT_COREDUMPS is not set
 # CONFIG_FEATURE_INITRD is not set
-# CONFIG_HALT is not set
-# CONFIG_FEATURE_CALL_TELINIT is not set
-CONFIG_TELINIT_PATH=""
+CONFIG_INIT_TERMINAL_TYPE=""
 # CONFIG_MESG is not set
 
 #
 # Login/Password Management Utilities
 #
+# CONFIG_ADD_SHELL is not set
+# CONFIG_REMOVE_SHELL is not set
 # CONFIG_FEATURE_SHADOWPASSWDS is not set
-# CONFIG_USE_BB_PWD_GRP is not set
+CONFIG_USE_BB_PWD_GRP=y
 # CONFIG_USE_BB_SHADOW is not set
-CONFIG_USE_BB_CRYPT=y
-CONFIG_USE_BB_CRYPT_SHA=y
-# CONFIG_ADDGROUP is not set
-# CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set
-# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
-# CONFIG_DELGROUP is not set
-# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
-# CONFIG_FEATURE_CHECK_NAMES is not set
+# CONFIG_USE_BB_CRYPT is not set
+# CONFIG_USE_BB_CRYPT_SHA is not set
 # CONFIG_ADDUSER is not set
 # CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set
-CONFIG_FIRST_SYSTEM_ID=0
-CONFIG_LAST_SYSTEM_ID=0
+# CONFIG_FEATURE_CHECK_NAMES is not set
+CONFIG_FIRST_SYSTEM_ID=100
+CONFIG_LAST_SYSTEM_ID=999
+CONFIG_ADDGROUP=y
+CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y
+CONFIG_FEATURE_ADDUSER_TO_GROUP=y
 # CONFIG_DELUSER is not set
+CONFIG_DELGROUP=y
+CONFIG_FEATURE_DEL_USER_FROM_GROUP=y
 # CONFIG_GETTY is not set
 # CONFIG_LOGIN is not set
 # CONFIG_PAM is not set
@@ -465,14 +466,10 @@ CONFIG_LAST_SYSTEM_ID=0
 #
 # Linux Ext2 FS Progs
 #
-# CONFIG_CHATTR is not set
+CONFIG_CHATTR=y
 # CONFIG_FSCK is not set
 # CONFIG_LSATTR is not set
 # CONFIG_TUNE2FS is not set
-
-#
-# Linux Module Utilities
-#
 # CONFIG_MODINFO is not set
 # CONFIG_MODPROBE_SMALL is not set
 # CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set
@@ -484,10 +481,6 @@ CONFIG_LAST_SYSTEM_ID=0
 # CONFIG_MODPROBE is not set
 # CONFIG_FEATURE_MODPROBE_BLACKLIST is not set
 # CONFIG_DEPMOD is not set
-
-#
-# Options common to multiple modutils
-#
 # CONFIG_FEATURE_2_4_MODULES is not set
 # CONFIG_FEATURE_INSMOD_TRY_MMAP is not set
 # CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
@@ -504,13 +497,13 @@ CONFIG_DEFAULT_DEPMOD_FILE=""
 #
 # Linux System Utilities
 #
-CONFIG_BLOCKDEV=y
+# CONFIG_BLOCKDEV is not set
 CONFIG_REV=y
 # CONFIG_ACPID is not set
 # CONFIG_FEATURE_ACPID_COMPAT is not set
 # CONFIG_BLKID is not set
-CONFIG_DMESG=y
-CONFIG_FEATURE_DMESG_PRETTY=y
+# CONFIG_DMESG is not set
+# CONFIG_FEATURE_DMESG_PRETTY is not set
 # CONFIG_FBSET is not set
 # CONFIG_FEATURE_FBSET_FANCY is not set
 # CONFIG_FEATURE_FBSET_READMODE is not set
@@ -523,9 +516,10 @@ CONFIG_FDISK_SUPPORT_LARGE_DISKS=y
 # CONFIG_FEATURE_SGI_LABEL is not set
 # CONFIG_FEATURE_SUN_LABEL is not set
 # CONFIG_FEATURE_OSF_LABEL is not set
+# CONFIG_FEATURE_GPT_LABEL is not set
 # CONFIG_FEATURE_FDISK_ADVANCED is not set
 # CONFIG_FINDFS is not set
-# CONFIG_FLOCK is not set
+CONFIG_FLOCK=y
 # CONFIG_FREERAMDISK is not set
 # CONFIG_FSCK_MINIX is not set
 # CONFIG_MKFS_EXT2 is not set
@@ -537,15 +531,15 @@ CONFIG_GETOPT=y
 CONFIG_FEATURE_GETOPT_LONG=y
 CONFIG_HEXDUMP=y
 # CONFIG_FEATURE_HEXDUMP_REVERSE is not set
-# CONFIG_HD is not set
+CONFIG_HD=y
 # CONFIG_HWCLOCK is not set
 # CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set
 # CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set
 # CONFIG_IPCRM is not set
 # CONFIG_IPCS is not set
-CONFIG_LOSETUP=y
-# CONFIG_LSPCI is not set
-# CONFIG_LSUSB is not set
+# CONFIG_LOSETUP is not set
+CONFIG_LSPCI=y
+CONFIG_LSUSB=y
 # CONFIG_MDEV is not set
 # CONFIG_FEATURE_MDEV_CONF is not set
 # CONFIG_FEATURE_MDEV_RENAME is not set
@@ -555,67 +549,61 @@ CONFIG_LOSETUP=y
 # CONFIG_MKSWAP is not set
 # CONFIG_FEATURE_MKSWAP_UUID is not set
 CONFIG_MORE=y
-CONFIG_FEATURE_USE_TERMIOS=y
-CONFIG_MOUNT=y
+# CONFIG_MOUNT is not set
 # CONFIG_FEATURE_MOUNT_FAKE is not set
 # CONFIG_FEATURE_MOUNT_VERBOSE is not set
-CONFIG_FEATURE_MOUNT_HELPERS=y
-CONFIG_FEATURE_MOUNT_LABEL=y
+# CONFIG_FEATURE_MOUNT_HELPERS is not set
+# CONFIG_FEATURE_MOUNT_LABEL is not set
 # CONFIG_FEATURE_MOUNT_NFS is not set
 # CONFIG_FEATURE_MOUNT_CIFS is not set
-CONFIG_FEATURE_MOUNT_FLAGS=y
-CONFIG_FEATURE_MOUNT_FSTAB=y
+# CONFIG_FEATURE_MOUNT_FLAGS is not set
+# CONFIG_FEATURE_MOUNT_FSTAB is not set
 # CONFIG_PIVOT_ROOT is not set
-CONFIG_RDATE=y
+# CONFIG_RDATE is not set
 # CONFIG_RDEV is not set
-# CONFIG_READPROFILE is not set
+CONFIG_READPROFILE=y
 # CONFIG_RTCWAKE is not set
 # CONFIG_SCRIPT is not set
-# CONFIG_SCRIPTREPLAY is not set
+CONFIG_SCRIPTREPLAY=y
 # CONFIG_SETARCH is not set
-CONFIG_SWAPONOFF=y
+# CONFIG_SWAPONOFF is not set
 # CONFIG_FEATURE_SWAPON_PRI is not set
 # CONFIG_SWITCH_ROOT is not set
-CONFIG_UMOUNT=y
-CONFIG_FEATURE_UMOUNT_ALL=y
-
-#
-# Common options for mount/umount
-#
-CONFIG_FEATURE_MOUNT_LOOP=y
+# CONFIG_UMOUNT is not set
+# CONFIG_FEATURE_UMOUNT_ALL is not set
+# CONFIG_FEATURE_MOUNT_LOOP is not set
 # CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set
 # CONFIG_FEATURE_MTAB_SUPPORT is not set
-CONFIG_VOLUMEID=y
-
-#
-# Filesystem/Volume identification
-#
-CONFIG_FEATURE_VOLUMEID_EXT=y
-CONFIG_FEATURE_VOLUMEID_BTRFS=y
-CONFIG_FEATURE_VOLUMEID_REISERFS=y
-CONFIG_FEATURE_VOLUMEID_FAT=y
-CONFIG_FEATURE_VOLUMEID_HFS=y
-CONFIG_FEATURE_VOLUMEID_JFS=y
-CONFIG_FEATURE_VOLUMEID_XFS=y
-CONFIG_FEATURE_VOLUMEID_NTFS=y
-CONFIG_FEATURE_VOLUMEID_ISO9660=y
-CONFIG_FEATURE_VOLUMEID_UDF=y
-CONFIG_FEATURE_VOLUMEID_LUKS=y
-CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y
-CONFIG_FEATURE_VOLUMEID_CRAMFS=y
-CONFIG_FEATURE_VOLUMEID_ROMFS=y
-CONFIG_FEATURE_VOLUMEID_SYSV=y
-CONFIG_FEATURE_VOLUMEID_OCFS2=y
-CONFIG_FEATURE_VOLUMEID_LINUXRAID=y
+# CONFIG_VOLUMEID is not set
+# CONFIG_FEATURE_VOLUMEID_EXT is not set
+# CONFIG_FEATURE_VOLUMEID_BTRFS is not set
+# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
+# CONFIG_FEATURE_VOLUMEID_FAT is not set
+# CONFIG_FEATURE_VOLUMEID_HFS is not set
+# CONFIG_FEATURE_VOLUMEID_JFS is not set
+# CONFIG_FEATURE_VOLUMEID_XFS is not set
+# CONFIG_FEATURE_VOLUMEID_NTFS is not set
+# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set
+# CONFIG_FEATURE_VOLUMEID_UDF is not set
+# CONFIG_FEATURE_VOLUMEID_LUKS is not set
+# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set
+# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set
+# CONFIG_FEATURE_VOLUMEID_ROMFS is not set
+# CONFIG_FEATURE_VOLUMEID_SYSV is not set
+# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set
+# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set
 
 #
 # Miscellaneous Utilities
 #
 # CONFIG_CONSPY is not set
+# CONFIG_NANDWRITE is not set
+# CONFIG_NANDDUMP is not set
 # CONFIG_UBIATTACH is not set
 # CONFIG_UBIDETACH is not set
-CONFIG_ADJTIMEX=y
+# CONFIG_ADJTIMEX is not set
 # CONFIG_BBCONFIG is not set
+# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
 # CONFIG_BEEP is not set
 CONFIG_FEATURE_BEEP_FREQ=0
 CONFIG_FEATURE_BEEP_LENGTH_MS=0
@@ -627,12 +615,12 @@ CONFIG_FEATURE_BEEP_LENGTH_MS=0
 # CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set
 # CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set
 # CONFIG_FEATURE_CHAT_CLR_ABORT is not set
-# CONFIG_CHRT is not set
+CONFIG_CHRT=y
 # CONFIG_CROND is not set
 # CONFIG_FEATURE_CROND_D is not set
 # CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
-CONFIG_FEATURE_CROND_DIR=""
-# CONFIG_CRONTAB is not set
+CONFIG_FEATURE_CROND_DIR="/var/spool/cron"
+CONFIG_CRONTAB=y
 CONFIG_DC=y
 CONFIG_FEATURE_DC_LIBM=y
 # CONFIG_DEVFSD is not set
@@ -648,17 +636,17 @@ CONFIG_FEATURE_DC_LIBM=y
 # CONFIG_FLASH_LOCK is not set
 # CONFIG_FLASH_UNLOCK is not set
 # CONFIG_FLASH_ERASEALL is not set
-CONFIG_IONICE=y
+# CONFIG_IONICE is not set
 # CONFIG_INOTIFYD is not set
-CONFIG_LAST=y
-CONFIG_FEATURE_LAST_SMALL=y
+# CONFIG_LAST is not set
+# CONFIG_FEATURE_LAST_SMALL is not set
 # CONFIG_FEATURE_LAST_FANCY is not set
-# CONFIG_LESS is not set
-CONFIG_FEATURE_LESS_MAXLINES=0
-# CONFIG_FEATURE_LESS_BRACKETS is not set
-# CONFIG_FEATURE_LESS_FLAGS is not set
-# CONFIG_FEATURE_LESS_MARKS is not set
-# CONFIG_FEATURE_LESS_REGEXP is not set
+CONFIG_LESS=y
+CONFIG_FEATURE_LESS_MAXLINES=9999999
+CONFIG_FEATURE_LESS_BRACKETS=y
+CONFIG_FEATURE_LESS_FLAGS=y
+CONFIG_FEATURE_LESS_MARKS=y
+CONFIG_FEATURE_LESS_REGEXP=y
 # CONFIG_FEATURE_LESS_WINCH is not set
 # CONFIG_FEATURE_LESS_DASHCMD is not set
 # CONFIG_FEATURE_LESS_LINENUMS is not set
@@ -673,69 +661,71 @@ CONFIG_FEATURE_LESS_MAXLINES=0
 # CONFIG_FEATURE_MAKEDEVS_LEAF is not set
 # CONFIG_FEATURE_MAKEDEVS_TABLE is not set
 # CONFIG_MAN is not set
-# CONFIG_MICROCOM is not set
+CONFIG_MICROCOM=y
 # CONFIG_MOUNTPOINT is not set
-CONFIG_MT=y
+# CONFIG_MT is not set
 # CONFIG_RAIDAUTORUN is not set
 # CONFIG_READAHEAD is not set
 # CONFIG_RFKILL is not set
 # CONFIG_RUNLEVEL is not set
 # CONFIG_RX is not set
-# CONFIG_SETSID is not set
+CONFIG_SETSID=y
 CONFIG_STRINGS=y
 # CONFIG_TASKSET is not set
 # CONFIG_FEATURE_TASKSET_FANCY is not set
-CONFIG_TIME=y
-# CONFIG_TIMEOUT is not set
-# CONFIG_TTYSIZE is not set
-# CONFIG_VOLNAME is not set
+# CONFIG_TIME is not set
+CONFIG_TIMEOUT=y
+CONFIG_TTYSIZE=y
+CONFIG_VOLNAME=y
 # CONFIG_WALL is not set
-CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG is not set
 
 #
 # Networking Utilities
 #
+# CONFIG_NBDCLIENT is not set
 CONFIG_NC=y
 # CONFIG_NC_SERVER is not set
 # CONFIG_NC_EXTRA is not set
 # CONFIG_NC_110_COMPAT is not set
-CONFIG_FEATURE_IPV6=y
+# CONFIG_FEATURE_IPV6 is not set
 # CONFIG_FEATURE_UNIX_LOCAL is not set
 # CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set
 # CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
 # CONFIG_ARP is not set
-CONFIG_ARPING=y
-CONFIG_BRCTL=y
+# CONFIG_ARPING is not set
+# CONFIG_BRCTL is not set
 # CONFIG_FEATURE_BRCTL_FANCY is not set
 # CONFIG_FEATURE_BRCTL_SHOW is not set
-# CONFIG_DNSD is not set
+CONFIG_DNSD=y
 # CONFIG_ETHER_WAKE is not set
-# CONFIG_FAKEIDENTD is not set
-# CONFIG_FTPD is not set
-# CONFIG_FEATURE_FTP_WRITE is not set
-# CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set
+CONFIG_FAKEIDENTD=y
+CONFIG_FTPD=y
+CONFIG_FEATURE_FTP_WRITE=y
+CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y
 CONFIG_FTPGET=y
 CONFIG_FTPPUT=y
 CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
 CONFIG_HOSTNAME=y
 CONFIG_HTTPD=y
-# CONFIG_FEATURE_HTTPD_RANGES is not set
+CONFIG_FEATURE_HTTPD_RANGES=y
 # CONFIG_FEATURE_HTTPD_USE_SENDFILE is not set
 # CONFIG_FEATURE_HTTPD_SETUID is not set
 CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
-# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
-# CONFIG_FEATURE_HTTPD_CGI is not set
-# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
-# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
-# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set
-# CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set
-# CONFIG_FEATURE_HTTPD_PROXY is not set
-CONFIG_IFCONFIG=y
-CONFIG_FEATURE_IFCONFIG_STATUS=y
-CONFIG_FEATURE_IFCONFIG_SLIP=y
-CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y
-CONFIG_FEATURE_IFCONFIG_HW=y
-CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y
+CONFIG_FEATURE_HTTPD_AUTH_MD5=y
+CONFIG_FEATURE_HTTPD_CGI=y
+CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y
+CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y
+CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
+CONFIG_FEATURE_HTTPD_ERROR_PAGES=y
+CONFIG_FEATURE_HTTPD_PROXY=y
+CONFIG_FEATURE_HTTPD_GZIP=y
+# CONFIG_IFCONFIG is not set
+# CONFIG_FEATURE_IFCONFIG_STATUS is not set
+# CONFIG_FEATURE_IFCONFIG_SLIP is not set
+# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
+# CONFIG_FEATURE_IFCONFIG_HW is not set
+# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
 # CONFIG_IFENSLAVE is not set
 # CONFIG_IFPLUGD is not set
 # CONFIG_IFUPDOWN is not set
@@ -754,12 +744,12 @@ CONFIG_IFUPDOWN_IFSTATE_PATH=""
 # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set
 # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set
 # CONFIG_FEATURE_INETD_RPC is not set
-CONFIG_IP=y
-CONFIG_FEATURE_IP_ADDRESS=y
-CONFIG_FEATURE_IP_LINK=y
-CONFIG_FEATURE_IP_ROUTE=y
-CONFIG_FEATURE_IP_TUNNEL=y
-CONFIG_FEATURE_IP_RULE=y
+# CONFIG_IP is not set
+# CONFIG_FEATURE_IP_ADDRESS is not set
+# CONFIG_FEATURE_IP_LINK is not set
+# CONFIG_FEATURE_IP_ROUTE is not set
+# CONFIG_FEATURE_IP_TUNNEL is not set
+# CONFIG_FEATURE_IP_RULE is not set
 # CONFIG_FEATURE_IP_SHORT_FORMS is not set
 # CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
 # CONFIG_IPADDR is not set
@@ -770,29 +760,29 @@ CONFIG_FEATURE_IP_RULE=y
 CONFIG_IPCALC=y
 CONFIG_FEATURE_IPCALC_FANCY=y
 CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y
-CONFIG_NAMEIF=y
+# CONFIG_NAMEIF is not set
 # CONFIG_FEATURE_NAMEIF_EXTENDED is not set
-CONFIG_NETSTAT=y
+# CONFIG_NETSTAT is not set
 # CONFIG_FEATURE_NETSTAT_WIDE is not set
 # CONFIG_FEATURE_NETSTAT_PRG is not set
-CONFIG_NSLOOKUP=y
+# CONFIG_NSLOOKUP is not set
 # CONFIG_NTPD is not set
 # CONFIG_FEATURE_NTPD_SERVER is not set
-CONFIG_PING=y
-CONFIG_PING6=y
-CONFIG_FEATURE_FANCY_PING=y
-# CONFIG_PSCAN is not set
-CONFIG_ROUTE=y
+# CONFIG_PING is not set
+# CONFIG_PING6 is not set
+# CONFIG_FEATURE_FANCY_PING is not set
+CONFIG_PSCAN=y
+# CONFIG_ROUTE is not set
 # CONFIG_SLATTACH is not set
 # CONFIG_TCPSVD is not set
 CONFIG_TELNET=y
 CONFIG_FEATURE_TELNET_TTYPE=y
 CONFIG_FEATURE_TELNET_AUTOLOGIN=y
-# CONFIG_TELNETD is not set
-# CONFIG_FEATURE_TELNETD_STANDALONE is not set
-# CONFIG_FEATURE_TELNETD_INETD_WAIT is not set
+CONFIG_TELNETD=y
+CONFIG_FEATURE_TELNETD_STANDALONE=y
+CONFIG_FEATURE_TELNETD_INETD_WAIT=y
 CONFIG_TFTP=y
-# CONFIG_TFTPD is not set
+CONFIG_TFTPD=y
 
 #
 # Common options for tftp/tftpd
@@ -802,25 +792,25 @@ CONFIG_FEATURE_TFTP_PUT=y
 CONFIG_FEATURE_TFTP_BLOCKSIZE=y
 CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
 # CONFIG_TFTP_DEBUG is not set
-CONFIG_TRACEROUTE=y
-CONFIG_TRACEROUTE6=y
-CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
+# CONFIG_TRACEROUTE is not set
+# CONFIG_TRACEROUTE6 is not set
+# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
 # CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
 # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
 # CONFIG_TUNCTL is not set
 # CONFIG_FEATURE_TUNCTL_UG is not set
-CONFIG_UDHCPD=y
+# CONFIG_UDHCPD is not set
 # CONFIG_DHCPRELAY is not set
-CONFIG_DUMPLEASES=y
+# CONFIG_DUMPLEASES is not set
 # CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
-CONFIG_DHCPD_LEASES_FILE="/var/lib/misc/udhcpd.leases"
-CONFIG_UDHCPC=y
-CONFIG_FEATURE_UDHCPC_ARPING=y
+CONFIG_DHCPD_LEASES_FILE=""
+# CONFIG_UDHCPC is not set
+# CONFIG_FEATURE_UDHCPC_ARPING is not set
 # CONFIG_FEATURE_UDHCP_PORT is not set
 CONFIG_UDHCP_DEBUG=0
-CONFIG_FEATURE_UDHCP_RFC3397=y
-CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script"
-CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80
+# CONFIG_FEATURE_UDHCP_RFC3397 is not set
+CONFIG_UDHCPC_DEFAULT_SCRIPT=""
+CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0
 CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS=""
 # CONFIG_UDPSVD is not set
 # CONFIG_VCONFIG is not set
@@ -828,14 +818,15 @@ CONFIG_WGET=y
 CONFIG_FEATURE_WGET_STATUSBAR=y
 CONFIG_FEATURE_WGET_AUTHENTICATION=y
 CONFIG_FEATURE_WGET_LONG_OPTIONS=y
+CONFIG_FEATURE_WGET_TIMEOUT=y
 # CONFIG_ZCIP is not set
 
 #
 # Print Utilities
 #
 # CONFIG_LPD is not set
-# CONFIG_LPR is not set
-# CONFIG_LPQ is not set
+CONFIG_LPR=y
+CONFIG_LPQ=y
 
 #
 # Mail Utilities
@@ -851,26 +842,30 @@ CONFIG_FEATURE_MIME_CHARSET=""
 #
 # Process Utilities
 #
-# CONFIG_SMEMCAP is not set
-CONFIG_FREE=y
+CONFIG_IOSTAT=y
+CONFIG_MPSTAT=y
+CONFIG_PMAP=y
+CONFIG_POWERTOP=y
+CONFIG_SMEMCAP=y
+# CONFIG_FREE is not set
 # CONFIG_FUSER is not set
 CONFIG_KILL=y
 CONFIG_KILLALL=y
-# CONFIG_KILLALL5 is not set
+CONFIG_KILLALL5=y
 # CONFIG_NMETER is not set
-# CONFIG_PGREP is not set
-CONFIG_PIDOF=y
+CONFIG_PGREP=y
+# CONFIG_PIDOF is not set
 # CONFIG_FEATURE_PIDOF_SINGLE is not set
 # CONFIG_FEATURE_PIDOF_OMIT is not set
-# CONFIG_PKILL is not set
+CONFIG_PKILL=y
 CONFIG_PS=y
-# CONFIG_FEATURE_PS_WIDE is not set
+CONFIG_FEATURE_PS_WIDE=y
 # CONFIG_FEATURE_PS_TIME is not set
 # CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set
 # CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
 CONFIG_RENICE=y
 CONFIG_BB_SYSCTL=y
-CONFIG_TOP=y
+# CONFIG_TOP is not set
 # CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set
 # CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set
 # CONFIG_FEATURE_TOP_SMP_CPU is not set
@@ -878,7 +873,7 @@ CONFIG_TOP=y
 # CONFIG_FEATURE_TOP_SMP_PROCESS is not set
 # CONFIG_FEATURE_TOPMEM is not set
 CONFIG_FEATURE_SHOW_THREADS=y
-CONFIG_UPTIME=y
+# CONFIG_UPTIME is not set
 CONFIG_WATCH=y
 
 #
@@ -915,22 +910,25 @@ CONFIG_SV_DEFAULT_SERVICE_DIR=""
 # Shells
 #
 CONFIG_ASH=y
-CONFIG_ASH_BASH_COMPAT=y
-CONFIG_ASH_JOB_CONTROL=y
-CONFIG_ASH_ALIAS=y
-CONFIG_ASH_GETOPTS=y
-CONFIG_ASH_BUILTIN_ECHO=y
-CONFIG_ASH_BUILTIN_PRINTF=y
-CONFIG_ASH_BUILTIN_TEST=y
-CONFIG_ASH_CMDCMD=y
-CONFIG_ASH_MAIL=y
-CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
-CONFIG_ASH_RANDOM_SUPPORT=y
-CONFIG_ASH_EXPAND_PRMT=y
+# CONFIG_ASH_BASH_COMPAT is not set
+# CONFIG_ASH_JOB_CONTROL is not set
+# CONFIG_ASH_ALIAS is not set
+# CONFIG_ASH_GETOPTS is not set
+# CONFIG_ASH_BUILTIN_ECHO is not set
+# CONFIG_ASH_BUILTIN_PRINTF is not set
+# CONFIG_ASH_BUILTIN_TEST is not set
+# CONFIG_ASH_CMDCMD is not set
+# CONFIG_ASH_MAIL is not set
+# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set
+# CONFIG_ASH_RANDOM_SUPPORT is not set
+# CONFIG_ASH_EXPAND_PRMT is not set
+# CONFIG_CTTYHACK is not set
 # CONFIG_HUSH is not set
 # CONFIG_HUSH_BASH_COMPAT is not set
+# CONFIG_HUSH_BRACE_EXPANSION is not set
 # CONFIG_HUSH_HELP is not set
 # CONFIG_HUSH_INTERACTIVE is not set
+# CONFIG_HUSH_SAVEHISTORY is not set
 # CONFIG_HUSH_JOB is not set
 # CONFIG_HUSH_TICK is not set
 # CONFIG_HUSH_IF is not set
@@ -938,35 +936,34 @@ CONFIG_ASH_EXPAND_PRMT=y
 # CONFIG_HUSH_CASE is not set
 # CONFIG_HUSH_FUNCTIONS is not set
 # CONFIG_HUSH_LOCAL is not set
-# CONFIG_HUSH_EXPORT_N is not set
 # CONFIG_HUSH_RANDOM_SUPPORT is not set
+# CONFIG_HUSH_EXPORT_N is not set
+# CONFIG_HUSH_MODE_X is not set
+# CONFIG_MSH is not set
 CONFIG_FEATURE_SH_IS_ASH=y
 # CONFIG_FEATURE_SH_IS_HUSH is not set
 # CONFIG_FEATURE_SH_IS_NONE is not set
 # CONFIG_FEATURE_BASH_IS_ASH is not set
 # CONFIG_FEATURE_BASH_IS_HUSH is not set
 CONFIG_FEATURE_BASH_IS_NONE=y
-# CONFIG_LASH is not set
-# CONFIG_MSH is not set
 CONFIG_SH_MATH_SUPPORT=y
 CONFIG_SH_MATH_SUPPORT_64=y
 # CONFIG_FEATURE_SH_EXTRA_QUIET is not set
-CONFIG_FEATURE_SH_STANDALONE=y
+# CONFIG_FEATURE_SH_STANDALONE is not set
 # CONFIG_FEATURE_SH_NOFORK is not set
-# CONFIG_CTTYHACK is not set
 
 #
 # System Logging Utilities
 #
 CONFIG_SYSLOGD=y
-# CONFIG_FEATURE_ROTATE_LOGFILE is not set
+CONFIG_FEATURE_ROTATE_LOGFILE=y
 CONFIG_FEATURE_REMOTE_LOG=y
 # CONFIG_FEATURE_SYSLOGD_DUP is not set
 CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256
 CONFIG_FEATURE_IPC_SYSLOG=y
-CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=64
+CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16
 CONFIG_LOGREAD=y
-# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
-CONFIG_KLOGD=y
-CONFIG_FEATURE_KLOGD_KLOGCTL=y
+CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y
+# CONFIG_KLOGD is not set
+# CONFIG_FEATURE_KLOGD_KLOGCTL is not set
 CONFIG_LOGGER=y
index 6e3191a..c657044 100644 (file)
@@ -10,6 +10,7 @@ INSERT
 config CHVT
        bool "chvt"
        default y
+       select PLATFORM_LINUX
        help
          This program is used to change to another terminal.
          Example: chvt 4 (change to terminal /dev/tty4)
@@ -17,6 +18,7 @@ config CHVT
 config FGCONSOLE
        bool "fgconsole"
        default y
+       select PLATFORM_LINUX
        help
          This program prints active (foreground) console number.
 
@@ -29,12 +31,14 @@ config CLEAR
 config DEALLOCVT
        bool "deallocvt"
        default y
+       select PLATFORM_LINUX
        help
          This program deallocates unused virtual consoles.
 
 config DUMPKMAP
        bool "dumpkmap"
        default y
+       select PLATFORM_LINUX
        help
          This program dumps the kernel's keyboard translation table to
          stdout, in binary format. You can then use loadkmap to load it.
@@ -42,18 +46,21 @@ config DUMPKMAP
 config KBD_MODE
        bool "kbd_mode"
        default y
+       select PLATFORM_LINUX
        help
          This program reports and sets keyboard mode.
 
 config LOADFONT
        bool "loadfont"
        default y
+       select PLATFORM_LINUX
        help
          This program loads a console font from standard input.
 
 config LOADKMAP
        bool "loadkmap"
        default y
+       select PLATFORM_LINUX
        help
          This program loads a keyboard translation table from
          standard input.
@@ -61,6 +68,7 @@ config LOADKMAP
 config OPENVT
        bool "openvt"
        default y
+       select PLATFORM_LINUX
        help
          This program is used to start a command on an unused
          virtual terminal.
@@ -92,6 +100,7 @@ config FEATURE_RESIZE_PRINT
 config SETCONSOLE
        bool "setconsole"
        default y
+       select PLATFORM_LINUX
        help
          This program redirects the system console to another device,
          like the current tty while logged in via telnet.
@@ -106,6 +115,7 @@ config FEATURE_SETCONSOLE_LONG_OPTIONS
 config SETFONT
        bool "setfont"
        default y
+       select PLATFORM_LINUX
        help
          Allows to load console screen map. Useful for i18n.
 
@@ -127,6 +137,7 @@ config DEFAULT_SETFONT_DIR
 config SETKEYCODES
        bool "setkeycodes"
        default y
+       select PLATFORM_LINUX
        help
          This program loads entries into the kernel's scancode-to-keycode
          map, allowing unusual keyboards to generate usable keycodes.
@@ -134,12 +145,14 @@ config SETKEYCODES
 config SETLOGCONS
        bool "setlogcons"
        default y
+       select PLATFORM_LINUX
        help
          This program redirects the output console of kernel messages.
 
 config SHOWKEY
        bool "showkey"
        default y
+       select PLATFORM_LINUX
        help
          Shows keys pressed.
 
index 17f6606..94de9ad 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
index 977c269..b9c974f 100644 (file)
@@ -4,8 +4,14 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define chvt_trivial_usage
+//usage:       "N"
+//usage:#define chvt_full_usage "\n\n"
+//usage:       "Change the foreground virtual terminal to /dev/ttyN"
+
 #include "libbb.h"
 
 int chvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
index cac7163..ac22b78 100644 (file)
@@ -4,8 +4,14 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define clear_trivial_usage
+//usage:       ""
+//usage:#define clear_full_usage "\n\n"
+//usage:       "Clear screen"
+
 #include "libbb.h"
 
 int clear_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
index 0974883..b131c0a 100644 (file)
@@ -5,11 +5,16 @@
  * Copyright (C) 2003 by Tito Ragusa <farmatito@tiscali.it>
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* no options, no getopt */
 
+//usage:#define deallocvt_trivial_usage
+//usage:       "[N]"
+//usage:#define deallocvt_full_usage "\n\n"
+//usage:       "Deallocate unused virtual terminal /dev/ttyN"
+
 #include "libbb.h"
 
 /* From <linux/vt.h> */
index a03b593..bf8d690 100644 (file)
@@ -4,11 +4,19 @@
  *
  * Copyright (C) Arne Bernin <arne@matrix.loopback.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  */
 /* no options, no getopt */
 
+//usage:#define dumpkmap_trivial_usage
+//usage:       "> keymap"
+//usage:#define dumpkmap_full_usage "\n\n"
+//usage:       "Print a binary keyboard translation table to stdout"
+//usage:
+//usage:#define dumpkmap_example_usage
+//usage:       "$ dumpkmap > keymap\n"
+
 #include "libbb.h"
 
 /* From <linux/kd.h> */
@@ -28,47 +36,56 @@ int dumpkmap_main(int argc UNUSED_PARAM, char **argv)
 {
        struct kbentry ke;
        int i, j, fd;
-       RESERVE_CONFIG_BUFFER(flags, MAX_NR_KEYMAPS);
+#define flags bb_common_bufsiz1
 
        /* When user accidentally runs "dumpkmap FILE"
         * instead of "dumpkmap >FILE", we'd dump binary stuff to tty.
-        * Let's prevent it: */
+        * Let's prevent it:
+        */
        if (argv[1])
                bb_show_usage();
 /*     bb_warn_ignoring_args(argv[1]);*/
 
        fd = get_console_fd_or_die();
 
+#if 0
        write(STDOUT_FILENO, "bkeymap", 7);
-
        /* Here we want to set everything to 0 except for indexes:
-        * [0-2] [4-6] [8-10] [12] */
-       memset(flags, 0x00, MAX_NR_KEYMAPS);
+        * [0-2] [4-6] [8-10] [12]
+        */
+       /*memset(flags, 0x00, MAX_NR_KEYMAPS); - already is */
        memset(flags, 0x01, 13);
        flags[3] = flags[7] = flags[11] = 0;
-
        /* dump flags */
        write(STDOUT_FILENO, flags, MAX_NR_KEYMAPS);
+#define flags7 flags
+#else
+       /* Same effect */
+       /*                     0 1 2 3 4 5 6 7 8 9 a b c=12 */
+       memcpy(flags, "bkeymap\1\1\1\0\1\1\1\0\1\1\1\0\1",
+       /* Can use sizeof, or sizeof-1. sizeof is even, using that */
+       /****/ sizeof("bkeymap\1\1\1\0\1\1\1\0\1\1\1\0\1")
+       );
+       write(STDOUT_FILENO, flags, 7 + MAX_NR_KEYMAPS);
+#define flags7 (flags + 7)
+#endif
 
-       for (i = 0; i < MAX_NR_KEYMAPS; i++) {
-               if (flags[i] == 1) {
+       for (i = 0; i < 13; i++) {
+               if (flags7[i]) {
                        for (j = 0; j < NR_KEYS; j++) {
                                ke.kb_index = j;
                                ke.kb_table = i;
                                if (!ioctl_or_perror(fd, KDGKBENT, &ke,
-                                               "ioctl failed with %s, %s, %p",
-                                               (char *)&ke.kb_index,
-                                               (char *)&ke.kb_table,
-                                               &ke.kb_value)
+                                               "ioctl(KDGKBENT{%d,%d}) failed",
+                                               j, i)
                                ) {
-                                       write(STDOUT_FILENO, (void*)&ke.kb_value, 2);
+                                       write(STDOUT_FILENO, &ke.kb_value, 2);
                                }
                        }
                }
        }
        if (ENABLE_FEATURE_CLEAN_UP) {
                close(fd);
-               RELEASE_CONFIG_BUFFER(flags);
        }
        return EXIT_SUCCESS;
 }
index 75fd98f..54355be 100644 (file)
@@ -4,9 +4,14 @@
  *
  * Copyright (C) 2010 by Grigory Batalov <bga@altlinux.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define fgconsole_trivial_usage
+//usage:       ""
+//usage:#define fgconsole_full_usage "\n\n"
+//usage:       "Get active console"
+
 #include "libbb.h"
 
 /* From <linux/vt.h> */
index e1d8523..1385367 100644 (file)
@@ -6,8 +6,19 @@
  *   written using Andries Brouwer <aeb@cwi.nl>'s kbd_mode from
  *   console-utils v0.2.3, licensed under GNU GPLv2
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define kbd_mode_trivial_usage
+//usage:       "[-a|k|s|u] [-C TTY]"
+//usage:#define kbd_mode_full_usage "\n\n"
+//usage:       "Report or set the keyboard mode\n"
+//usage:     "\n       -a      Default (ASCII)"
+//usage:     "\n       -k      Medium-raw (keyboard)"
+//usage:     "\n       -s      Raw (scancode)"
+//usage:     "\n       -u      Unicode (utf-8)"
+//usage:     "\n       -C TTY  Affect TTY instead of /dev/tty"
+
 #include "libbb.h"
 #include <linux/kd.h>
 
@@ -16,9 +27,9 @@ int kbd_mode_main(int argc UNUSED_PARAM, char **argv)
 {
        enum {
                SCANCODE  = (1 << 0),
-               ASCII     = (1 << 1),
+               ASCII     = (1 << 1),
                MEDIUMRAW = (1 << 2),
-               UNICODE   = (1 << 3),
+               UNICODE   = (1 << 3),
        };
        int fd;
        unsigned opt;
index e51142c..032506d 100644 (file)
@@ -7,8 +7,28 @@
  * Loads the console font, and possibly the corresponding screen map(s).
  * (Adapted for busybox by Matej Vela.)
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define loadfont_trivial_usage
+//usage:       "< font"
+//usage:#define loadfont_full_usage "\n\n"
+//usage:       "Load a console font from stdin"
+/* //usage:     "\n    -C TTY  Affect TTY instead of /dev/tty" */
+//usage:
+//usage:#define loadfont_example_usage
+//usage:       "$ loadfont < /etc/i18n/fontname\n"
+//usage:
+//usage:#define setfont_trivial_usage
+//usage:       "FONT [-m MAPFILE] [-C TTY]"
+//usage:#define setfont_full_usage "\n\n"
+//usage:       "Load a console font\n"
+//usage:     "\n       -m MAPFILE      Load console screen map"
+//usage:     "\n       -C TTY          Affect TTY instead of /dev/tty"
+//usage:
+//usage:#define setfont_example_usage
+//usage:       "$ setfont -m koi8-r /etc/i18n/fontname\n"
+
 #include "libbb.h"
 #include <sys/kd.h>
 
@@ -136,7 +156,7 @@ static void do_loadfont(int fd, unsigned char *inbuf, int height, int width, int
  * Example:
  * At the font position for a capital A-ring glyph, we
  * may have:
- *     00C5,212B,FFFE,0041,030A,FFFF
+ *   00C5,212B,FFFE,0041,030A,FFFF
  * Some font positions may be described by sequences only,
  * namely when there is no precomposed Unicode value for the glyph.
  */
@@ -159,7 +179,7 @@ static void do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize,
        int glyph;
        uint16_t unicode;
 
-       maxct = tailsz; /* more than enough */
+       maxct = tailsz; /* more than enough */
        up = xmalloc(maxct * sizeof(*up));
 
        for (glyph = 0; glyph < fontsize; glyph++) {
@@ -209,7 +229,7 @@ static void do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize,
        }
 
        /* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP
-          this printf did not work on many kernels */
+        * this printf did not work on many kernels */
 
        advice.advised_hashsize = 0;
        advice.advised_hashstep = 0;
@@ -255,10 +275,10 @@ static void do_load(int fd, unsigned char *buffer, size_t len)
        } else
 #endif
 #if ENABLE_FEATURE_LOADFONT_RAW
-       if (len == 9780) {      /* file with three code pages? */
+       if (len == 9780) {  /* file with three code pages? */
                charsize = height = 16;
                font += 40;
-       } else if ((len & 0377) == 0) {         /* bare font */
+       } else if ((len & 0377) == 0) {  /* bare font */
                charsize = height = len / 256;
        } else
 #endif
index 8f1a915..66ec3b0 100644 (file)
@@ -4,8 +4,18 @@
  *
  * Copyright (C) 1998 Enrique Zanardi <ezanardi@ull.es>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define loadkmap_trivial_usage
+//usage:       "< keymap"
+//usage:#define loadkmap_full_usage "\n\n"
+//usage:       "Load a binary keyboard translation table from stdin\n"
+/* //usage:     "\n    -C TTY  Affect TTY instead of /dev/tty" */
+//usage:
+//usage:#define loadkmap_example_usage
+//usage:       "$ loadkmap < /etc/i18n/lang-keymap\n"
+
 #include "libbb.h"
 
 #define BINARY_KEYMAP_MAGIC "bkeymap"
@@ -38,6 +48,7 @@ int loadkmap_main(int argc UNUSED_PARAM, char **argv)
        if (argv[1])
                bb_show_usage();
 /* bb_warn_ignoring_args(argv[1]); */
+
        fd = get_console_fd_or_die();
 /* or maybe:
        opt = getopt32(argv, "C:", &tty_name);
@@ -51,14 +62,24 @@ int loadkmap_main(int argc UNUSED_PARAM, char **argv)
        xread(STDIN_FILENO, flags, MAX_NR_KEYMAPS);
 
        for (i = 0; i < MAX_NR_KEYMAPS; i++) {
-               if (flags[i] == 1) {
-                       xread(STDIN_FILENO, ibuff, NR_KEYS * sizeof(uint16_t));
-                       for (j = 0; j < NR_KEYS; j++) {
-                               ke.kb_index = j;
-                               ke.kb_table = i;
-                               ke.kb_value = ibuff[j];
-                               ioctl(fd, KDSKBENT, &ke);
-                       }
+               if (flags[i] != 1)
+                       continue;
+               xread(STDIN_FILENO, ibuff, NR_KEYS * sizeof(uint16_t));
+               for (j = 0; j < NR_KEYS; j++) {
+                       ke.kb_index = j;
+                       ke.kb_table = i;
+                       ke.kb_value = ibuff[j];
+                       /*
+                        * Note: table[idx:0] can contain special value
+                        * K_ALLOCATED (marks allocated tables in kernel).
+                        * dumpkmap saves the value as-is; but attempts
+                        * to load it here fail, since it isn't a valid
+                        * key value: it is K(KT_SPEC,126) == 2<<8 + 126,
+                        * whereas last valid KT_SPEC is
+                        * K_BARENUMLOCK == K(KT_SPEC,19).
+                        * So far we just ignore these errors:
+                        */
+                       ioctl(fd, KDSKBENT, &ke);
                }
        }
 
index e3ea71b..e523566 100644 (file)
@@ -5,9 +5,21 @@
  *  busyboxed by Quy Tonthat <quy@signal3.com>
  *  hacked by Tito <farmatito@tiscali.it>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define openvt_trivial_usage
+//usage:       "[-c N] [-sw] [PROG ARGS]"
+//usage:#define openvt_full_usage "\n\n"
+//usage:       "Start PROG on a new virtual terminal\n"
+//usage:     "\n       -c N    Use specified VT"
+//usage:     "\n       -s      Switch to the VT"
+/* //usage:     "\n    -l      Run PROG as login shell (by prepending '-')" */
+//usage:     "\n       -w      Wait for PROG to exit"
+//usage:
+//usage:#define openvt_example_usage
+//usage:       "openvt 2 /bin/ash\n"
+
 #include <linux/vt.h>
 #include "libbb.h"
 
@@ -144,9 +156,7 @@ int openvt_main(int argc UNUSED_PARAM, char **argv)
 
        if (!argv[0]) {
                argv--;
-               argv[0] = getenv("SHELL");
-               if (!argv[0])
-                       argv[0] = (char *) DEFAULT_SHELL;
+               argv[0] = (char *) get_shell_name();
                /*argv[1] = NULL; - already is */
        }
 
index f0ea5cb..65940bd 100644 (file)
@@ -5,14 +5,21 @@
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  * Written by Erik Andersen and Kent Robotti <robotti@metconnect.com>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
-#include "libbb.h"
-
 /* BTW, which "standard" package has this utility? It doesn't seem
  * to be ncurses, coreutils, console-tools... then what? */
 
+//usage:#define reset_trivial_usage
+//usage:       ""
+//usage:#define reset_full_usage "\n\n"
+//usage:       "Reset the screen"
+
+#include "libbb.h"
+
+#define ESC "\033"
+
 #if ENABLE_STTY
 int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 #endif
@@ -26,15 +33,15 @@ int reset_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 
        /* no options, no getopt */
 
-       if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
+       if (/*isatty(STDIN_FILENO) &&*/ isatty(STDOUT_FILENO)) {
                /* See 'man 4 console_codes' for details:
                 * "ESC c"        -- Reset
-                * "ESC ( K"      -- Select user mapping
-                * "ESC [ J"      -- Erase to the end of screen
+                * "ESC ( B"      -- Select G0 Character Set (B = US)
                 * "ESC [ 0 m"    -- Reset all display attributes
+                * "ESC [ J"      -- Erase to the end of screen
                 * "ESC [ ? 25 h" -- Make cursor visible
                 */
-               printf("\033c\033(K\033[J\033[0m\033[?25h");
+               printf(ESC"c" ESC"(B" ESC"[0m" ESC"[J" ESC"[?25h");
                /* http://bugs.busybox.net/view.php?id=1414:
                 * people want it to reset echo etc: */
 #if ENABLE_STTY
index 828b5bb..4b0d63a 100644 (file)
@@ -4,9 +4,15 @@
  *
  * Copyright 2006 Bernhard Reutner-Fischer
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 /* no options, no getopt */
+
+//usage:#define resize_trivial_usage
+//usage:       ""
+//usage:#define resize_full_usage "\n\n"
+//usage:       "Resize the screen"
+
 #include "libbb.h"
 
 #define ESC "\033"
@@ -17,7 +23,7 @@ static void
 onintr(int sig UNUSED_PARAM)
 {
        tcsetattr(STDERR_FILENO, TCSANOW, old_termios_p);
-       exit(EXIT_FAILURE);
+       _exit(EXIT_FAILURE);
 }
 
 int resize_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -53,6 +59,7 @@ int resize_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
         */
        fprintf(stderr, ESC"7" ESC"[r" ESC"[999;999H" ESC"[6n");
        alarm(3); /* Just in case terminal won't answer */
+//BUG: death by signal won't restore termios
        scanf(ESC"[%hu;%huR", &w.ws_row, &w.ws_col);
        fprintf(stderr, ESC"8");
 
index 8ad9948..c0051dc 100644 (file)
@@ -5,9 +5,15 @@
  *  Copyright (C) 2004,2005  Enrik Berkhan <Enrik.Berkhan@inka.de>
  *  Copyright (C) 2008 Bernhard Reutner-Fischer
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define setconsole_trivial_usage
+//usage:       "[-r" IF_FEATURE_SETCONSOLE_LONG_OPTIONS("|--reset") "] [DEVICE]"
+//usage:#define setconsole_full_usage "\n\n"
+//usage:       "Redirect system console output to DEVICE (default: /dev/tty)\n"
+//usage:     "\n       -r      Reset output to /dev/console"
+
 #include "libbb.h"
 
 int setconsole_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -34,6 +40,6 @@ int setconsole_main(int argc UNUSED_PARAM, char **argv)
                        device = DEV_CONSOLE;
        }
 
-       xioctl(xopen(device, O_RDONLY), TIOCCONS, NULL);
+       xioctl(xopen(device, O_WRONLY), TIOCCONS, NULL);
        return EXIT_SUCCESS;
 }
index b6a9a32..a6a7c23 100644 (file)
@@ -6,8 +6,20 @@
  *
  * Adjusted for BusyBox by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define setkeycodes_trivial_usage
+//usage:       "SCANCODE KEYCODE..."
+//usage:#define setkeycodes_full_usage "\n\n"
+//usage:       "Set entries into the kernel's scancode-to-keycode map,\n"
+//usage:       "allowing unusual keyboards to generate usable keycodes.\n\n"
+//usage:       "SCANCODE may be either xx or e0xx (hexadecimal),\n"
+//usage:       "and KEYCODE is given in decimal."
+//usage:
+//usage:#define setkeycodes_example_usage
+//usage:       "$ setkeycodes e030 127\n"
+
 #include "libbb.h"
 
 /* From <linux/kd.h> */
index dd44591..c76a5a4 100644 (file)
@@ -6,9 +6,14 @@
  *
  * Based on setlogcons (kbd-1.12) by Andries E. Brouwer
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define setlogcons_trivial_usage
+//usage:       "N"
+//usage:#define setlogcons_full_usage "\n\n"
+//usage:       "Redirect the kernel output to console N (0 for current)"
+
 #include "libbb.h"
 
 int setlogcons_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -17,9 +22,10 @@ int setlogcons_main(int argc UNUSED_PARAM, char **argv)
        struct {
                char fn;
                char subarg;
-       } arg = { 11, /* redirect kernel messages */
-                         0   /* to specified console (current as default) */
-                       };
+       } arg = {
+               11, /* redirect kernel messages */
+               0   /* to specified console (current as default) */
+       };
 
        if (argv[1])
                arg.subarg = xatou_range(argv[1], 0, 63);
index 681114d..69b785e 100644 (file)
  *
  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#define showkey_trivial_usage
+//usage:       "[-a | -k | -s]"
+//usage:#define showkey_full_usage "\n\n"
+//usage:       "Show keys pressed\n"
+//usage:     "\n       -a      Display decimal/octal/hex values of the keys"
+//usage:     "\n       -k      Display interpreted keycodes (default)"
+//usage:     "\n       -s      Display raw scan-codes"
+
 #include "libbb.h"
 #include <linux/kd.h>
 
-// set raw tty mode
-// also used by microcom
-// libbb candidates?
-static void xget1(int fd, struct termios *t, struct termios *oldt)
-{
-       tcgetattr(fd, oldt);
-       *t = *oldt;
-       cfmakeraw(t);
-}
-
-static int xset1(int fd, struct termios *tio, const char *device)
-{
-       int ret = tcsetattr(fd, TCSAFLUSH, tio);
-
-       if (ret) {
-               bb_perror_msg("can't tcsetattr for %s", device);
-       }
-       return ret;
-}
 
-/*
- * GLOBALS
- */
 struct globals {
        int kbmode;
        struct termios tio, tio0;
 };
 #define G (*ptr_to_globals)
-#define kbmode (G.kbmode)
-#define tio    (G.tio)
-#define tio0   (G.tio0)
+#define kbmode (G.kbmode)
+#define tio    (G.tio)
+#define tio0   (G.tio0)
 #define INIT_G() do { \
        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
 } while (0)
 
 
-static void signal_handler(int signo)
+// set raw tty mode
+// also used by microcom
+// libbb candidates?
+static void xget1(struct termios *t, struct termios *oldt)
+{
+       tcgetattr(STDIN_FILENO, oldt);
+       *t = *oldt;
+       cfmakeraw(t);
+}
+
+static void xset1(struct termios *t)
 {
-       // restore keyboard and console settings
-       xset1(STDIN_FILENO, &tio0, "stdin");
-       xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)kbmode);
-       // alarmed? -> exit 0
-       exit(SIGALRM == signo);
+       int ret = tcsetattr(STDIN_FILENO, TCSAFLUSH, t);
+       if (ret) {
+               bb_perror_msg("can't tcsetattr for stdin");
+       }
 }
 
 int showkey_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int showkey_main(int argc UNUSED_PARAM, char **argv)
 {
        enum {
-               OPT_a = (1<<0), // display the decimal/octal/hex values of the keys
-               OPT_k = (1<<1), // display only the interpreted keycodes (default)
-               OPT_s = (1<<2), // display only the raw scan-codes
+               OPT_a = (1<<0), // display the decimal/octal/hex values of the keys
+               OPT_k = (1<<1), // display only the interpreted keycodes (default)
+               OPT_s = (1<<2), // display only the raw scan-codes
        };
 
+       INIT_G();
+
        // FIXME: aks are all mutually exclusive
        getopt32(argv, "aks");
 
-       INIT_G();
-
-       // get keyboard settings
-       xioctl(STDIN_FILENO, KDGKBMODE, &kbmode);
-       printf("kb mode was %s\n\nPress any keys. Program terminates %s\n\n",
-               kbmode == K_RAW ? "RAW" :
-                       (kbmode == K_XLATE ? "XLATE" :
-                               (kbmode == K_MEDIUMRAW ? "MEDIUMRAW" :
-                                       (kbmode == K_UNICODE ? "UNICODE" : "?UNKNOWN?")))
-               , (option_mask32 & OPT_a) ? "when CTRL+D pressed" : "10s after last keypress"
-       );
        // prepare for raw mode
-       xget1(STDIN_FILENO, &tio, &tio0);
+       xget1(&tio, &tio0);
        // put stdin in raw mode
-       xset1(STDIN_FILENO, &tio, "stdin");
+       xset1(&tio);
+
+#define press_keys "Press any keys, program terminates %s:\r\n\n"
 
        if (option_mask32 & OPT_a) {
-               char c;
                // just read stdin char by char
-               while (1 == safe_read(STDIN_FILENO, &c, 1)) {
-                       printf("%3d 0%03o 0x%02x\r\n", c, c, c);
+               unsigned char c;
+
+               printf(press_keys, "on EOF (ctrl-D)");
+
+               // read and show byte values
+               while (1 == read(STDIN_FILENO, &c, 1)) {
+                       printf("%3u 0%03o 0x%02x\r\n", c, c, c);
                        if (04 /*CTRL-D*/ == c)
                                break;
                }
+
        } else {
-               // we should exit on any signal
-               bb_signals(BB_FATAL_SIGS, signal_handler);
+               // we assume a PC keyboard
+               xioctl(STDIN_FILENO, KDGKBMODE, &kbmode);
+               printf("Keyboard mode was %s.\r\n\n",
+                       kbmode == K_RAW ? "RAW" :
+                               (kbmode == K_XLATE ? "XLATE" :
+                                       (kbmode == K_MEDIUMRAW ? "MEDIUMRAW" :
+                                               (kbmode == K_UNICODE ? "UNICODE" : "UNKNOWN")))
+               );
+
                // set raw keyboard mode
                xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)((option_mask32 & OPT_k) ? K_MEDIUMRAW : K_RAW));
 
+               // we should exit on any signal; signals should interrupt read
+               bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo);
+
+               // inform user that program ends after time of inactivity
+               printf(press_keys, "10s after last keypress");
+
                // read and show scancodes
-               while (1) {
+               while (!bb_got_signal) {
                        char buf[18];
                        int i, n;
+
                        // setup 10s watchdog
                        alarm(10);
+
                        // read scancodes
                        n = read(STDIN_FILENO, buf, sizeof(buf));
                        i = 0;
                        while (i < n) {
-                               char c = buf[i];
-                               // show raw scancodes ordered? ->
                                if (option_mask32 & OPT_s) {
+                                       // show raw scancodes
                                        printf("0x%02x ", buf[i++]);
-                               // show interpreted scancodes (default) ? ->
                                } else {
+                                       // show interpreted scancodes (default)
+                                       char c = buf[i];
                                        int kc;
-                                       if (i+2 < n && (c & 0x7f) == 0
-                                               && (buf[i+1] & 0x80) != 0
-                                               && (buf[i+2] & 0x80) != 0) {
+                                       if (i+2 < n
+                                        && (c & 0x7f) == 0
+                                        && (buf[i+1] & 0x80) != 0
+                                        && (buf[i+2] & 0x80) != 0
+                                       ) {
                                                kc = ((buf[i+1] & 0x7f) << 7) | (buf[i+2] & 0x7f);
                                                i += 3;
                                        } else {
                                                kc = (c & 0x7f);
                                                i++;
                                        }
-                                       printf("keycode %3d %s", kc, (c & 0x80) ? "release" : "press");
+                                       printf("keycode %3u %s", kc, (c & 0x80) ? "release" : "press");
                                }
                        }
                        puts("\r");
                }
+
+               // restore keyboard mode
+               xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)kbmode);
        }
 
-       // cleanup
-       signal_handler(SIGALRM);
+       // restore console settings
+       xset1(&tio0);
 
-       // should never be here!
        return EXIT_SUCCESS;
 }
index d4c9e05..0c44c4b 100644 (file)
@@ -269,19 +269,6 @@ config FEATURE_FANCY_HEAD
        help
          This enables the head options (-c, -q, and -v).
 
-config HOSTID
-       bool "hostid"
-       default y
-       help
-         hostid prints the numeric identifier (in hexadecimal) for
-         the current host.
-
-config ID
-       bool "id"
-       default y
-       help
-         id displays the current user and group ID names.
-
 config INSTALL
        bool "install"
        default y
@@ -295,11 +282,11 @@ config FEATURE_INSTALL_LONG_OPTIONS
        help
          Support long options for the install applet.
 
-config LENGTH
-       bool "length"
-       default y
-       help
-         length is used to print out the length of a specified string.
+####config LENGTH
+####   bool "length"
+####   default y
+####   help
+####     length is used to print out the length of a specified string.
 
 config LN
        bool "ln"
@@ -527,6 +514,12 @@ config SHA512SUM
        help
          Compute and check SHA512 message digest
 
+config SHA3SUM
+       bool "sha3sum"
+       default y
+       help
+         Compute and check SHA3 (512-bit) message digest
+
 config SLEEP
        bool "sleep"
        default y
@@ -591,6 +584,7 @@ config FEATURE_SPLIT_FANCY
 config STAT
        bool "stat"
        default y
+       select PLATFORM_LINUX # statfs()
        help
          display file or filesystem status.
 
@@ -660,13 +654,6 @@ config FEATURE_TEE_USE_BLOCK_IO
        help
          Enable this option for a faster tee, at expense of size.
 
-config TOUCH
-       bool "touch"
-       default y
-       help
-         touch is used to create or change the access and/or
-         modification timestamp of specified files.
-
 config TRUE
        bool "true"
        default y
@@ -737,13 +724,6 @@ config FEATURE_WC_LARGE
        help
          Use "unsigned long long" in wc for counter variables.
 
-config WHO
-       bool "who"
-       default y
-       depends on FEATURE_UTMP
-       help
-         who is used to show who is logged on.
-
 config WHOAMI
        bool "whoami"
        default y
@@ -792,13 +772,13 @@ config FEATURE_HUMAN_READABLE
        help
          Allow df, du, and ls to have human readable output.
 
-comment "Common options for md5sum, sha1sum, sha256sum, sha512sum"
-       depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM
+comment "Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum"
+       depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM
 
 config FEATURE_MD5_SHA1_SUM_CHECK
        bool "Enable -c, -s and -w options"
        default y
-       depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM
+       depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM
        help
          Enabling the -c options allows files to be checked
          against pre-calculated hash values.
index 1c846a7..ec4ef7d 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 libs-y                 += libcoreutils/
 
@@ -35,11 +35,8 @@ lib-$(CONFIG_EXPAND)    += expand.o
 lib-$(CONFIG_FALSE)     += false.o
 lib-$(CONFIG_FOLD)      += fold.o
 lib-$(CONFIG_FSYNC)     += fsync.o
-lib-$(CONFIG_HEAD)      += head.o
-lib-$(CONFIG_HOSTID)    += hostid.o
-lib-$(CONFIG_ID)        += id.o
 lib-$(CONFIG_INSTALL)   += install.o
-lib-$(CONFIG_LENGTH)    += length.o
+#lib-$(CONFIG_LENGTH)    += length.o
 lib-$(CONFIG_LN)        += ln.o
 lib-$(CONFIG_LOGNAME)   += logname.o
 lib-$(CONFIG_LS)        += ls.o
@@ -64,6 +61,7 @@ lib-$(CONFIG_SEQ)       += seq.o
 lib-$(CONFIG_SHA1SUM)   += md5_sha1_sum.o
 lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o
 lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o
+lib-$(CONFIG_SHA3SUM)   += md5_sha1_sum.o
 lib-$(CONFIG_SLEEP)     += sleep.o
 lib-$(CONFIG_SPLIT)     += split.o
 lib-$(CONFIG_SORT)      += sort.o
@@ -72,9 +70,7 @@ lib-$(CONFIG_STTY)      += stty.o
 lib-$(CONFIG_SUM)       += sum.o
 lib-$(CONFIG_SYNC)      += sync.o
 lib-$(CONFIG_TAC)       += tac.o
-lib-$(CONFIG_TAIL)      += tail.o
 lib-$(CONFIG_TEE)       += tee.o
-lib-$(CONFIG_TOUCH)     += touch.o
 lib-$(CONFIG_TRUE)      += true.o
 lib-$(CONFIG_TTY)       += tty.o
 lib-$(CONFIG_UNAME)     += uname.o
@@ -84,6 +80,5 @@ lib-$(CONFIG_USLEEP)    += usleep.o
 lib-$(CONFIG_UUDECODE)  += uudecode.o
 lib-$(CONFIG_UUENCODE)  += uuencode.o
 lib-$(CONFIG_WC)        += wc.o
-lib-$(CONFIG_WHO)       += who.o
 lib-$(CONFIG_WHOAMI)    += whoami.o
 lib-$(CONFIG_YES)       += yes.o
index b79d561..1f7a137 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
 //config:        leaving just the filename itself. Enable this option if you wish
 //config:        to enable the 'basename' utility.
 
+//usage:#define basename_trivial_usage
+//usage:       "FILE [SUFFIX]"
+//usage:#define basename_full_usage "\n\n"
+//usage:       "Strip directory path and .SUFFIX from FILE\n"
+//usage:
+//usage:#define basename_example_usage
+//usage:       "$ basename /usr/local/bin/foo\n"
+//usage:       "foo\n"
+//usage:       "$ basename /usr/local/bin/\n"
+//usage:       "bin\n"
+//usage:       "$ basename /foo/bar.txt .txt\n"
+//usage:       "bar"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
@@ -38,6 +51,11 @@ int basename_main(int argc, char **argv)
        size_t m, n;
        char *s;
 
+       if (argv[1] && strcmp(argv[1], "--") == 0) {
+               argv++;
+               argc--;
+       }
+
        if ((unsigned)(argc-2) >= 2) {
                bb_show_usage();
        }
index c98229c..12c46b1 100644 (file)
@@ -4,7 +4,7 @@
  *
  * See original copyright at the end of this file
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant with -j and -y extensions (from util-linux). */
  *
  * Major size reduction... over 50% (>1.5k) on i386.
  */
+
+//usage:#define cal_trivial_usage
+//usage:       "[-jy] [[MONTH] YEAR]"
+//usage:#define cal_full_usage "\n\n"
+//usage:       "Display a calendar\n"
+//usage:     "\n       -j      Use julian dates"
+//usage:     "\n       -y      Display the entire year"
+
 #include "libbb.h"
 #include "unicode.h"
 
@@ -35,7 +43,7 @@ static const unsigned char days_in_month[] ALIGN1 = {
 };
 
 static const unsigned char sep1752[] ALIGN1 = {
-                1,     2,      14,     15,     16,
+               1,      2,      14,     15,     16,
        17,     18,     19,     20,     21,     22,     23,
        24,     25,     26,     27,     28,     29,     30
 };
@@ -138,7 +146,7 @@ int cal_main(int argc UNUSED_PARAM, char **argv)
                        if (julian)
                                *hp++ = ' ';
                        {
-                               char *two_wchars = unicode_conv_to_printable_fixedwidth(NULL, buf, 2);
+                               char *two_wchars = unicode_conv_to_printable_fixedwidth(/*NULL,*/ buf, 2);
                                strcpy(hp, two_wchars);
                                free(two_wchars);
                        }
@@ -157,10 +165,10 @@ int cal_main(int argc UNUSED_PARAM, char **argv)
                char lineout[30];
 
                day_array(month, year, dp);
-               len = sprintf(lineout, "%s %d", month_names[month - 1], year);
+               len = sprintf(lineout, "%s %u", month_names[month - 1], year);
                printf("%*s%s\n%s\n",
-                          ((7*julian + WEEK_LEN) - len) / 2, "",
-                          lineout, day_headings);
+                               ((7*julian + WEEK_LEN) - len) / 2, "",
+                               lineout, day_headings);
                for (row = 0; row < 6; row++) {
                        build_row(lineout, dp)[0] = '\0';
                        dp += 7;
@@ -173,10 +181,11 @@ int cal_main(int argc UNUSED_PARAM, char **argv)
 
                sprintf(lineout, "%u", year);
                center(lineout,
-                          (WEEK_LEN * 3 + HEAD_SEP * 2)
-                          + julian * (J_WEEK_LEN * 2 + HEAD_SEP
-                                                  - (WEEK_LEN * 3 + HEAD_SEP * 2)),
-                          0);
+                               (WEEK_LEN * 3 + HEAD_SEP * 2)
+                               + julian * (J_WEEK_LEN * 2 + HEAD_SEP
+                                               - (WEEK_LEN * 3 + HEAD_SEP * 2)),
+                               0
+               );
                puts("\n");             /* two \n's */
                for (i = 0; i < 12; i++) {
                        day_array(i + 1, year, days[i]);
index ab68258..00c38d4 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2, see file License in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
 //config:        cat is used to concatenate files and print them to the standard
 //config:        output. Enable this option if you wish to enable the 'cat' utility.
 
+//usage:#define cat_trivial_usage
+//usage:       "[FILE]..."
+//usage:#define cat_full_usage "\n\n"
+//usage:       "Concatenate FILEs and print them to stdout"
+//usage:
+//usage:#define cat_example_usage
+//usage:       "$ cat /proc/uptime\n"
+//usage:       "110716.72 17.67"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
index ff3139c..e3499c5 100644 (file)
@@ -4,12 +4,20 @@
  *
  * Copyright (C) 2006 Rob Landley <rob@landley.net>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* See "Cat -v considered harmful" at
  * http://cm.bell-labs.com/cm/cs/doc/84/kp.ps.gz */
 
+//usage:#define catv_trivial_usage
+//usage:       "[-etv] [FILE]..."
+//usage:#define catv_full_usage "\n\n"
+//usage:       "Display nonprinting characters as ^x or M-x\n"
+//usage:     "\n       -e      End each line with $"
+//usage:     "\n       -t      Show tabs as ^I"
+//usage:     "\n       -v      Don't use ^x or M-x escapes"
+
 #include "libbb.h"
 
 int catv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -17,14 +25,23 @@ int catv_main(int argc UNUSED_PARAM, char **argv)
 {
        int retval = EXIT_SUCCESS;
        int fd;
-       unsigned flags;
-
-       flags = getopt32(argv, "etv");
+       unsigned opts;
 #define CATV_OPT_e (1<<0)
 #define CATV_OPT_t (1<<1)
 #define CATV_OPT_v (1<<2)
-       flags ^= CATV_OPT_v;
+       typedef char BUG_const_mismatch[
+               CATV_OPT_e == VISIBLE_ENDLINE && CATV_OPT_t == VISIBLE_SHOW_TABS
+               ? 1 : -1
+       ];
+
+       opts = getopt32(argv, "etv");
        argv += optind;
+#if 0 /* These consts match, we can just pass "opts" to visible() */
+       if (opts & CATV_OPT_e)
+               flags |= VISIBLE_ENDLINE;
+       if (opts & CATV_OPT_t)
+               flags |= VISIBLE_SHOW_TABS;
+#endif
 
        /* Read from stdin if there's nothing else to do. */
        if (!argv[0])
@@ -42,29 +59,17 @@ int catv_main(int argc UNUSED_PARAM, char **argv)
                        res = read(fd, read_buf, COMMON_BUFSIZE);
                        if (res < 0)
                                retval = EXIT_FAILURE;
-                       if (res < 1)
+                       if (res <= 0)
                                break;
                        for (i = 0; i < res; i++) {
                                unsigned char c = read_buf[i];
-
-                               if (c > 126 && (flags & CATV_OPT_v)) {
-                                       if (c == 127) {
-                                               printf("^?");
-                                               continue;
-                                       }
-                                       printf("M-");
-                                       c -= 128;
-                               }
-                               if (c < 32) {
-                                       if (c == 10) {
-                                               if (flags & CATV_OPT_e)
-                                                       bb_putchar('$');
-                                       } else if (flags & (c==9 ? CATV_OPT_t : CATV_OPT_v)) {
-                                               printf("^%c", c+'@');
-                                               continue;
-                                       }
+                               if (opts & CATV_OPT_v) {
+                                       putchar(c);
+                               } else {
+                                       char buf[sizeof("M-^c")];
+                                       visible(c, buf, opts);
+                                       fputs(buf, stdout);
                                }
-                               bb_putchar(c);
                        }
                }
                if (ENABLE_FEATURE_CLEAN_UP && fd)
index 7f39048..7076db6 100644 (file)
@@ -4,13 +4,35 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 defects - none? */
 /* BB_AUDIT GNU defects - unsupported long options. */
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/chgrp.html */
 
+//usage:#define chgrp_trivial_usage
+//usage:       "[-RhLHP"IF_DESKTOP("cvf")"]... GROUP FILE..."
+//usage:#define chgrp_full_usage "\n\n"
+//usage:       "Change the group membership of each FILE to GROUP\n"
+//usage:     "\n       -R      Recurse"
+//usage:     "\n       -h      Affect symlinks instead of symlink targets"
+//usage:     "\n       -L      Traverse all symlinks to directories"
+//usage:     "\n       -H      Traverse symlinks on command line only"
+//usage:     "\n       -P      Don't traverse symlinks (default)"
+//usage:       IF_DESKTOP(
+//usage:     "\n       -c      List changed files"
+//usage:     "\n       -v      Verbose"
+//usage:     "\n       -f      Hide errors"
+//usage:       )
+//usage:
+//usage:#define chgrp_example_usage
+//usage:       "$ ls -l /tmp/foo\n"
+//usage:       "-r--r--r--    1 andersen andersen        0 Apr 12 18:25 /tmp/foo\n"
+//usage:       "$ chgrp root /tmp/foo\n"
+//usage:       "$ ls -l /tmp/foo\n"
+//usage:       "-r--r--r--    1 andersen root            0 Apr 12 18:25 /tmp/foo\n"
+
 #include "libbb.h"
 
 /* This is a NOEXEC applet. Be very careful! */
index c04201e..5ee45b9 100644 (file)
@@ -7,13 +7,35 @@
  * Reworked by (C) 2002 Vladimir Oleynik <dzo@simtreas.ru>
  *  to correctly parse '-rwxgoa'
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
 /* BB_AUDIT GNU defects - unsupported long options. */
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
 
+//usage:#define chmod_trivial_usage
+//usage:       "[-R"IF_DESKTOP("cvf")"] MODE[,MODE]... FILE..."
+//usage:#define chmod_full_usage "\n\n"
+//usage:       "Each MODE is one or more of the letters ugoa, one of the\n"
+//usage:       "symbols +-= and one or more of the letters rwxst\n"
+//usage:     "\n       -R      Recurse"
+//usage:       IF_DESKTOP(
+//usage:     "\n       -c      List changed files"
+//usage:     "\n       -v      List all files"
+//usage:     "\n       -f      Hide errors"
+//usage:       )
+//usage:
+//usage:#define chmod_example_usage
+//usage:       "$ ls -l /tmp/foo\n"
+//usage:       "-rw-rw-r--    1 root     root            0 Apr 12 18:25 /tmp/foo\n"
+//usage:       "$ chmod u+x /tmp/foo\n"
+//usage:       "$ ls -l /tmp/foo\n"
+//usage:       "-rwxrw-r--    1 root     root            0 Apr 12 18:25 /tmp/foo*\n"
+//usage:       "$ chmod 444 /tmp/foo\n"
+//usage:       "$ ls -l /tmp/foo\n"
+//usage:       "-r--r--r--    1 root     root            0 Apr 12 18:25 /tmp/foo\n"
+
 #include "libbb.h"
 
 /* This is a NOEXEC applet. Be very careful! */
index 717e4b1..1a91276 100644 (file)
@@ -4,12 +4,37 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 defects - none? */
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */
 
+//usage:#define chown_trivial_usage
+//usage:       "[-RhLHP"IF_DESKTOP("cvf")"]... OWNER[<.|:>[GROUP]] FILE..."
+//usage:#define chown_full_usage "\n\n"
+//usage:       "Change the owner and/or group of each FILE to OWNER and/or GROUP\n"
+//usage:     "\n       -R      Recurse"
+//usage:     "\n       -h      Affect symlinks instead of symlink targets"
+//usage:     "\n       -L      Traverse all symlinks to directories"
+//usage:     "\n       -H      Traverse symlinks on command line only"
+//usage:     "\n       -P      Don't traverse symlinks (default)"
+//usage:       IF_DESKTOP(
+//usage:     "\n       -c      List changed files"
+//usage:     "\n       -v      List all files"
+//usage:     "\n       -f      Hide errors"
+//usage:       )
+//usage:
+//usage:#define chown_example_usage
+//usage:       "$ ls -l /tmp/foo\n"
+//usage:       "-r--r--r--    1 andersen andersen        0 Apr 12 18:25 /tmp/foo\n"
+//usage:       "$ chown root /tmp/foo\n"
+//usage:       "$ ls -l /tmp/foo\n"
+//usage:       "-r--r--r--    1 root     andersen        0 Apr 12 18:25 /tmp/foo\n"
+//usage:       "$ chown root.root /tmp/foo\n"
+//usage:       "ls -l /tmp/foo\n"
+//usage:       "-r--r--r--    1 root     root            0 Apr 12 18:25 /tmp/foo\n"
+
 #include "libbb.h"
 
 /* This is a NOEXEC applet. Be very careful! */
@@ -101,8 +126,8 @@ int chown_main(int argc UNUSED_PARAM, char **argv)
        /* This matches coreutils behavior (almost - see below) */
        param.chown_func = chown;
        if (OPT_NODEREF
-           /* || (OPT_RECURSE && !OPT_TRAVERSE_TOP): */
-           IF_DESKTOP( || (opt & (BIT_RECURSE|BIT_TRAVERSE_TOP)) == BIT_RECURSE)
+       /* || (OPT_RECURSE && !OPT_TRAVERSE_TOP): */
+       IF_DESKTOP( || (opt & (BIT_RECURSE|BIT_TRAVERSE_TOP)) == BIT_RECURSE)
        ) {
                param.chown_func = lchown;
        }
index 046c2fa..633e66b 100644 (file)
@@ -4,11 +4,24 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
 
+//usage:#define chroot_trivial_usage
+//usage:       "NEWROOT [PROG ARGS]"
+//usage:#define chroot_full_usage "\n\n"
+//usage:       "Run PROG with root directory set to NEWROOT"
+//usage:
+//usage:#define chroot_example_usage
+//usage:       "$ ls -l /bin/ls\n"
+//usage:       "lrwxrwxrwx    1 root     root          12 Apr 13 00:46 /bin/ls -> /BusyBox\n"
+//usage:       "# mount /dev/hdc1 /mnt -t minix\n"
+//usage:       "# chroot /mnt\n"
+//usage:       "# ls -l /bin/ls\n"
+//usage:       "-rwxr-xr-x    1 root     root        40816 Feb  5 07:45 /bin/ls*\n"
+
 #include "libbb.h"
 
 int chroot_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -18,16 +31,13 @@ int chroot_main(int argc UNUSED_PARAM, char **argv)
        if (!*argv)
                bb_show_usage();
        xchroot(*argv);
-       xchdir("/");
 
        ++argv;
        if (!*argv) { /* no 2nd param (PROG), use shell */
                argv -= 2;
-               argv[0] = getenv("SHELL");
-               if (!argv[0]) {
-                       argv[0] = (char *) DEFAULT_SHELL;
-               }
-               argv[1] = (char *) "-i";
+               argv[0] = (char *) get_shell_name();
+               argv[1] = (char *) "-i"; /* GNU coreutils 8.4 compat */
+               /*argv[2] = NULL; - already is */
        }
 
        BB_EXECVP_or_die(argv);
index 8e65b1c..ac0b0c3 100644 (file)
@@ -4,10 +4,18 @@
  *
  * Copyright (C) 2006 by Rob Sullivan, with ideas from code by Walter Harms
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define cksum_trivial_usage
+//usage:       "FILES..."
+//usage:#define cksum_full_usage "\n\n"
+//usage:       "Calculate the CRC32 checksums of FILES"
+
 #include "libbb.h"
 
+/* This is a NOEXEC applet. Be very careful! */
+
 int cksum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int cksum_main(int argc UNUSED_PARAM, char **argv)
 {
@@ -16,7 +24,6 @@ int cksum_main(int argc UNUSED_PARAM, char **argv)
        off_t length, filesize;
        int bytes_read;
        int exit_code = EXIT_SUCCESS;
-       uint8_t *cp;
 
 #if ENABLE_DESKTOP
        getopt32(argv, ""); /* coreutils 6.9 compat */
@@ -37,11 +44,8 @@ int cksum_main(int argc UNUSED_PARAM, char **argv)
 
 #define read_buf bb_common_bufsiz1
                while ((bytes_read = safe_read(fd, read_buf, sizeof(read_buf))) > 0) {
-                       cp = (uint8_t *) read_buf;
                        length += bytes_read;
-                       do {
-                               crc = (crc << 8) ^ crc32_table[(crc >> 24) ^ *cp++];
-                       } while (--bytes_read);
+                       crc = crc32_block_endian1(crc, read_buf, bytes_read, crc32_table);
                }
                close(fd);
 
index 221cbfb..cd45095 100644 (file)
@@ -4,9 +4,17 @@
  *
  * Copyright (C) 2005 by Robert Sullivan <cogito.ergo.cogito@gmail.com>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define comm_trivial_usage
+//usage:       "[-123] FILE1 FILE2"
+//usage:#define comm_full_usage "\n\n"
+//usage:       "Compare FILE1 with FILE2\n"
+//usage:     "\n       -1      Suppress lines unique to FILE1"
+//usage:     "\n       -2      Suppress lines unique to FILE2"
+//usage:     "\n       -3      Suppress lines common to both files"
+
 #include "libbb.h"
 
 #define COMM_OPT_1 (1 << 0)
index d7c8d91..de2e512 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
  * SELinux support by Yuichi Nakamura <ynakam@hitachisoft.jp>
  *
- * Licensed under GPL v2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */
  * Size reduction.
  */
 
+//usage:#define cp_trivial_usage
+//usage:       "[OPTIONS] SOURCE... DEST"
+//usage:#define cp_full_usage "\n\n"
+//usage:       "Copy SOURCE(s) to DEST\n"
+//usage:     "\n       -a      Same as -dpR"
+//usage:       IF_SELINUX(
+//usage:     "\n       -c      Preserve security context"
+//usage:       )
+//usage:     "\n       -R,-r   Recurse"
+//usage:     "\n       -d,-P   Preserve symlinks (default if -R)"
+//usage:     "\n       -L      Follow all symlinks"
+//usage:     "\n       -H      Follow symlinks on command line"
+//usage:     "\n       -p      Preserve file attributes if possible"
+//usage:     "\n       -f      Overwrite"
+//usage:     "\n       -i      Prompt before overwrite"
+//usage:     "\n       -l,-s   Create (sym)links"
+
 #include "libbb.h"
 #include "libcoreutils/coreutils.h"
 
index 53f343a..84449c7 100644 (file)
@@ -6,9 +6,26 @@
  * Written by Mark Whitley <markw@codepoet.org>
  * debloated by Bernhard Reutner-Fischer
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define cut_trivial_usage
+//usage:       "[OPTIONS] [FILE]..."
+//usage:#define cut_full_usage "\n\n"
+//usage:       "Print selected fields from each input FILE to stdout\n"
+//usage:     "\n       -b LIST Output only bytes from LIST"
+//usage:     "\n       -c LIST Output only characters from LIST"
+//usage:     "\n       -d CHAR Use CHAR instead of tab as the field delimiter"
+//usage:     "\n       -s      Output only the lines containing delimiter"
+//usage:     "\n       -f N    Print only these fields"
+//usage:     "\n       -n      Ignored"
+//usage:
+//usage:#define cut_example_usage
+//usage:       "$ echo \"Hello world\" | cut -f 1 -d ' '\n"
+//usage:       "Hello\n"
+//usage:       "$ echo \"Hello world\" | cut -f 2 -d ' '\n"
+//usage:       "world\n"
+
 #include "libbb.h"
 
 /* This is a NOEXEC applet. Be very careful! */
@@ -37,7 +54,6 @@ static int cmpfunc(const void *a, const void *b)
 {
        return (((struct cut_list *) a)->startpos -
                        ((struct cut_list *) b)->startpos);
-
 }
 
 static void cut_file(FILE *file, char delim, const struct cut_list *cut_lists, unsigned nlists)
@@ -196,7 +212,7 @@ int cut_main(int argc UNUSED_PARAM, char **argv)
                if (opt & CUT_OPT_SUPPRESS_FLGS) {
                        bb_error_msg_and_die
                                ("suppressing non-delimited lines makes sense%s",
-                                _op_on_field);
+                               _op_on_field);
                }
                if (delim != '\t') {
                        bb_error_msg_and_die
@@ -225,7 +241,7 @@ int cut_main(int argc UNUSED_PARAM, char **argv)
                        if (!ntok[0]) {
                                s = BOL;
                        } else {
-                               s = xatoi_u(ntok);
+                               s = xatoi_positive(ntok);
                                /* account for the fact that arrays are zero based, while
                                 * the user expects the first char on the line to be char #1 */
                                if (s != 0)
@@ -238,7 +254,7 @@ int cut_main(int argc UNUSED_PARAM, char **argv)
                        } else if (!ltok[0]) {
                                e = EOL;
                        } else {
-                               e = xatoi_u(ltok);
+                               e = xatoi_positive(ltok);
                                /* if the user specified and end position of 0,
                                 * that means "til the end of the line" */
                                if (e == 0)
index c737f09..767e0d4 100644 (file)
@@ -7,7 +7,7 @@
  * iso-format handling added by Robert Griebl <griebl@gmx.de>
  * bugfixes and cleanup by Bernhard Reutner-Fischer
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 */
 
 /* This 'date' command supports only 2 time setting formats,
 /* Input parsing code is always bulky - used heavy duty libc stuff as
    much as possible, missed out a lot of bounds checking */
 
-/* Default input handling to save surprising some people */
-
-/* GNU coreutils 6.9 man page:
- * date [OPTION]... [+FORMAT]
- * date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]
- * -d, --date=STRING
- *      display time described by STRING, not `now'
- * -f, --file=DATEFILE
- *      like --date once for each line of DATEFILE
- * -r, --reference=FILE
- *      display the last modification time of FILE
- * -R, --rfc-2822
- *      output date and time in RFC 2822 format.
- *      Example: Mon, 07 Aug 2006 12:34:56 -0600
- * --rfc-3339=TIMESPEC
- *      output date and time in RFC 3339 format.
- *      TIMESPEC='date', 'seconds', or 'ns'
- *      Date and time components are separated by a single space:
- *      2006-08-07 12:34:56-06:00
- * -s, --set=STRING
- *      set time described by STRING
- * -u, --utc, --universal
- *      print or set Coordinated Universal Time
- *
- * Busybox:
- * long options are not supported
- * -f is not supported
- * -I seems to roughly match --rfc-3339, but -I has _optional_ param
- *    (thus "-I seconds" doesn't work, only "-Iseconds"),
- *    and does not support -Ins
- * -D FMT is a bbox extension for _input_ conversion of -d DATE
- */
+//applet:IF_DATE(APPLET(date, BB_DIR_BIN, BB_SUID_DROP))
 
 //kbuild:lib-$(CONFIG_DATE) += date.o
 
 //config:        Enable option (-I) to output an ISO-8601 compliant
 //config:        date/time string.
 //config:
+//config:# defaults to "no": stat's nanosecond field is a bit non-portable
 //config:config FEATURE_DATE_NANO
 //config:      bool "Support %[num]N nanosecond format specifier"
 //config:      default n
-//config:      depends on DATE
+//config:      depends on DATE  # syscall(__NR_clock_gettime)
+//config:      select PLATFORM_LINUX
 //config:      help
 //config:        Support %[num]N format specifier. Adds ~250 bytes of code.
 //config:
 //config:        the same format. With it on, 'date DATE' additionally supports
 //config:        MMDDhhmm[[YY]YY][.ss] format.
 
+/* GNU coreutils 6.9 man page:
+ * date [OPTION]... [+FORMAT]
+ * date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]
+ * -d, --date=STRING
+ *      display time described by STRING, not `now'
+ * -f, --file=DATEFILE
+ *      like --date once for each line of DATEFILE
+ * -r, --reference=FILE
+ *      display the last modification time of FILE
+ * -R, --rfc-2822
+ *      output date and time in RFC 2822 format.
+ *      Example: Mon, 07 Aug 2006 12:34:56 -0600
+ * --rfc-3339=TIMESPEC
+ *      output date and time in RFC 3339 format.
+ *      TIMESPEC='date', 'seconds', or 'ns'
+ *      Date and time components are separated by a single space:
+ *      2006-08-07 12:34:56-06:00
+ * -s, --set=STRING
+ *      set time described by STRING
+ * -u, --utc, --universal
+ *      print or set Coordinated Universal Time
+ *
+ * Busybox:
+ * long options are not supported
+ * -f is not supported
+ * -I seems to roughly match --rfc-3339, but -I has _optional_ param
+ *    (thus "-I seconds" doesn't work, only "-Iseconds"),
+ *    and does not support -Ins
+ * -D FMT is a bbox extension for _input_ conversion of -d DATE
+ */
+
+//usage:#define date_trivial_usage
+//usage:       "[OPTIONS] [+FMT] [TIME]"
+//usage:#define date_full_usage "\n\n"
+//usage:       "Display time (using +FMT), or set time\n"
+//usage:       IF_NOT_LONG_OPTS(
+//usage:     "\n       [-s] TIME       Set time to TIME"
+//usage:     "\n       -u              Work in UTC (don't convert to local time)"
+//usage:     "\n       -R              Output RFC-2822 compliant date string"
+//usage:       ) IF_LONG_OPTS(
+//usage:     "\n       [-s,--set] TIME Set time to TIME"
+//usage:     "\n       -u,--utc        Work in UTC (don't convert to local time)"
+//usage:     "\n       -R,--rfc-2822   Output RFC-2822 compliant date string"
+//usage:       )
+//usage:       IF_FEATURE_DATE_ISOFMT(
+//usage:     "\n       -I[SPEC]        Output ISO-8601 compliant date string"
+//usage:     "\n                       SPEC='date' (default) for date only,"
+//usage:     "\n                       'hours', 'minutes', or 'seconds' for date and"
+//usage:     "\n                       time to the indicated precision"
+//usage:       )
+//usage:       IF_NOT_LONG_OPTS(
+//usage:     "\n       -r FILE         Display last modification time of FILE"
+//usage:     "\n       -d TIME         Display TIME, not 'now'"
+//usage:       ) IF_LONG_OPTS(
+//usage:     "\n       -r,--reference FILE     Display last modification time of FILE"
+//usage:     "\n       -d,--date TIME  Display TIME, not 'now'"
+//usage:       )
+//usage:       IF_FEATURE_DATE_ISOFMT(
+//usage:     "\n       -D FMT          Use FMT for -d TIME conversion"
+//usage:       )
+//usage:     "\n"
+//usage:     "\nRecognized TIME formats:"
+//usage:     "\n       hh:mm[:ss]"
+//usage:     "\n       [YYYY.]MM.DD-hh:mm[:ss]"
+//usage:     "\n       YYYY-MM-DD hh:mm[:ss]"
+//usage:     "\n       [[[[[YY]YY]MM]DD]hh]mm[.ss]"
+//usage:       IF_FEATURE_DATE_COMPAT(
+//usage:     "\n       'date TIME' form accepts MMDDhhmm[[YY]YY][.ss] instead"
+//usage:       )
+//usage:
+//usage:#define date_example_usage
+//usage:       "$ date\n"
+//usage:       "Wed Apr 12 18:52:41 MDT 2000\n"
+
 #include "libbb.h"
 #if ENABLE_FEATURE_DATE_NANO
 # include <sys/syscall.h>
@@ -208,6 +253,10 @@ int date_main(int argc UNUSED_PARAM, char **argv)
                ts.tv_sec = statbuf.st_mtime;
 #if ENABLE_FEATURE_DATE_NANO
                ts.tv_nsec = statbuf.st_mtim.tv_nsec;
+               /* Some toolchains use .st_mtimensec instead of st_mtim.tv_nsec.
+                * If you need #define _SVID_SOURCE 1 to enable st_mtim.tv_nsec,
+                * drop a mail to project mailing list please
+                */
 #endif
        } else {
 #if ENABLE_FEATURE_DATE_NANO
@@ -236,7 +285,9 @@ int date_main(int argc UNUSED_PARAM, char **argv)
                }
 
                /* Correct any day of week and day of year etc. fields */
-               tm_time.tm_isdst = -1;  /* Be sure to recheck dst */
+               /* Be sure to recheck dst (but not if date is time_t format) */
+               if (date_str[0] != '@')
+                       tm_time.tm_isdst = -1;
                ts.tv_sec = validate_tm_time(date_str, &tm_time);
 
                maybe_set_utc(opt);
@@ -300,7 +351,7 @@ int date_main(int argc UNUSED_PARAM, char **argv)
                        scale = 1;
                        pres = 9;
                        if (n) {
-                               pres = xatoi_u(p);
+                               pres = xatoi_positive(p);
                                if (pres == 0)
                                        pres = 9;
                                m = 9 - pres;
index 7c1a0c0..2838f63 100644 (file)
@@ -5,9 +5,41 @@
  *
  * Copyright (C) 2000,2001  Matt Kraai
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define dd_trivial_usage
+//usage:       "[if=FILE] [of=FILE] " IF_FEATURE_DD_IBS_OBS("[ibs=N] [obs=N] ") "[bs=N] [count=N] [skip=N]\n"
+//usage:       "       [seek=N]" IF_FEATURE_DD_IBS_OBS(" [conv=notrunc|noerror|sync|fsync]")
+//usage:#define dd_full_usage "\n\n"
+//usage:       "Copy a file with converting and formatting\n"
+//usage:     "\n       if=FILE         Read from FILE instead of stdin"
+//usage:     "\n       of=FILE         Write to FILE instead of stdout"
+//usage:     "\n       bs=N            Read and write N bytes at a time"
+//usage:       IF_FEATURE_DD_IBS_OBS(
+//usage:     "\n       ibs=N           Read N bytes at a time"
+//usage:       )
+//usage:       IF_FEATURE_DD_IBS_OBS(
+//usage:     "\n       obs=N           Write N bytes at a time"
+//usage:       )
+//usage:     "\n       count=N         Copy only N input blocks"
+//usage:     "\n       skip=N          Skip N input blocks"
+//usage:     "\n       seek=N          Skip N output blocks"
+//usage:       IF_FEATURE_DD_IBS_OBS(
+//usage:     "\n       conv=notrunc    Don't truncate output file"
+//usage:     "\n       conv=noerror    Continue after read errors"
+//usage:     "\n       conv=sync       Pad blocks with zeros"
+//usage:     "\n       conv=fsync      Physically write data out before finishing"
+//usage:     "\n       conv=swab       Swap every pair of bytes"
+//usage:       )
+//usage:     "\n"
+//usage:     "\nN may be suffixed by c (1), w (2), b (512), kD (1000), k (1024), MD, M, GD, G"
+//usage:
+//usage:#define dd_example_usage
+//usage:       "$ dd if=/dev/zero of=/dev/ram1 bs=1M count=4\n"
+//usage:       "4+0 records in\n"
+//usage:       "4+0 records out\n"
+
 #include "libbb.h"
 
 /* This is a NOEXEC applet. Be very careful! */
@@ -24,7 +56,7 @@ static const struct suffix_mult dd_suffixes[] = {
        { "b", 512 },
        { "kD", 1000 },
        { "k", 1024 },
-       { "K", 1024 },  /* compat with coreutils dd */
+       { "K", 1024 },  /* compat with coreutils dd */
        { "MD", 1000000 },
        { "M", 1048576 },
        { "GD", 1000000000 },
@@ -119,13 +151,14 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
        enum {
                /* Must be in the same order as OP_conv_XXX! */
                /* (see "flags |= (1 << what)" below) */
-               FLAG_NOTRUNC = 1 << 0,
-               FLAG_SYNC    = 1 << 1,
-               FLAG_NOERROR = 1 << 2,
-               FLAG_FSYNC   = 1 << 3,
+               FLAG_NOTRUNC = (1 << 0) * ENABLE_FEATURE_DD_IBS_OBS,
+               FLAG_SYNC    = (1 << 1) * ENABLE_FEATURE_DD_IBS_OBS,
+               FLAG_NOERROR = (1 << 2) * ENABLE_FEATURE_DD_IBS_OBS,
+               FLAG_FSYNC   = (1 << 3) * ENABLE_FEATURE_DD_IBS_OBS,
+               FLAG_SWAB    = (1 << 4) * ENABLE_FEATURE_DD_IBS_OBS,
                /* end of conv flags */
-               FLAG_TWOBUFS = 1 << 4,
-               FLAG_COUNT   = 1 << 5,
+               FLAG_TWOBUFS = (1 << 5) * ENABLE_FEATURE_DD_IBS_OBS,
+               FLAG_COUNT   = 1 << 6,
        };
        static const char keywords[] ALIGN1 =
                "bs\0""count\0""seek\0""skip\0""if\0""of\0"
@@ -135,7 +168,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
                ;
 #if ENABLE_FEATURE_DD_IBS_OBS
        static const char conv_words[] ALIGN1 =
-               "notrunc\0""sync\0""noerror\0""fsync\0";
+               "notrunc\0""sync\0""noerror\0""fsync\0""swab\0";
 #endif
        enum {
                OP_bs = 0,
@@ -153,11 +186,11 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
                OP_conv_sync,
                OP_conv_noerror,
                OP_conv_fsync,
+               OP_conv_swab,
        /* Unimplemented conv=XXX: */
        //nocreat       do not create the output file
        //excl          fail if the output file already exists
        //fdatasync     physically write output file data before finishing
-       //swab          swap every pair of input bytes
        //lcase         change upper case to lower case
        //ucase         change lower case to upper case
        //block         pad newline-terminated records with spaces to cbs-size
@@ -165,22 +198,33 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
        //ascii         from EBCDIC to ASCII
        //ebcdic        from ASCII to EBCDIC
        //ibm           from ASCII to alternate EBCDIC
+       /* Partially implemented: */
+       //swab          swap every pair of input bytes: will abort on non-even reads
 #endif
        };
-       int exitcode = EXIT_FAILURE;
-       size_t ibs = 512, obs = 512;
-       ssize_t n, w;
-       char *ibuf, *obuf;
-       /* And these are all zeroed at once! */
+       smallint exitcode = EXIT_FAILURE;
+       int i;
+       size_t ibs = 512;
+       char *ibuf;
+#if ENABLE_FEATURE_DD_IBS_OBS
+       size_t obs = 512;
+       char *obuf;
+#else
+# define obs  ibs
+# define obuf ibuf
+#endif
+       /* These are all zeroed at once! */
        struct {
                int flags;
                size_t oc;
+               ssize_t prev_read_size; /* for detecting swab failure */
                off_t count;
                off_t seek, skip;
                const char *infile, *outfile;
        } Z;
 #define flags   (Z.flags  )
 #define oc      (Z.oc     )
+#define prev_read_size (Z.prev_read_size)
 #define count   (Z.count  )
 #define seek    (Z.seek   )
 #define skip    (Z.skip   )
@@ -191,10 +235,10 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
        INIT_G();
        //fflush_all(); - is this needed because of NOEXEC?
 
-       for (n = 1; argv[n]; n++) {
+       for (i = 1; argv[i]; i++) {
                int what;
                char *val;
-               char *arg = argv[n];
+               char *arg = argv[i];
 
 #if ENABLE_DESKTOP
                /* "dd --". NB: coreutils 6.9 will complain if they see
@@ -223,6 +267,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
                }
                if (what == OP_conv) {
                        while (1) {
+                               int n;
                                /* find ',', replace them with NUL so we can use val for
                                 * index_in_strings() without copying.
                                 * We rely on val being non-null, else strchr would fault.
@@ -230,20 +275,21 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
                                arg = strchr(val, ',');
                                if (arg)
                                        *arg = '\0';
-                               what = index_in_strings(conv_words, val);
-                               if (what < 0)
+                               n = index_in_strings(conv_words, val);
+                               if (n < 0)
                                        bb_error_msg_and_die(bb_msg_invalid_arg, val, "conv");
-                               flags |= (1 << what);
+                               flags |= (1 << n);
                                if (!arg) /* no ',' left, so this was the last specifier */
                                        break;
                                /* *arg = ','; - to preserve ps listing? */
                                val = arg + 1; /* skip this keyword and ',' */
                        }
-                       continue; /* we trashed 'what', can't fall through */
+                       /*continue;*/
                }
 #endif
                if (what == OP_bs) {
-                       ibs = obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes);
+                       ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes);
+                       obs = ibs;
                        /*continue;*/
                }
                /* These can be large: */
@@ -268,14 +314,17 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
                        outfile = val;
                        /*continue;*/
                }
-       } /* end of "for (argv[n])" */
+       } /* end of "for (argv[i])" */
 
 //XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever
-       ibuf = obuf = xmalloc(ibs);
+       ibuf = xmalloc(ibs);
+       obuf = ibuf;
+#if ENABLE_FEATURE_DD_IBS_OBS
        if (ibs != obs) {
                flags |= FLAG_TWOBUFS;
                obuf = xmalloc(obs);
        }
+#endif
 
 #if ENABLE_FEATURE_DD_SIGNAL_HANDLING
        signal_SA_RESTART_empty_mask(SIGUSR1, dd_output_status);
@@ -284,12 +333,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
        G.begin_time_us = monotonic_us();
 #endif
 
-       if (infile != NULL)
+       if (infile) {
                xmove_fd(xopen(infile, O_RDONLY), ifd);
-       else {
+       else {
                infile = bb_msg_standard_input;
        }
-       if (outfile != NULL) {
+       if (outfile) {
                int oflag = O_WRONLY | O_CREAT;
 
                if (!seek && !(flags & FLAG_NOTRUNC))
@@ -314,13 +363,13 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
        }
        if (skip) {
                if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) {
-                       while (skip-- > 0) {
-                               n = safe_read(ifd, ibuf, ibs);
+                       do {
+                               ssize_t n = safe_read(ifd, ibuf, ibs);
                                if (n < 0)
                                        goto die_infile;
                                if (n == 0)
                                        break;
-                       }
+                       } while (--skip != 0);
                }
        }
        if (seek) {
@@ -329,6 +378,8 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
        }
 
        while (!(flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) {
+               ssize_t n;
+
                n = safe_read(ifd, ibuf, ibs);
                if (n == 0)
                        break;
@@ -343,6 +394,27 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
                         * conv=noerror just ignores input bad blocks */
                        n = 0;
                }
+               if (flags & FLAG_SWAB) {
+                       uint16_t *p16;
+                       ssize_t n2;
+
+                       /* Our code allows only last read to be odd-sized */
+                       if (prev_read_size & 1)
+                               bb_error_msg_and_die("can't swab %lu byte buffer",
+                                               (unsigned long)prev_read_size);
+                       prev_read_size = n;
+
+                       /* If n is odd, last byte is not swapped:
+                        *  echo -n "qwe" | dd conv=swab
+                        * prints "wqe".
+                        */
+                       p16 = (void*) ibuf;
+                       n2 = (n >> 1);
+                       while (--n2 >= 0) {
+                               *p16 = bswap_16(*p16);
+                               p16++;
+                       }
+               }
                if ((size_t)n == ibs)
                        G.in_full++;
                else {
@@ -369,8 +441,10 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
                                        oc = 0;
                                }
                        }
-               } else if (write_and_stats(ibuf, n, obs, outfile))
-                       goto out_status;
+               } else {
+                       if (write_and_stats(ibuf, n, obs, outfile))
+                               goto out_status;
+               }
 
                if (flags & FLAG_FSYNC) {
                        if (fsync(ofd) < 0)
@@ -379,9 +453,8 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
        }
 
        if (ENABLE_FEATURE_DD_IBS_OBS && oc) {
-               w = full_write_or_warn(obuf, oc, outfile);
-               if (w < 0) goto out_status;
-               if (w > 0) G.out_part++;
+               if (write_and_stats(obuf, oc, obs, outfile))
+                       goto out_status;
        }
        if (close(ifd) < 0) {
  die_infile:
@@ -397,5 +470,11 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
  out_status:
        dd_output_status(0);
 
+       if (ENABLE_FEATURE_CLEAN_UP) {
+               free(obuf);
+               if (flags & FLAG_TWOBUFS)
+                       free(ibuf);
+       }
+
        return exitcode;
 }
index 5eeb5b4..5e9a867 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  * based on original code by (I think) Bruce Perens <bruce@pixar.com>.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 _NOT_ compliant -- option -t missing. */
  * Implement -P and -B; better coreutils compat; cleanup
  */
 
+//usage:#define df_trivial_usage
+//usage:       "[-Pk"
+//usage:       IF_FEATURE_HUMAN_READABLE("mh")
+//usage:       IF_FEATURE_DF_FANCY("ai] [-B SIZE")
+//usage:       "] [FILESYSTEM]..."
+//usage:#define df_full_usage "\n\n"
+//usage:       "Print filesystem usage statistics\n"
+//usage:     "\n       -P      POSIX output format"
+//usage:     "\n       -k      1024-byte blocks (default)"
+//usage:       IF_FEATURE_HUMAN_READABLE(
+//usage:     "\n       -m      1M-byte blocks"
+//usage:     "\n       -h      Human readable (e.g. 1K 243M 2G)"
+//usage:       )
+//usage:       IF_FEATURE_DF_FANCY(
+//usage:     "\n       -a      Show all filesystems"
+//usage:     "\n       -i      Inodes"
+//usage:     "\n       -B SIZE Blocksize"
+//usage:       )
+//usage:
+//usage:#define df_example_usage
+//usage:       "$ df\n"
+//usage:       "Filesystem           1K-blocks      Used Available Use% Mounted on\n"
+//usage:       "/dev/sda3              8690864   8553540    137324  98% /\n"
+//usage:       "/dev/sda1                64216     36364     27852  57% /boot\n"
+//usage:       "$ df /dev/sda3\n"
+//usage:       "Filesystem           1K-blocks      Used Available Use% Mounted on\n"
+//usage:       "/dev/sda3              8690864   8553540    137324  98% /\n"
+//usage:       "$ POSIXLY_CORRECT=sure df /dev/sda3\n"
+//usage:       "Filesystem         512B-blocks      Used Available Use% Mounted on\n"
+//usage:       "/dev/sda3             17381728  17107080    274648  98% /\n"
+//usage:       "$ POSIXLY_CORRECT=yep df -P /dev/sda3\n"
+//usage:       "Filesystem          512-blocks      Used Available Capacity Mounted on\n"
+//usage:       "/dev/sda3             17381728  17107080    274648      98% /\n"
+
 #include <mntent.h>
 #include <sys/vfs.h>
 #include "libbb.h"
@@ -76,9 +110,9 @@ int df_main(int argc UNUSED_PARAM, char **argv)
                df_disp_hr = xatoul_range(chp, 1, ULONG_MAX); /* disallow 0 */
 
        /* From the manpage of df from coreutils-6.10:
-          Disk space is shown in 1K blocks by default, unless the environment
-          variable POSIXLY_CORRECT is set, in which case 512-byte blocks are used.
-       */
+        * Disk space is shown in 1K blocks by default, unless the environment
+        * variable POSIXLY_CORRECT is set, in which case 512-byte blocks are used.
+        */
        if (getenv("POSIXLY_CORRECT")) /* TODO - a new libbb function? */
                df_disp_hr = 512;
 
@@ -160,7 +194,7 @@ int df_main(int argc UNUSED_PARAM, char **argv)
                        }
 
                        /* GNU coreutils 6.10 skips certain mounts, try to be compatible.  */
-                       if (strcmp(device, "rootfs") == 0)
+                       if (ENABLE_FEATURE_SKIP_ROOTFS && strcmp(device, "rootfs") == 0)
                                continue;
 
 #ifdef WHY_WE_DO_IT_FOR_DEV_ROOT_ONLY
@@ -178,7 +212,7 @@ int df_main(int argc UNUSED_PARAM, char **argv)
                        {
                                uni_stat_t uni_stat;
                                char *uni_dev = unicode_conv_to_printable(&uni_stat, device);
-                               if (uni_stat.unicode_width > 20) {
+                               if (uni_stat.unicode_width > 20 && !(opt & OPT_POSIX)) {
                                        printf("%s\n%20s", uni_dev, "");
                                } else {
                                        printf("%s%*s", uni_dev, 20 - (int)uni_stat.unicode_width, "");
@@ -186,8 +220,8 @@ int df_main(int argc UNUSED_PARAM, char **argv)
                                free(uni_dev);
                        }
 #else
-                       if (printf("\n%-20s" + 1, device) > 20)
-                                   printf("\n%-20s", "");
+                       if (printf("\n%-20s" + 1, device) > 20 && !(opt & OPT_POSIX))
+                               printf("\n%-20s", "");
 #endif
 
 #if ENABLE_FEATURE_HUMAN_READABLE
index 94c22a7..101067c 100644 (file)
@@ -4,12 +4,23 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/dirname.html */
 
+//usage:#define dirname_trivial_usage
+//usage:       "FILENAME"
+//usage:#define dirname_full_usage "\n\n"
+//usage:       "Strip non-directory suffix from FILENAME"
+//usage:
+//usage:#define dirname_example_usage
+//usage:       "$ dirname /tmp/foo\n"
+//usage:       "/tmp\n"
+//usage:       "$ dirname /tmp/foo/\n"
+//usage:       "/tmp\n"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
index e06ecc4..07398bd 100644 (file)
@@ -9,11 +9,29 @@
  *
  * dos2unix filters reading input from stdin and writing output to stdout.
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 */
 
+//usage:#define dos2unix_trivial_usage
+//usage:       "[-ud] [FILE]"
+//usage:#define dos2unix_full_usage "\n\n"
+//usage:       "Convert FILE in-place from DOS to Unix format.\n"
+//usage:       "When no file is given, use stdin/stdout.\n"
+//usage:     "\n       -u      dos2unix"
+//usage:     "\n       -d      unix2dos"
+//usage:
+//usage:#define unix2dos_trivial_usage
+//usage:       "[-ud] [FILE]"
+//usage:#define unix2dos_full_usage "\n\n"
+//usage:       "Convert FILE in-place from Unix to DOS format.\n"
+//usage:       "When no file is given, use stdin/stdout.\n"
+//usage:     "\n       -u      dos2unix"
+//usage:     "\n       -d      unix2dos"
+
 #include "libbb.h"
 
+/* This is a NOEXEC applet. Be very careful! */
+
 enum {
        CT_UNIX2DOS = 1,
        CT_DOS2UNIX
@@ -39,12 +57,10 @@ static void convert(char *fn, int conv_type)
                fstat(fileno(in), &st);
 
                temp_fn = xasprintf("%sXXXXXX", resolved_fn);
-               i = mkstemp(temp_fn);
-               if (i == -1
-                || fchmod(i, st.st_mode) == -1
-               ) {
+               i = xmkstemp(temp_fn);
+               if (fchmod(i, st.st_mode) == -1)
                        bb_simple_perror_msg_and_die(temp_fn);
-               }
+
                out = xfdopen_for_write(i);
        }
 
index 5894ed4..9c6ff88 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
  * Copyright (C) 2002  Edward Betts <edward@debian.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */
  * 4) Fixed busybox bug #1284 involving long overflow with human_readable.
  */
 
+//usage:#define du_trivial_usage
+//usage:       "[-aHLdclsx" IF_FEATURE_HUMAN_READABLE("hm") "k] [FILE]..."
+//usage:#define du_full_usage "\n\n"
+//usage:       "Summarize disk space used for each FILE and/or directory\n"
+//usage:     "\n       -a      Show file sizes too"
+//usage:     "\n       -L      Follow all symlinks"
+//usage:     "\n       -H      Follow symlinks on command line"
+//usage:     "\n       -d N    Limit output to directories (and files with -a) of depth < N"
+//usage:     "\n       -c      Show grand total"
+//usage:     "\n       -l      Count sizes many times if hard linked"
+//usage:     "\n       -s      Display only a total for each argument"
+//usage:     "\n       -x      Skip directories on different filesystems"
+//usage:       IF_FEATURE_HUMAN_READABLE(
+//usage:     "\n       -h      Sizes in human readable format (e.g., 1K 243M 2G)"
+//usage:     "\n       -m      Sizes in megabytes"
+//usage:       )
+//usage:     "\n       -k      Sizes in kilobytes" IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(" (default)")
+//usage:       IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(
+//usage:     "\n               Default unit is 512 bytes"
+//usage:       )
+//usage:
+//usage:#define du_example_usage
+//usage:       "$ du\n"
+//usage:       "16      ./CVS\n"
+//usage:       "12      ./kernel-patches/CVS\n"
+//usage:       "80      ./kernel-patches\n"
+//usage:       "12      ./tests/CVS\n"
+//usage:       "36      ./tests\n"
+//usage:       "12      ./scripts/CVS\n"
+//usage:       "16      ./scripts\n"
+//usage:       "12      ./docs/CVS\n"
+//usage:       "104     ./docs\n"
+//usage:       "2417    .\n"
+
 #include "libbb.h"
 
 enum {
@@ -52,9 +86,14 @@ struct globals {
        dev_t dir_dev;
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { } while (0)
 
 
-static void print(unsigned long size, const char *filename)
+/* FIXME? coreutils' du rounds sizes up:
+ * for example,  1025k file is shown as "2" by du -m.
+ * We round to nearest.
+ */
+static void print(unsigned long long size, const char *filename)
 {
        /* TODO - May not want to defer error checking here. */
 #if ENABLE_FEATURE_HUMAN_READABLE
@@ -68,15 +107,15 @@ static void print(unsigned long size, const char *filename)
                size++;
                size >>= 1;
        }
-       printf("%lu\t%s\n", size, filename);
+       printf("%llu\t%s\n", size, filename);
 #endif
 }
 
 /* tiny recursive du */
-static unsigned long du(const char *filename)
+static unsigned long long du(const char *filename)
 {
        struct stat statbuf;
-       unsigned long sum;
+       unsigned long long sum;
 
        if (lstat(filename, &statbuf) != 0) {
                bb_simple_perror_msg(filename);
@@ -153,10 +192,12 @@ static unsigned long du(const char *filename)
 int du_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int du_main(int argc UNUSED_PARAM, char **argv)
 {
-       unsigned long total;
+       unsigned long long total;
        int slink_depth_save;
        unsigned opt;
 
+       INIT_G();
+
 #if ENABLE_FEATURE_HUMAN_READABLE
        IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_hr = 1024;)
        IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_hr = 512;)
index 6903996..9663894 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (c) 1991, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Original copyright notice is retained at the end of this file.
  */
  *    The previous version did not allow 4-digit octals.
  */
 
+//usage:#define echo_trivial_usage
+//usage:       IF_FEATURE_FANCY_ECHO("[-neE] ") "[ARG]..."
+//usage:#define echo_full_usage "\n\n"
+//usage:       "Print the specified ARGs to stdout"
+//usage:       IF_FEATURE_FANCY_ECHO( "\n"
+//usage:     "\n       -n      Suppress trailing newline"
+//usage:     "\n       -e      Interpret backslash escapes (i.e., \\t=tab)"
+//usage:     "\n       -E      Don't interpret backslash escapes (default)"
+//usage:       )
+//usage:
+//usage:#define echo_example_usage
+//usage:       "$ echo \"Erik is cool\"\n"
+//usage:       "Erik is cool\n"
+//usage:       IF_FEATURE_FANCY_ECHO("$ echo -e \"Erik\\nis\\ncool\"\n"
+//usage:       "Erik\n"
+//usage:       "is\n"
+//usage:       "cool\n"
+//usage:       "$ echo \"Erik\\nis\\ncool\"\n"
+//usage:       "Erik\\nis\\ncool\n")
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
 
 /* NB: can be used by shell even if not enabled as applet */
 
+/*
+ * NB2: we don't use stdio, we need better error handing.
+ * Examples include writing into non-opened stdout and error on write.
+ *
+ * With stdio, output gets shoveled into stdout buffer, and even
+ * fflush cannot clear it out. It seems that even if libc receives
+ * EBADF on write attempts, it feels determined to output data no matter what.
+ * If echo is called by shell, it will try writing again later, and possibly
+ * will clobber future output. Not good.
+ *
+ * Solaris has fpurge which discards buffered input. glibc has __fpurge.
+ * But this function is not standard.
+ */
+
 int echo_main(int argc UNUSED_PARAM, char **argv)
 {
+       char **pp;
        const char *arg;
+       char *out;
+       char *buffer;
+       unsigned buflen;
 #if !ENABLE_FEATURE_FANCY_ECHO
        enum {
                eflag = '\\',
                nflag = 1,  /* 1 -- print '\n' */
        };
 
-       /* We must check that stdout is not closed.
-        * The reason for this is highly non-obvious.
-        * echo_main is used from shell. Shell must correctly handle "echo foo"
-        * if stdout is closed. With stdio, output gets shoveled into
-        * stdout buffer, and even fflush cannot clear it out. It seems that
-        * even if libc receives EBADF on write attempts, it feels determined
-        * to output data no matter what. So it will try later,
-        * and possibly will clobber future output. Not good. */
-// TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR?
-       if (fcntl(1, F_GETFL) == -1)
-               return 1; /* match coreutils 6.10 (sans error msg to stderr) */
-       //if (dup2(1, 1) != 1) - old way
-       //      return 1;
-
-       arg = *++argv;
-       if (!arg)
-               goto newline_ret;
+       argv++;
 #else
-       const char *p;
        char nflag = 1;
        char eflag = 0;
 
-       /* We must check that stdout is not closed. */
-       if (fcntl(1, F_GETFL) == -1)
-               return 1;
+       while ((arg = *++argv) != NULL) {
+               char n, e;
 
-       while (1) {
-               arg = *++argv;
-               if (!arg)
-                       goto newline_ret;
-               if (*arg != '-')
-                       break;
+               if (arg[0] != '-')
+                       break; /* not an option arg, echo it */
 
                /* If it appears that we are handling options, then make sure
                 * that all of the options specified are actually valid.
                 * Otherwise, the string should just be echoed.
                 */
-               p = arg + 1;
-               if (!*p)        /* A single '-', so echo it. */
-                       goto just_echo;
-
+               arg++;
+               n = nflag;
+               e = eflag;
                do {
-                       if (!strrchr("neE", *p))
+                       if (*arg == 'n')
+                               n = 0;
+                       else if (*arg == 'e')
+                               e = '\\';
+                       else if (*arg != 'E') {
+                               /* "-ccc" arg with one of c's invalid, echo it */
+                               /* arg consisting from just "-" also handled here */
                                goto just_echo;
-               } while (*++p);
-
-               /* All of the options in this arg are valid, so handle them. */
-               p = arg + 1;
-               do {
-                       if (*p == 'n')
-                               nflag = 0;
-                       if (*p == 'e')
-                               eflag = '\\';
-               } while (*++p);
+                       }
+               } while (*++arg);
+               nflag = n;
+               eflag = e;
        }
  just_echo:
 #endif
-       while (1) {
-               /* arg is already == *argv and isn't NULL */
+
+       buflen = 0;
+       pp = argv;
+       while ((arg = *pp) != NULL) {
+               buflen += strlen(arg) + 1;
+               pp++;
+       }
+       out = buffer = xmalloc(buflen + 1); /* +1 is needed for "no args" case */
+
+       while ((arg = *argv) != NULL) {
                int c;
 
                if (!eflag) {
                        /* optimization for very common case */
-                       fputs(arg, stdout);
-               } else while ((c = *arg++)) {
-                       if (c == eflag) {       /* Check for escape seq. */
+                       out = stpcpy(out, arg);
+               } else
+               while ((c = *arg++) != '\0') {
+                       if (c == eflag) {
+                               /* This is an "\x" sequence */
+
                                if (*arg == 'c') {
-                                       /* '\c' means cancel newline and
+                                       /* "\c" means cancel newline and
                                         * ignore all subsequent chars. */
-                                       goto ret;
+                                       goto do_write;
                                }
-#if !ENABLE_FEATURE_FANCY_ECHO
-                               /* SUSv3 specifies that octal escapes must begin with '0'. */
-                               if ( ((int)(unsigned char)(*arg) - '0') >= 8) /* '8' or bigger */
-#endif
-                               {
-                                       /* Since SUSv3 mandates a first digit of 0, 4-digit octals
-                                       * of the form \0### are accepted. */
-                                       if (*arg == '0') {
-                                               /* NB: don't turn "...\0" into "...\" */
-                                               if (arg[1] && ((unsigned char)(arg[1]) - '0') < 8) {
-                                                       arg++;
-                                               }
+                               /* Since SUSv3 mandates a first digit of 0, 4-digit octals
+                               * of the form \0### are accepted. */
+                               if (*arg == '0') {
+                                       if ((unsigned char)(arg[1] - '0') < 8) {
+                                               /* 2nd char is 0..7: skip leading '0' */
+                                               arg++;
                                        }
-                                       /* bb_process_escape_sequence handles NUL correctly
-                                        * ("...\" case). */
-                                       c = bb_process_escape_sequence(&arg);
+                               }
+                               /* bb_process_escape_sequence handles NUL correctly
+                                * ("...\" case). */
+                               {
+                                       /* optimization: don't force arg to be on-stack,
+                                        * use another variable for that. ~30 bytes win */
+                                       const char *z = arg;
+                                       c = bb_process_escape_sequence(&z);
+                                       arg = z;
                                }
                        }
-                       bb_putchar(c);
+                       *out++ = c;
                }
 
-               arg = *++argv;
-               if (!arg)
+               if (!*++argv)
                        break;
-               bb_putchar(' ');
+               *out++ = ' ';
        }
 
- newline_ret:
        if (nflag) {
-               bb_putchar('\n');
+               *out++ = '\n';
        }
- ret:
-       return fflush_all();
+
+ do_write:
+       /* Careful to error out on partial writes too (think ENOSPC!) */
+       errno = 0;
+       /*r =*/ full_write(STDOUT_FILENO, buffer, out - buffer);
+       free(buffer);
+       if (/*WRONG:r < 0*/ errno) {
+               bb_perror_msg(bb_msg_write_error);
+               return 1;
+       }
+       return 0;
 }
 
-/*-
+/*
  * Copyright (c) 1991, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -230,7 +263,7 @@ int echo_main(int argc, char **argv)
                        goto just_echo;
 
                do {
-                       if (!strrchr("neE", *p))
+                       if (!strchr("neE", *p))
                                goto just_echo;
                } while (*++p);
 
@@ -256,27 +289,23 @@ int echo_main(int argc, char **argv)
                        /* optimization for very common case */
                        p += strlen(arg);
                } else while ((c = *arg++)) {
-                       if (c == eflag) {       /* Check for escape seq. */
+                       if (c == eflag) {
+                               /* This is an "\x" sequence */
+
                                if (*arg == 'c') {
-                                       /* '\c' means cancel newline and
+                                       /* "\c" means cancel newline and
                                         * ignore all subsequent chars. */
                                        cur_io->iov_len = p - (char*)cur_io->iov_base;
                                        cur_io++;
                                        goto ret;
                                }
-#if !ENABLE_FEATURE_FANCY_ECHO
-                               /* SUSv3 specifies that octal escapes must begin with '0'. */
-                               if ( (((unsigned char)*arg) - '1') >= 7)
-#endif
-                               {
-                                       /* Since SUSv3 mandates a first digit of 0, 4-digit octals
-                                       * of the form \0### are accepted. */
-                                       if (*arg == '0' && ((unsigned char)(arg[1]) - '0') < 8) {
-                                               arg++;
-                                       }
-                                       /* bb_process_escape_sequence can handle nul correctly */
-                                       c = bb_process_escape_sequence( (void*) &arg);
+                               /* Since SUSv3 mandates a first digit of 0, 4-digit octals
+                               * of the form \0### are accepted. */
+                               if (*arg == '0' && (unsigned char)(arg[1] - '0') < 8) {
+                                       arg++;
                                }
+                               /* bb_process_escape_sequence can handle nul correctly */
+                               c = bb_process_escape_sequence( (void*) &arg);
                        }
                        *p++ = c;
                }
index d4eab19..807ef13 100644 (file)
@@ -3,9 +3,9 @@
  * env implementation for busybox
  *
  * Copyright (c) 1988, 1993, 1994
- *     The Regents of the University of California.  All rights reserved.
+ * The Regents of the University of California.  All rights reserved.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Original copyright notice is retained at the end of this file.
  *
 
 /* This is a NOEXEC applet. Be very careful! */
 
+//usage:#define env_trivial_usage
+//usage:       "[-iu] [-] [name=value]... [PROG ARGS]"
+//usage:#define env_full_usage "\n\n"
+//usage:       "Print the current environment or run PROG after setting up\n"
+//usage:       "the specified environment\n"
+//usage:     "\n       -, -i   Start with an empty environment"
+//usage:     "\n       -u      Remove variable from the environment"
+
 #include "libbb.h"
 
 #if ENABLE_FEATURE_ENV_LONG_OPTIONS
@@ -103,8 +111,8 @@ int env_main(int argc UNUSED_PARAM, char **argv)
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
- * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
- *             ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ * 3. BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
  *
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
index b874b6a..8d376ff 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 89, 91, 1995-2006 Free Software Foundation, Inc.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * David MacKenzie <djm@gnu.ai.mit.edu>
  *
  *
  *  Caveat: this versions of expand and unexpand don't accept tab lists.
  */
+
+//usage:#define expand_trivial_usage
+//usage:       "[-i] [-t N] [FILE]..."
+//usage:#define expand_full_usage "\n\n"
+//usage:       "Convert tabs to spaces, writing to stdout\n"
+//usage:       IF_FEATURE_EXPAND_LONG_OPTIONS(
+//usage:     "\n       -i,--initial    Don't convert tabs after non blanks"
+//usage:     "\n       -t,--tabs=N     Tabstops every N chars"
+//usage:       )
+//usage:       IF_NOT_FEATURE_EXPAND_LONG_OPTIONS(
+//usage:     "\n       -i      Don't convert tabs after non blanks"
+//usage:     "\n       -t      Tabstops every N chars"
+//usage:       )
+
+//usage:#define unexpand_trivial_usage
+//usage:       "[-fa][-t N] [FILE]..."
+//usage:#define unexpand_full_usage "\n\n"
+//usage:       "Convert spaces to tabs, writing to stdout\n"
+//usage:       IF_FEATURE_UNEXPAND_LONG_OPTIONS(
+//usage:     "\n       -a,--all        Convert all blanks"
+//usage:     "\n       -f,--first-only Convert only leading blanks"
+//usage:     "\n       -t,--tabs=N     Tabstops every N chars"
+//usage:       )
+//usage:       IF_NOT_FEATURE_UNEXPAND_LONG_OPTIONS(
+//usage:     "\n       -a      Convert all blanks"
+//usage:     "\n       -f      Convert only leading blanks"
+//usage:     "\n       -t N    Tabstops every N chars"
+//usage:       )
+
 #include "libbb.h"
 #include "unicode.h"
 
@@ -49,11 +78,7 @@ static void expand(FILE *file, unsigned tab_size, unsigned opt)
                                unsigned len;
                                *ptr = '\0';
 # if ENABLE_UNICODE_SUPPORT
-                               {
-                                       uni_stat_t uni_stat;
-                                       printable_string(&uni_stat, ptr_strbeg);
-                                       len = uni_stat.unicode_width;
-                               }
+                               len = unicode_strwidth(ptr_strbeg);
 # else
                                len = ptr - ptr_strbeg;
 # endif
@@ -109,12 +134,9 @@ static void unexpand(FILE *file, unsigned tab_size, unsigned opt)
                        printf("%*s%.*s", len, "", n, ptr);
 # if ENABLE_UNICODE_SUPPORT
                        {
-                               char c;
-                               uni_stat_t uni_stat;
-                               c = ptr[n];
+                               char c = ptr[n];
                                ptr[n] = '\0';
-                               printable_string(&uni_stat, ptr);
-                               len = uni_stat.unicode_width;
+                               len = unicode_strwidth(ptr);
                                ptr[n] = c;
                        }
 # else
index f40edad..c986f93 100644 (file)
@@ -11,7 +11,7 @@
  *  - reduced 464 bytes.
  *  - 64 math support
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* This program evaluates expressions.  Each token (operator, operand,
 
 /* no getopt needed */
 
+//usage:#define expr_trivial_usage
+//usage:       "EXPRESSION"
+//usage:#define expr_full_usage "\n\n"
+//usage:       "Print the value of EXPRESSION to stdout\n"
+//usage:    "\n"
+//usage:       "EXPRESSION may be:\n"
+//usage:       "       ARG1 | ARG2     ARG1 if it is neither null nor 0, otherwise ARG2\n"
+//usage:       "       ARG1 & ARG2     ARG1 if neither argument is null or 0, otherwise 0\n"
+//usage:       "       ARG1 < ARG2     1 if ARG1 is less than ARG2, else 0. Similarly:\n"
+//usage:       "       ARG1 <= ARG2\n"
+//usage:       "       ARG1 = ARG2\n"
+//usage:       "       ARG1 != ARG2\n"
+//usage:       "       ARG1 >= ARG2\n"
+//usage:       "       ARG1 > ARG2\n"
+//usage:       "       ARG1 + ARG2     Sum of ARG1 and ARG2. Similarly:\n"
+//usage:       "       ARG1 - ARG2\n"
+//usage:       "       ARG1 * ARG2\n"
+//usage:       "       ARG1 / ARG2\n"
+//usage:       "       ARG1 % ARG2\n"
+//usage:       "       STRING : REGEXP         Anchored pattern match of REGEXP in STRING\n"
+//usage:       "       match STRING REGEXP     Same as STRING : REGEXP\n"
+//usage:       "       substr STRING POS LENGTH Substring of STRING, POS counted from 1\n"
+//usage:       "       index STRING CHARS      Index in STRING where any CHARS is found, or 0\n"
+//usage:       "       length STRING           Length of STRING\n"
+//usage:       "       quote TOKEN             Interpret TOKEN as a string, even if\n"
+//usage:       "                               it is a keyword like 'match' or an\n"
+//usage:       "                               operator like '/'\n"
+//usage:       "       (EXPRESSION)            Value of EXPRESSION\n"
+//usage:       "\n"
+//usage:       "Beware that many operators need to be escaped or quoted for shells.\n"
+//usage:       "Comparisons are arithmetic if both ARGs are numbers, else\n"
+//usage:       "lexicographical. Pattern matches return the string matched between\n"
+//usage:       "\\( and \\) or null; if \\( and \\) are not used, they return the number\n"
+//usage:       "of characters matched or 0."
+
 #include "libbb.h"
 #include "xregex.h"
 
@@ -65,6 +100,7 @@ struct globals {
        char **args;
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { } while (0)
 
 /* forward declarations */
 static VALUE *eval(void);
@@ -341,7 +377,6 @@ static VALUE *eval6(void)
                freev(i2);
        }
        return v;
-
 }
 
 /* Handle : operator (pattern matching).
@@ -485,6 +520,8 @@ int expr_main(int argc UNUSED_PARAM, char **argv)
 {
        VALUE *v;
 
+       INIT_G();
+
        xfunc_error_retval = 2; /* coreutils compat */
        G.args = argv + 1;
        if (*G.args == NULL) {
index f448ebf..59c2f32 100644 (file)
@@ -4,12 +4,22 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
 /* http://www.opengroup.org/onlinepubs/000095399/utilities/false.html */
 
+//usage:#define false_trivial_usage
+//usage:       ""
+//usage:#define false_full_usage "\n\n"
+//usage:       "Return an exit code of FALSE (1)"
+//usage:
+//usage:#define false_example_usage
+//usage:       "$ false\n"
+//usage:       "$ echo $?\n"
+//usage:       "1\n"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
index cbea31f..0e73063 100644 (file)
@@ -7,11 +7,22 @@
    Modified for busybox based on coreutils v 5.0
    Copyright (C) 2003 Glenn McGrath
 
-   Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+   Licensed under GPLv2 or later, see file LICENSE in this source tree.
 */
+
+//usage:#define fold_trivial_usage
+//usage:       "[-bs] [-w WIDTH] [FILE]..."
+//usage:#define fold_full_usage "\n\n"
+//usage:       "Wrap input lines in each FILE (or stdin), writing to stdout\n"
+//usage:     "\n       -b      Count bytes rather than columns"
+//usage:     "\n       -s      Break at spaces"
+//usage:     "\n       -w      Use WIDTH columns instead of 80"
+
 #include "libbb.h"
 #include "unicode.h"
 
+/* This is a NOEXEC applet. Be very careful! */
+
 /* Must match getopt32 call */
 #define FLAG_COUNT_BYTES        1
 #define FLAG_BREAK_SPACES       2
index 53900f8..652a41c 100644 (file)
@@ -4,8 +4,15 @@
  *
  * Copyright (C) 2008 Nokia Corporation. All rights reserved.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define fsync_trivial_usage
+//usage:       "[-d] FILE..."
+//usage:#define fsync_full_usage "\n\n"
+//usage:       "Write files' buffered blocks to disk\n"
+//usage:     "\n       -d      Avoid syncing metadata"
+
 #include "libbb.h"
 #ifndef O_NOATIME
 # define O_NOATIME 0
@@ -27,7 +34,7 @@ int fsync_main(int argc UNUSED_PARAM, char **argv)
 
        status = EXIT_SUCCESS;
        do {
-               int fd = open3_or_warn(*argv, O_NOATIME | O_NOCTTY | O_RDONLY, 0);
+               int fd = open_or_warn(*argv, O_NOATIME | O_NOCTTY | O_RDONLY);
 
                if (fd == -1) {
                        status = EXIT_FAILURE;
index cc28374..9388b02 100644 (file)
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
 /* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/head.html */
 
+//kbuild:lib-$(CONFIG_HEAD) += head.o
+
+//usage:#define head_trivial_usage
+//usage:       "[OPTIONS] [FILE]..."
+//usage:#define head_full_usage "\n\n"
+//usage:       "Print first 10 lines of each FILE (or stdin) to stdout.\n"
+//usage:       "With more than one FILE, precede each with a filename header.\n"
+//usage:     "\n       -n N[kbm]       Print first N lines"
+//usage:       IF_FEATURE_FANCY_HEAD(
+//usage:     "\n       -n -N[kbm]      Print all except N last lines"
+//usage:     "\n       -c [-]N[kbm]    Print first N bytes"
+//usage:     "\n       -q              Never print headers"
+//usage:     "\n       -v              Always print headers"
+//usage:       )
+//usage:     "\n"
+//usage:     "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)."
+//usage:
+//usage:#define head_example_usage
+//usage:       "$ head -n 2 /etc/passwd\n"
+//usage:       "root:x:0:0:root:/root:/bin/bash\n"
+//usage:       "daemon:x:1:1:daemon:/usr/sbin:/bin/sh\n"
+
 #include "libbb.h"
 
+/* This is a NOEXEC applet. Be very careful! */
+
+#if !ENABLE_FEATURE_FANCY_HEAD
+# define print_first_N(fp,count,bytes) print_first_N(fp,count)
+#endif
+static void
+print_first_N(FILE *fp, unsigned long count, bool count_bytes)
+{
+#if !ENABLE_FEATURE_FANCY_HEAD
+       const int count_bytes = 0;
+#endif
+       while (count) {
+               int c = getc(fp);
+               if (c == EOF)
+                       break;
+               if (count_bytes || (c == '\n'))
+                       --count;
+               putchar(c);
+       }
+}
+
+#if ENABLE_FEATURE_FANCY_HEAD
+static void
+print_except_N_last_bytes(FILE *fp, unsigned count)
+{
+       unsigned char *circle = xmalloc(++count);
+       unsigned head = 0;
+       for(;;) {
+               int c;
+               c = getc(fp);
+               if (c == EOF)
+                       goto ret;
+               circle[head++] = c;
+               if (head == count)
+                       break;
+       }
+       for (;;) {
+               int c;
+               if (head == count)
+                       head = 0;
+               putchar(circle[head]);
+               c = getc(fp);
+               if (c == EOF)
+                       goto ret;
+               circle[head] = c;
+               head++;
+       }
+ ret:
+       free(circle);
+}
+
+static void
+print_except_N_last_lines(FILE *fp, unsigned count)
+{
+       char **circle = xzalloc((++count) * sizeof(circle[0]));
+       unsigned head = 0;
+       for(;;) {
+               char *c;
+               c = xmalloc_fgets(fp);
+               if (!c)
+                       goto ret;
+               circle[head++] = c;
+               if (head == count)
+                       break;
+       }
+       for (;;) {
+               char *c;
+               if (head == count)
+                       head = 0;
+               fputs(circle[head], stdout);
+               c = xmalloc_fgets(fp);
+               if (!c)
+                       goto ret;
+               free(circle[head]);
+               circle[head++] = c;
+       }
+ ret:
+       head = 0;
+       for(;;) {
+               free(circle[head++]);
+               if (head == count)
+                       break;
+       }
+       free(circle);
+}
+#else
+/* Must never be called */
+void print_except_N_last_bytes(FILE *fp, unsigned count);
+void print_except_N_last_lines(FILE *fp, unsigned count);
+#endif
+
+#if !ENABLE_FEATURE_FANCY_HEAD
+# define eat_num(negative_N,p) eat_num(p)
+#endif
+static unsigned long
+eat_num(bool *negative_N, const char *p)
+{
+#if ENABLE_FEATURE_FANCY_HEAD
+       if (*p == '-') {
+               *negative_N = 1;
+               p++;
+       }
+#endif
+       return xatoul_sfx(p, bkm_suffixes);
+}
+
 static const char head_opts[] ALIGN1 =
        "n:"
 #if ENABLE_FEATURE_FANCY_HEAD
@@ -20,29 +148,25 @@ static const char head_opts[] ALIGN1 =
 #endif
        ;
 
-static const struct suffix_mult head_suffixes[] = {
-       { "b", 512 },
-       { "k", 1024 },
-       { "m", 1024*1024 },
-       { "", 0 }
-};
-
 #define header_fmt_str "\n==> %s <==\n"
 
 int head_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int head_main(int argc, char **argv)
 {
        unsigned long count = 10;
-       unsigned long i;
 #if ENABLE_FEATURE_FANCY_HEAD
-       int count_bytes = 0;
        int header_threshhold = 1;
+       bool count_bytes = 0;
+       bool negative_N = 0;
+#else
+# define header_threshhold 1
+# define count_bytes       0
+# define negative_N        0
 #endif
        FILE *fp;
        const char *fmt;
        char *p;
        int opt;
-       int c;
        int retval = EXIT_SUCCESS;
 
 #if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD
@@ -52,7 +176,7 @@ int head_main(int argc, char **argv)
        ) {
                --argc;
                ++argv;
-               p = (*argv) + 1;
+               p = argv[0] + 1;
                goto GET_COUNT;
        }
 #endif
@@ -76,7 +200,7 @@ int head_main(int argc, char **argv)
 #if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD
  GET_COUNT:
 #endif
-                       count = xatoul_sfx(p, head_suffixes);
+                       count = eat_num(&negative_N, p);
                        break;
                default:
                        bb_show_usage();
@@ -89,19 +213,17 @@ int head_main(int argc, char **argv)
                *--argv = (char*)"-";
 
        fmt = header_fmt_str + 1;
-#if ENABLE_FEATURE_FANCY_HEAD
        if (argc <= header_threshhold) {
+#if ENABLE_FEATURE_FANCY_HEAD
                header_threshhold = 0;
-       }
 #else
-       if (argc <= 1) {
                fmt += 11; /* "" */
-       }
-       /* Now define some things here to avoid #ifdefs in the code below.
-        * These should optimize out of the if conditions below. */
-#define header_threshhold   1
-#define count_bytes         0
 #endif
+       }
+       if (negative_N) {
+               if (count >= INT_MAX / sizeof(char*))
+                       bb_error_msg("count is too big: %lu", count);
+       }
 
        do {
                fp = fopen_or_warn_stdin(*argv);
@@ -112,18 +234,20 @@ int head_main(int argc, char **argv)
                        if (header_threshhold) {
                                printf(fmt, *argv);
                        }
-                       i = count;
-                       while (i && ((c = getc(fp)) != EOF)) {
-                               if (count_bytes || (c == '\n')) {
-                                       --i;
+                       if (negative_N) {
+                               if (count_bytes) {
+                                       print_except_N_last_bytes(fp, count);
+                               } else {
+                                       print_except_N_last_lines(fp, count);
                                }
-                               putchar(c);
+                       } else {
+                               print_first_N(fp, count, count_bytes);
                        }
+                       die_if_ferror_stdout();
                        if (fclose_if_not_stdin(fp)) {
                                bb_simple_perror_msg(*argv);
                                retval = EXIT_FAILURE;
                        }
-                       die_if_ferror_stdout();
                } else {
                        retval = EXIT_FAILURE;
                }
index a537e3a..e5b1f51 100644 (file)
@@ -4,11 +4,27 @@
  *
  * Copyright (C) 2000  Edward Betts <edward@debian.org>.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
 
+//config:config HOSTID
+//config:      bool "hostid"
+//config:      default y
+//config:      help
+//config:        hostid prints the numeric identifier (in hexadecimal) for
+//config:        the current host.
+
+//applet:IF_HOSTID(APPLET_NOFORK(hostid, hostid, BB_DIR_USR_BIN, BB_SUID_DROP, hostid))
+
+//kbuild:lib-$(CONFIG_HOSTID) += hostid.o
+
+//usage:#define hostid_trivial_usage
+//usage:       ""
+//usage:#define hostid_full_usage "\n\n"
+//usage:       "Print out a unique 32-bit identifier for the machine"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
@@ -20,7 +36,8 @@ int hostid_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
                bb_show_usage();
        }
 
-       printf("%lx\n", gethostid());
+       /* POSIX says gethostid returns a "32-bit identifier" */
+       printf("%08x\n", (unsigned)(uint32_t)gethostid());
 
        return fflush_all();
 }
index ec9227d..1f3e1c4 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) 2000 by Randolph Chung <tausq@debian.org>
  * Copyright (C) 2008 by Tito Ragusa <farmatito@tiscali.it>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant. */
  * Added -G option Tito Ragusa (C) 2008 for SUSv3.
  */
 
+//config:config ID
+//config:      bool "id"
+//config:      default y
+//config:      help
+//config:        id displays the current user and group ID names.
+
+//config:config GROUPS
+//config:      bool "groups"
+//config:      default y
+//config:      help
+//config:        Print the group names associated with current user id.
+
+//kbuild:lib-$(CONFIG_GROUPS) += id.o
+//kbuild:lib-$(CONFIG_ID)     += id.o
+
+//applet:IF_GROUPS(APPLET_NOEXEC(groups, id, BB_DIR_USR_BIN, BB_SUID_DROP, groups))
+//applet:IF_ID(    APPLET_NOEXEC(id,     id, BB_DIR_USR_BIN, BB_SUID_DROP, id    ))
+
+//usage:#define id_trivial_usage
+//usage:       "[OPTIONS] [USER]"
+//usage:#define id_full_usage "\n\n"
+//usage:       "Print information about USER or the current user\n"
+//usage:       IF_SELINUX(
+//usage:     "\n       -Z      Security context"
+//usage:       )
+//usage:     "\n       -u      User ID"
+//usage:     "\n       -g      Group ID"
+//usage:     "\n       -G      Supplementary group IDs"
+//usage:     "\n       -n      Print names instead of numbers"
+//usage:     "\n       -r      Print real ID instead of effective ID"
+//usage:
+//usage:#define id_example_usage
+//usage:       "$ id\n"
+//usage:       "uid=1000(andersen) gid=1000(andersen)\n"
+
+//usage:#define groups_trivial_usage
+//usage:       "[USER]"
+//usage:#define groups_full_usage "\n\n"
+//usage:       "Print the group memberships of USER or for the current process"
+//usage:
+//usage:#define groups_example_usage
+//usage:       "$ groups\n"
+//usage:       "andersen lp dialout cdrom floppy\n"
+
 #include "libbb.h"
 
+/* This is a NOEXEC applet. Be very careful! */
+
 #if !ENABLE_USE_BB_PWD_GRP
-#if defined(__UCLIBC_MAJOR__) && (__UCLIBC_MAJOR__ == 0)
-#if (__UCLIBC_MINOR__ < 9) || (__UCLIBC_MINOR__ == 9 &&  __UCLIBC_SUBLEVEL__ < 30)
+#if defined(__UCLIBC__) && UCLIBC_VERSION < KERNEL_VERSION(0, 9, 30)
 #error "Sorry, you need at least uClibc version 0.9.30 for id applet to build"
 #endif
 #endif
-#endif
 
 enum {
        PRINT_REAL      = (1 << 0),
@@ -71,7 +115,7 @@ static int print_user(uid_t id, const char *prefix)
 
 /* On error set *n < 0 and return >= 0
  * If *n is too small, update it and return < 0
- *  (ok to trash groups[] in both cases)
+ * (ok to trash groups[] in both cases)
  * Otherwise fill in groups[] and return >= 0
  */
 static int get_groups(const char *username, gid_t rgid, gid_t *groups, int *n)
@@ -85,20 +129,19 @@ static int get_groups(const char *username, gid_t rgid, gid_t *groups, int *n)
                m = getgrouplist(username, rgid, groups, n);
                /* I guess *n < 0 might indicate error. Anyway,
                 * malloc'ing -1 bytes won't be good, so: */
-               //if (*n < 0)
-               //      return 0;
-               //return m;
-               //commented out here, happens below anyway
-       } else {
-               /* On error -1 is returned, which ends up in *n */
-               int nn = getgroups(*n, groups);
-               /* 0: nn <= *n, groups[] was big enough; -1 otherwise */
-               m = - (nn > *n);
-               *n = nn;
+               if (*n < 0)
+                       return 0;
+               return m;
        }
-       if (*n < 0)
-               return 0; /* error, don't return < 0! */
-       return m;
+
+       *n = getgroups(*n, groups);
+       if (*n >= 0)
+               return *n;
+       /* Error */
+       if (errno == EINVAL) /* *n is too small? */
+               *n = getgroups(0, groups); /* get needed *n */
+       /* if *n >= 0, return -1 (got new *n), else return 0 (error): */
+       return -(*n >= 0);
 }
 
 int id_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -116,11 +159,22 @@ int id_main(int argc UNUSED_PARAM, char **argv)
 #if ENABLE_SELINUX
        security_context_t scontext = NULL;
 #endif
-       /* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/
-       /* Don't allow more than one username */
-       opt_complementary = "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG"
-                        IF_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G");
-       opt = getopt32(argv, "rnugG" IF_SELINUX("Z"));
+
+       if (ENABLE_GROUPS && (!ENABLE_ID || applet_name[0] == 'g')) {
+               /* TODO: coreutils groups prepend "USER : " prefix,
+                * and accept many usernames. Example:
+                * # groups root root
+                * root : root
+                * root : root
+                */
+               opt = option_mask32 = getopt32(argv, "") | JUST_ALL_GROUPS | NAME_NOT_NUMBER;
+       } else {
+               /* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/
+               /* Don't allow more than one username */
+               opt_complementary = "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG"
+                       IF_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G");
+               opt = getopt32(argv, "rnugG" IF_SELINUX("Z"));
+       }
 
        username = argv[optind];
        if (username) {
@@ -157,11 +211,11 @@ int id_main(int argc UNUSED_PARAM, char **argv)
                /* We are supplying largish buffer, trying
                 * to not run get_groups() twice. That might be slow
                 * ("user database in remote SQL server" case) */
-               groups = xmalloc(64 * sizeof(gid_t));
+               groups = xmalloc(64 * sizeof(groups[0]));
                n = 64;
                if (get_groups(username, rgid, groups, &n) < 0) {
                        /* Need bigger buffer after all */
-                       groups = xrealloc(groups, n * sizeof(gid_t));
+                       groups = xrealloc(groups, n * sizeof(groups[0]));
                        get_groups(username, rgid, groups, &n);
                }
                if (n > 0) {
@@ -174,10 +228,9 @@ int id_main(int argc UNUSED_PARAM, char **argv)
                                prefix = ",";
                        }
                } else if (n < 0) { /* error in get_groups() */
-                       if (!ENABLE_DESKTOP)
+                       if (ENABLE_DESKTOP)
                                bb_error_msg_and_die("can't get groups");
-                       else
-                               return EXIT_FAILURE;
+                       return EXIT_FAILURE;
                }
                if (ENABLE_FEATURE_CLEAN_UP)
                        free(groups);
index ab9feff..445497f 100644 (file)
@@ -3,9 +3,26 @@
  * Copyright (C) 2003 by Glenn McGrath
  * SELinux support: by Yuichi Nakamura <ynakam@hitachisoft.jp>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+/* -v, -b, -c are ignored */
+//usage:#define install_trivial_usage
+//usage:       "[-cdDsp] [-o USER] [-g GRP] [-m MODE] [SOURCE]... DEST"
+//usage:#define install_full_usage "\n\n"
+//usage:       "Copy files and set attributes\n"
+//usage:     "\n       -c      Just copy (default)"
+//usage:     "\n       -d      Create directories"
+//usage:     "\n       -D      Create leading target directories"
+//usage:     "\n       -s      Strip symbol table"
+//usage:     "\n       -p      Preserve date"
+//usage:     "\n       -o USER Set ownership"
+//usage:     "\n       -g GRP  Set group ownership"
+//usage:     "\n       -m MODE Set permissions"
+//usage:       IF_SELINUX(
+//usage:     "\n       -Z      Set security context"
+//usage:       )
+
 #include "libbb.h"
 #include "libcoreutils/coreutils.h"
 
similarity index 57%
rename from coreutils/length.c
rename to coreutils/length.c.disabled
index 015b221..aee898d 100644 (file)
@@ -1,10 +1,19 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 N/A -- Apparently a busybox (obsolete?) extension. */
 
+//usage:#define length_trivial_usage
+//usage:       "STRING"
+//usage:#define length_full_usage "\n\n"
+//usage:       "Print STRING's length"
+//usage:
+//usage:#define length_example_usage
+//usage:       "$ length Hello\n"
+//usage:       "5\n"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
index 981b606..2042d5f 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+# Licensed under GPLv2 or later, see file LICENSE in this source tree.
 
 lib-y:=
 
index 99b67b1..307d033 100644 (file)
@@ -1,6 +1,6 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #ifndef COREUTILS_H
index f977aa1..3b822e8 100644 (file)
@@ -4,23 +4,42 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
 /* BB_AUDIT GNU options missing: -d, -F, -i, and -v. */
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/ln.html */
 
+//usage:#define ln_trivial_usage
+//usage:       "[OPTIONS] TARGET... LINK|DIR"
+//usage:#define ln_full_usage "\n\n"
+//usage:       "Create a link LINK or DIR/TARGET to the specified TARGET(s)\n"
+//usage:     "\n       -s      Make symlinks instead of hardlinks"
+//usage:     "\n       -f      Remove existing destinations"
+//usage:     "\n       -n      Don't dereference symlinks - treat like normal file"
+//usage:     "\n       -b      Make a backup of the target (if exists) before link operation"
+//usage:     "\n       -S suf  Use suffix instead of ~ when making backup files"
+//usage:     "\n       -T      2nd arg must be a DIR"
+//usage:     "\n       -v      Verbose"
+//usage:
+//usage:#define ln_example_usage
+//usage:       "$ ln -s BusyBox /tmp/ls\n"
+//usage:       "$ ls -l /tmp/ls\n"
+//usage:       "lrwxrwxrwx    1 root     root            7 Apr 12 18:39 ls -> BusyBox*\n"
+
 #include "libbb.h"
 
 /* This is a NOEXEC applet. Be very careful! */
 
 
-#define LN_SYMLINK          1
-#define LN_FORCE            2
-#define LN_NODEREFERENCE    4
-#define LN_BACKUP           8
-#define LN_SUFFIX           16
+#define LN_SYMLINK          (1 << 0)
+#define LN_FORCE            (1 << 1)
+#define LN_NODEREFERENCE    (1 << 2)
+#define LN_BACKUP           (1 << 3)
+#define LN_SUFFIX           (1 << 4)
+#define LN_VERBOSE          (1 << 5)
+#define LN_LINKFILE         (1 << 6)
 
 int ln_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int ln_main(int argc, char **argv)
@@ -35,13 +54,22 @@ int ln_main(int argc, char **argv)
        int (*link_func)(const char *, const char *);
 
        opt_complementary = "-1"; /* min one arg */
-       opts = getopt32(argv, "sfnbS:", &suffix);
+       opts = getopt32(argv, "sfnbS:vT", &suffix);
 
        last = argv[argc - 1];
        argv += optind;
+       argc -= optind;
+
+       if ((opts & LN_LINKFILE) && argc > 2) {
+               bb_error_msg_and_die("-T accepts 2 args max");
+       }
 
-       if (argc == optind + 1) {
+       if (!argv[1]) {
+               /* "ln PATH/TO/FILE" -> "ln PATH/TO/FILE FILE" */
                *--argv = last;
+               /* xstrdup is needed: "ln -s PATH/TO/FILE/" is equivalent to
+                * "ln -s PATH/TO/FILE/ FILE", not "ln -s PATH/TO/FILE FILE"
+                */
                last = bb_get_last_path_component_strip(xstrdup(last));
        }
 
@@ -50,9 +78,12 @@ int ln_main(int argc, char **argv)
                src = last;
 
                if (is_directory(src,
-                               (opts & LN_NODEREFERENCE) ^ LN_NODEREFERENCE,
-                               NULL)
+                               (opts & LN_NODEREFERENCE) ^ LN_NODEREFERENCE
+                               )
                ) {
+                       if (opts & LN_LINKFILE) {
+                               bb_error_msg_and_die("'%s' is a directory", src);
+                       }
                        src_name = xstrdup(*argv);
                        src = concat_path_file(src, bb_get_last_path_component_strip(src_name));
                        free(src_name);
@@ -93,6 +124,10 @@ int ln_main(int argc, char **argv)
                        link_func = symlink;
                }
 
+               if (opts & LN_VERBOSE) {
+                       printf("'%s' -> '%s'\n", src, *argv);
+               }
+
                if (link_func(*argv, src) != 0) {
                        bb_simple_perror_msg(src);
                        status = EXIT_FAILURE;
index 8357b9a..10b9615 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2000  Edward Betts <edward@debian.org>.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
  * a diagnostic message and an error return.
  */
 
+//usage:#define logname_trivial_usage
+//usage:       ""
+//usage:#define logname_full_usage "\n\n"
+//usage:       "Print the name of the current user"
+//usage:
+//usage:#define logname_example_usage
+//usage:       "$ logname\n"
+//usage:       "root\n"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
index 1197f7d..166473d 100644 (file)
@@ -1,9 +1,8 @@
 /* vi: set sw=4 ts=4: */
 /*
- * tiny-ls.c version 0.1.0: A minimalist 'ls'
  * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* [date unknown. Perhaps before year 2000]
  * [2009-03]
  * ls sorts listing now, and supports almost all options.
  */
+
+//usage:#define ls_trivial_usage
+//usage:       "[-1AaCxd"
+//usage:       IF_FEATURE_LS_FOLLOWLINKS("LH")
+//usage:       IF_FEATURE_LS_RECURSIVE("R")
+//usage:       IF_FEATURE_LS_FILETYPES("Fp") "lins"
+//usage:       IF_FEATURE_LS_TIMESTAMPS("e")
+//usage:       IF_FEATURE_HUMAN_READABLE("h")
+//usage:       IF_FEATURE_LS_SORTFILES("rSXv")
+//usage:       IF_FEATURE_LS_TIMESTAMPS("ctu")
+//usage:       IF_SELINUX("kKZ") "]"
+//usage:       IF_FEATURE_AUTOWIDTH(" [-w WIDTH]") " [FILE]..."
+//usage:#define ls_full_usage "\n\n"
+//usage:       "List directory contents\n"
+//usage:     "\n       -1      One column output"
+//usage:     "\n       -a      Include entries which start with ."
+//usage:     "\n       -A      Like -a, but exclude . and .."
+//usage:     "\n       -C      List by columns"
+//usage:     "\n       -x      List by lines"
+//usage:     "\n       -d      List directory entries instead of contents"
+//usage:       IF_FEATURE_LS_FOLLOWLINKS(
+//usage:     "\n       -L      Follow symlinks"
+//usage:     "\n       -H      Follow symlinks on command line"
+//usage:       )
+//usage:       IF_FEATURE_LS_RECURSIVE(
+//usage:     "\n       -R      Recurse"
+//usage:       )
+//usage:       IF_FEATURE_LS_FILETYPES(
+//usage:     "\n       -p      Append / to dir entries"
+//usage:     "\n       -F      Append indicator (one of */=@|) to entries"
+//usage:       )
+//usage:     "\n       -l      Long listing format"
+//usage:     "\n       -i      List inode numbers"
+//usage:     "\n       -n      List numeric UIDs and GIDs instead of names"
+//usage:     "\n       -s      List allocated blocks"
+//usage:       IF_FEATURE_LS_TIMESTAMPS(
+//usage:     "\n       -e      List full date and time"
+//usage:       )
+//usage:       IF_FEATURE_HUMAN_READABLE(
+//usage:     "\n       -h      List sizes in human readable format (1K 243M 2G)"
+//usage:       )
+//usage:       IF_FEATURE_LS_SORTFILES(
+//usage:     "\n       -r      Sort in reverse order"
+//usage:     "\n       -S      Sort by size"
+//usage:     "\n       -X      Sort by extension"
+//usage:     "\n       -v      Sort by version"
+//usage:       )
+//usage:       IF_FEATURE_LS_TIMESTAMPS(
+//usage:     "\n       -c      With -l: sort by ctime"
+//usage:     "\n       -t      With -l: sort by mtime"
+//usage:     "\n       -u      With -l: sort by atime"
+//usage:       )
+//usage:       IF_SELINUX(
+//usage:     "\n       -k      List security context"
+//usage:     "\n       -K      List security context in long format"
+//usage:     "\n       -Z      List security context and permission"
+//usage:       )
+//usage:       IF_FEATURE_AUTOWIDTH(
+//usage:     "\n       -w N    Assume the terminal is N columns wide"
+//usage:       )
+//usage:       IF_FEATURE_LS_COLOR(
+//usage:     "\n       --color[={always,never,auto}]   Control coloring"
+//usage:       )
+
 #include "libbb.h"
 #include "unicode.h"
 
 
 
 enum {
-
 TERMINAL_WIDTH  = 80,           /* use 79 if terminal has linefold bug */
-COLUMN_GAP      = 2,            /* includes the file type char */
 
-/* what is the overall style of the listing */
-STYLE_COLUMNS   = 1 << 21,      /* fill columns */
-STYLE_LONG      = 2 << 21,      /* one record per line, extended info */
-STYLE_SINGLE    = 3 << 21,      /* one record per line */
-STYLE_MASK      = STYLE_SINGLE,
+SPLIT_FILE      = 0,
+SPLIT_DIR       = 1,
+SPLIT_SUBDIR    = 2,
+
+/* Bits in G.all_fmt: */
 
 /* 51306 lrwxrwxrwx  1 root     root         2 May 11 01:43 /bin/view -> vi* */
 /* what file information will be listed */
@@ -72,77 +133,75 @@ LIST_ID_NAME    = 1 << 4,
 LIST_ID_NUMERIC = 1 << 5,
 LIST_CONTEXT    = 1 << 6,
 LIST_SIZE       = 1 << 7,
-//LIST_DEV        = 1 << 8, - unused, synonym to LIST_SIZE
-LIST_DATE_TIME  = 1 << 9,
-LIST_FULLTIME   = 1 << 10,
-LIST_FILENAME   = 1 << 11,
-LIST_SYMLINK    = 1 << 12,
-LIST_FILETYPE   = 1 << 13,
-LIST_EXEC       = 1 << 14,
-LIST_MASK       = (LIST_EXEC << 1) - 1,
+LIST_DATE_TIME  = 1 << 8,
+LIST_FULLTIME   = 1 << 9,
+LIST_SYMLINK    = 1 << 10,
+LIST_FILETYPE   = 1 << 11, /* show / suffix for dirs */
+LIST_CLASSIFY   = 1 << 12, /* requires LIST_FILETYPE, also show *,|,@,= suffixes */
+LIST_MASK       = (LIST_CLASSIFY << 1) - 1,
 
 /* what files will be displayed */
-DISP_DIRNAME    = 1 << 15,      /* 2 or more items? label directories */
-DISP_HIDDEN     = 1 << 16,      /* show filenames starting with . */
-DISP_DOT        = 1 << 17,      /* show . and .. */
-DISP_NOLIST     = 1 << 18,      /* show directory as itself, not contents */
-DISP_RECURSIVE  = 1 << 19,      /* show directory and everything below it */
-DISP_ROWS       = 1 << 20,      /* print across rows */
+DISP_DIRNAME    = 1 << 13,      /* 2 or more items? label directories */
+DISP_HIDDEN     = 1 << 14,      /* show filenames starting with . */
+DISP_DOT        = 1 << 15,      /* show . and .. */
+DISP_NOLIST     = 1 << 16,      /* show directory as itself, not contents */
+DISP_RECURSIVE  = 1 << 17,      /* show directory and everything below it */
+DISP_ROWS       = 1 << 18,      /* print across rows */
 DISP_MASK       = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
 
-/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
-SORT_FORWARD    = 0,            /* sort in reverse order */
-SORT_REVERSE    = 1 << 27,      /* sort in reverse order */
-
-SORT_NAME       = 0,            /* sort by file name */
-SORT_SIZE       = 1 << 28,      /* sort by file size */
-SORT_ATIME      = 2 << 28,      /* sort by last access time */
-SORT_CTIME      = 3 << 28,      /* sort by last change time */
-SORT_MTIME      = 4 << 28,      /* sort by last modification time */
-SORT_VERSION    = 5 << 28,      /* sort by version */
-SORT_EXT        = 6 << 28,      /* sort by file name extension */
-SORT_DIR        = 7 << 28,      /* sort by file or directory */
-SORT_MASK       = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES,
+/* what is the overall style of the listing */
+STYLE_COLUMNAR  = 1 << 19,      /* many records per line */
+STYLE_LONG      = 2 << 19,      /* one record per line, extended info */
+STYLE_SINGLE    = 3 << 19,      /* one record per line */
+STYLE_MASK      = STYLE_SINGLE,
 
 /* which of the three times will be used */
-TIME_CHANGE     = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
-TIME_ACCESS     = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS,
-TIME_MASK       = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
+TIME_CHANGE     = (1 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
+TIME_ACCESS     = (2 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
+TIME_MASK       = (3 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
 
-FOLLOW_LINKS    = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS,
+/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
+SORT_REVERSE    = 1 << 23,
 
-LS_DISP_HR      = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE,
+SORT_NAME       = 0,            /* sort by file name */
+SORT_SIZE       = 1 << 24,      /* sort by file size */
+SORT_ATIME      = 2 << 24,      /* sort by last access time */
+SORT_CTIME      = 3 << 24,      /* sort by last change time */
+SORT_MTIME      = 4 << 24,      /* sort by last modification time */
+SORT_VERSION    = 5 << 24,      /* sort by version */
+SORT_EXT        = 6 << 24,      /* sort by file name extension */
+SORT_DIR        = 7 << 24,      /* sort by file or directory */
+SORT_MASK       = (7 << 24) * ENABLE_FEATURE_LS_SORTFILES,
 
-LIST_SHORT      = LIST_FILENAME,
 LIST_LONG       = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
-                  LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK,
-
-SPLIT_DIR       = 1,
-SPLIT_FILE      = 0,
-SPLIT_SUBDIR    = 2,
-
+                  LIST_DATE_TIME | LIST_SYMLINK,
 };
 
-/* "[-]Cadil1", POSIX mandated options, busybox always supports */
-/* "[-]gnsx", POSIX non-mandated options, busybox always supports */
-/* "[-]Q" GNU option? busybox always supports */
-/* "[-]Ak" GNU options, busybox always supports */
-/* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
-/* "[-]p", POSIX non-mandated options, busybox optionally supports */
-/* "[-]SXvThw", GNU options, busybox optionally supports */
-/* "[-]K", SELinux mandated options, busybox optionally supports */
-/* "[-]e", I think we made this one up */
+/* -Cadil1  Std options, busybox always supports */
+/* -gnsxA   Std options, busybox always supports */
+/* -Q       GNU option, busybox always supports */
+/* -k       SELinux option, busybox always supports (ignores if !SELinux) */
+/*          Std has -k which means "show sizes in kbytes" */
+/* -LHRctur Std options, busybox optionally supports */
+/* -Fp      Std options, busybox optionally supports */
+/* -SXvhTw  GNU options, busybox optionally supports */
+/* -T WIDTH Ignored (we don't use tabs on output) */
+/* -KZ      SELinux mandated options, busybox optionally supports */
+/*          (coreutils 8.4 has no -K, remove it?) */
+/* -e       I think we made this one up (looks similar to GNU --full-time) */
+/* We already used up all 32 bits, if we need to add more, candidates for removal: */
+/* -K, -T, -e (add --full-time instead) */
 static const char ls_options[] ALIGN1 =
-       "Cadil1gnsxQAk" /* 13 opts, total 13 */
+       "Cadil1gnsxQAk"      /* 13 opts, total 13 */
        IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
        IF_FEATURE_LS_SORTFILES("SXrv")  /* 4, 21 */
        IF_FEATURE_LS_FILETYPES("Fp")    /* 2, 23 */
-       IF_FEATURE_LS_FOLLOWLINKS("L")   /* 1, 24 */
-       IF_FEATURE_LS_RECURSIVE("R")     /* 1, 25 */
-       IF_FEATURE_HUMAN_READABLE("h")   /* 1, 26 */
-       IF_SELINUX("KZ") /* 2, 28 */
-       IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */
-       ;
+       IF_FEATURE_LS_RECURSIVE("R")     /* 1, 24 */
+       IF_SELINUX("KZ")                 /* 2, 26 */
+       IF_FEATURE_LS_FOLLOWLINKS("LH")  /* 2, 28 */
+       IF_FEATURE_HUMAN_READABLE("h")   /* 1, 29 */
+       IF_FEATURE_AUTOWIDTH("T:w:")     /* 2, 31 */
+       /* with --color, we use all 32 bits */;
 enum {
        //OPT_C = (1 << 0),
        //OPT_a = (1 << 1),
@@ -157,99 +216,149 @@ enum {
        OPT_Q = (1 << 10),
        //OPT_A = (1 << 11),
        //OPT_k = (1 << 12),
-       OPTBIT_color = 13
-               + 4 * ENABLE_FEATURE_LS_TIMESTAMPS
-               + 4 * ENABLE_FEATURE_LS_SORTFILES
-               + 2 * ENABLE_FEATURE_LS_FILETYPES
-               + 1 * ENABLE_FEATURE_LS_FOLLOWLINKS
-               + 1 * ENABLE_FEATURE_LS_RECURSIVE
-               + 1 * ENABLE_FEATURE_HUMAN_READABLE
-               + 2 * ENABLE_SELINUX
-               + 2 * ENABLE_FEATURE_AUTOWIDTH,
-       OPT_color = 1 << OPTBIT_color,
-};
 
-enum {
-       LIST_MASK_TRIGGER       = 0,
-       STYLE_MASK_TRIGGER      = STYLE_MASK,
-       DISP_MASK_TRIGGER       = DISP_ROWS,
-       SORT_MASK_TRIGGER       = SORT_MASK,
+       OPTBIT_c = 13,
+       OPTBIT_e,
+       OPTBIT_t,
+       OPTBIT_u,
+       OPTBIT_S = OPTBIT_c + 4 * ENABLE_FEATURE_LS_TIMESTAMPS,
+       OPTBIT_X, /* 18 */
+       OPTBIT_r,
+       OPTBIT_v,
+       OPTBIT_F = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
+       OPTBIT_p, /* 22 */
+       OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
+       OPTBIT_K = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
+       OPTBIT_Z, /* 25 */
+       OPTBIT_L = OPTBIT_K + 2 * ENABLE_SELINUX,
+       OPTBIT_H, /* 27 */
+       OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
+       OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
+       OPTBIT_w, /* 30 */
+       OPTBIT_color = OPTBIT_T + 2 * ENABLE_FEATURE_AUTOWIDTH,
+
+       OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS,
+       OPT_e = (1 << OPTBIT_e) * ENABLE_FEATURE_LS_TIMESTAMPS,
+       OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS,
+       OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS,
+       OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES,
+       OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES,
+       OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES,
+       OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES,
+       OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
+       OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
+       OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE,
+       OPT_K = (1 << OPTBIT_K) * ENABLE_SELINUX,
+       OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX,
+       OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS,
+       OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS,
+       OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE,
+       OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_AUTOWIDTH,
+       OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_AUTOWIDTH,
+       OPT_color = (1 << OPTBIT_color) * ENABLE_FEATURE_LS_COLOR,
 };
 
 /* TODO: simple toggles may be stored as OPT_xxx bits instead */
-static const unsigned opt_flags[] = {
-       LIST_SHORT | STYLE_COLUMNS, /* C */
-       DISP_HIDDEN | DISP_DOT,     /* a */
-       DISP_NOLIST,                /* d */
-       LIST_INO,                   /* i */
-       LIST_LONG | STYLE_LONG,     /* l - remember LS_DISP_HR in mask! */
-       LIST_SHORT | STYLE_SINGLE,  /* 1 */
-       0,                          /* g (don't show group) - handled via OPT_g */
-       LIST_ID_NUMERIC,            /* n */
-       LIST_BLOCKS,                /* s */
-       DISP_ROWS,                  /* x */
-       0,                          /* Q (quote filename) - handled via OPT_Q */
-       DISP_HIDDEN,                /* A */
-       ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
+static const uint32_t opt_flags[] = {
+       STYLE_COLUMNAR,              /* C */
+       DISP_HIDDEN | DISP_DOT,      /* a */
+       DISP_NOLIST,                 /* d */
+       LIST_INO,                    /* i */
+       LIST_LONG | STYLE_LONG,      /* l */
+       STYLE_SINGLE,                /* 1 */
+       LIST_LONG | STYLE_LONG,      /* g (don't show owner) - handled via OPT_g. assumes l */
+       LIST_ID_NUMERIC | LIST_LONG | STYLE_LONG, /* n (assumes l) */
+       LIST_BLOCKS,                 /* s */
+       DISP_ROWS | STYLE_COLUMNAR,  /* x */
+       0,                           /* Q (quote filename) - handled via OPT_Q */
+       DISP_HIDDEN,                 /* A */
+       ENABLE_SELINUX * (LIST_CONTEXT|STYLE_SINGLE), /* k (ignored if !SELINUX) */
 #if ENABLE_FEATURE_LS_TIMESTAMPS
-       TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME),   /* c */
-       LIST_FULLTIME,              /* e */
-       ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME,   /* t */
-       TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME),   /* u */
+       TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
+       LIST_FULLTIME,               /* e */
+       ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
+       TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
 #endif
 #if ENABLE_FEATURE_LS_SORTFILES
-       SORT_SIZE,                  /* S */
-       SORT_EXT,                   /* X */
-       SORT_REVERSE,               /* r */
-       SORT_VERSION,               /* v */
+       SORT_SIZE,                   /* S */
+       SORT_EXT,                    /* X */
+       SORT_REVERSE,                /* r */
+       SORT_VERSION,                /* v */
 #endif
 #if ENABLE_FEATURE_LS_FILETYPES
-       LIST_FILETYPE | LIST_EXEC,  /* F */
-       LIST_FILETYPE,              /* p */
-#endif
-#if ENABLE_FEATURE_LS_FOLLOWLINKS
-       FOLLOW_LINKS,               /* L */
+       LIST_FILETYPE | LIST_CLASSIFY, /* F */
+       LIST_FILETYPE,               /* p */
 #endif
 #if ENABLE_FEATURE_LS_RECURSIVE
-       DISP_RECURSIVE,             /* R */
-#endif
-#if ENABLE_FEATURE_HUMAN_READABLE
-       LS_DISP_HR,                 /* h */
-#endif
-#if ENABLE_SELINUX
-       LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
+       DISP_RECURSIVE,              /* R */
 #endif
 #if ENABLE_SELINUX
-       LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
+       LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME|STYLE_SINGLE, /* K */
+       LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT|STYLE_SINGLE, /* Z */
 #endif
-       (1U<<31)
-       /* options after Z are not processed through opt_flags:
-        * T, w - ignored
-        */
+       (1U << 31)
+       /* options after Z are not processed through opt_flags */
 };
 
 
 /*
- * a directory entry and its stat info are stored here
+ * a directory entry and its stat info
  */
 struct dnode {
-       const char *name;       /* the dir entry name */
-       const char *fullname;   /* the dir entry name */
-       struct dnode *next;     /* point at the next node */
-       smallint fname_allocated;
-       struct stat dstat;      /* the file stat info */
+       const char *name;       /* usually basename, but think "ls -l dir/file" */
+       const char *fullname;   /* full name (usable for stat etc) */
+       struct dnode *dn_next;  /* for linked list */
        IF_SELINUX(security_context_t sid;)
+       smallint fname_allocated;
+
+       /* Used to avoid re-doing [l]stat at printout stage
+        * if we already collected needed data in scan stage:
+        */
+       mode_t    dn_mode_lstat;   /* obtained with lstat, or 0 */
+       mode_t    dn_mode_stat;    /* obtained with stat, or 0 */
+
+//     struct stat dstat;
+// struct stat is huge. We don't need it in full.
+// At least we don't need st_dev and st_blksize,
+// but there are invisible fields as well
+// (such as nanosecond-resolution timespamps)
+// and padding, which we also don't want to store.
+// We also can pre-parse dev_t dn_rdev (in glibc, it's huge).
+// On 32-bit uclibc: dnode size went from 112 to 84 bytes.
+//
+       /* Same names as in struct stat, but with dn_ instead of st_ pfx: */
+       mode_t    dn_mode; /* obtained with lstat OR stat, depending on -L etc */
+       off_t     dn_size;
+#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
+       time_t    dn_atime;
+       time_t    dn_mtime;
+       time_t    dn_ctime;
+#endif
+       ino_t     dn_ino;
+       blkcnt_t  dn_blocks;
+       nlink_t   dn_nlink;
+       uid_t     dn_uid;
+       gid_t     dn_gid;
+       int       dn_rdev_maj;
+       int       dn_rdev_min;
+//     dev_t     dn_dev;
+//     blksize_t dn_blksize;
 };
 
 struct globals {
 #if ENABLE_FEATURE_LS_COLOR
        smallint show_color;
+# define G_show_color (G.show_color)
+#else
+# define G_show_color 0
 #endif
        smallint exit_code;
        unsigned all_fmt;
 #if ENABLE_FEATURE_AUTOWIDTH
-       unsigned tabstops; // = COLUMN_GAP;
-       unsigned terminal_width; // = TERMINAL_WIDTH;
+       unsigned terminal_width;
+# define G_terminal_width (G.terminal_width)
+#else
+# define G_terminal_width TERMINAL_WIDTH
 #endif
 #if ENABLE_FEATURE_LS_TIMESTAMPS
        /* Do time() just once. Saves one syscall per file for "ls -l" */
@@ -257,77 +366,24 @@ struct globals {
 #endif
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
-#if ENABLE_FEATURE_LS_COLOR
-# define show_color     (G.show_color    )
-#else
-enum { show_color = 0 };
-#endif
-#define exit_code       (G.exit_code     )
-#define all_fmt         (G.all_fmt       )
-#if ENABLE_FEATURE_AUTOWIDTH
-# define tabstops       (G.tabstops      )
-# define terminal_width (G.terminal_width)
-#else
-enum {
-       tabstops = COLUMN_GAP,
-       terminal_width = TERMINAL_WIDTH,
-};
-#endif
-#define current_time_t (G.current_time_t)
 #define INIT_G() do { \
        /* we have to zero it out because of NOEXEC */ \
        memset(&G, 0, sizeof(G)); \
-       IF_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \
-       IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
-       IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
+       IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \
+       IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
 } while (0)
 
 
-static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
-{
-       struct stat dstat;
-       struct dnode *cur;
-       IF_SELINUX(security_context_t sid = NULL;)
+/*** Output code ***/
 
-       if ((all_fmt & FOLLOW_LINKS) || force_follow) {
-#if ENABLE_SELINUX
-               if (is_selinux_enabled())  {
-                        getfilecon(fullname, &sid);
-               }
-#endif
-               if (stat(fullname, &dstat)) {
-                       bb_simple_perror_msg(fullname);
-                       exit_code = EXIT_FAILURE;
-                       return 0;
-               }
-       } else {
-#if ENABLE_SELINUX
-               if (is_selinux_enabled()) {
-                       lgetfilecon(fullname, &sid);
-               }
-#endif
-               if (lstat(fullname, &dstat)) {
-                       bb_simple_perror_msg(fullname);
-                       exit_code = EXIT_FAILURE;
-                       return 0;
-               }
-       }
-
-       cur = xmalloc(sizeof(*cur));
-       cur->fullname = fullname;
-       cur->name = name;
-       cur->dstat = dstat;
-       IF_SELINUX(cur->sid = sid;)
-       return cur;
-}
 
 /* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
  * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
  *  3/7:multiplexed char/block device)
  * and we use 0 for unknown and 15 for executables (see below) */
 #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
-#define TYPECHAR(mode)  ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
-#define APPCHAR(mode)   ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
+/*                       un  fi chr -   dir -  blk  -  file -  link - sock -   - exe */
+#define APPCHAR(mode)   ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)])
 /* 036 black foreground              050 black background
    037 red foreground                051 red background
    040 green foreground              052 green background
@@ -338,7 +394,7 @@ static struct dnode *my_stat(const char *fullname, const char *name, int force_f
    045 gray foreground               057 white background
 */
 #define COLOR(mode) ( \
-       /*un  fi  chr     dir     blk     file    link    sock        exe */ \
+       /*un  fi  chr  -  dir  -  blk  -  file -  link -  sock -   -  exe */ \
        "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
        [TYPEINDEX(mode)])
 /* Select normal (0) [actually "reset all"] or bold (1)
@@ -347,7 +403,7 @@ static struct dnode *my_stat(const char *fullname, const char *name, int force_f
  * Note: coreutils 6.9 uses inverted red for setuid binaries.
  */
 #define ATTR(mode) ( \
-       /*un fi chr   dir   blk   file  link  sock     exe */ \
+       /*un fi chr - dir - blk - file- link- sock- -  exe */ \
        "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
        [TYPEINDEX(mode)])
 
@@ -367,14 +423,14 @@ static char bold(mode_t mode)
 }
 #endif
 
-#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
+#if ENABLE_FEATURE_LS_FILETYPES
 static char append_char(mode_t mode)
 {
-       if (!(all_fmt & LIST_FILETYPE))
+       if (!(G.all_fmt & LIST_FILETYPE))
                return '\0';
        if (S_ISDIR(mode))
                return '/';
-       if (!(all_fmt & LIST_EXEC))
+       if (!(G.all_fmt & LIST_CLASSIFY))
                return '\0';
        if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
                return '*';
@@ -382,149 +438,6 @@ static char append_char(mode_t mode)
 }
 #endif
 
-static unsigned count_dirs(struct dnode **dn, int which)
-{
-       unsigned dirs, all;
-
-       if (!dn)
-               return 0;
-
-       dirs = all = 0;
-       for (; *dn; dn++) {
-               const char *name;
-
-               all++;
-               if (!S_ISDIR((*dn)->dstat.st_mode))
-                       continue;
-               name = (*dn)->name;
-               if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
-                /* or if it's not . or .. */
-                || name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))
-               ) {
-                       dirs++;
-               }
-       }
-       return which != SPLIT_FILE ? dirs : all - dirs;
-}
-
-/* get memory to hold an array of pointers */
-static struct dnode **dnalloc(unsigned num)
-{
-       if (num < 1)
-               return NULL;
-
-       num++; /* so that we have terminating NULL */
-       return xzalloc(num * sizeof(struct dnode *));
-}
-
-#if ENABLE_FEATURE_LS_RECURSIVE
-static void dfree(struct dnode **dnp)
-{
-       unsigned i;
-
-       if (dnp == NULL)
-               return;
-
-       for (i = 0; dnp[i]; i++) {
-               struct dnode *cur = dnp[i];
-               if (cur->fname_allocated)
-                       free((char*)cur->fullname);
-               free(cur);
-       }
-       free(dnp);
-}
-#else
-#define dfree(...) ((void)0)
-#endif
-
-/* Returns NULL-terminated malloced vector of pointers (or NULL) */
-static struct dnode **splitdnarray(struct dnode **dn, int which)
-{
-       unsigned dncnt, d;
-       struct dnode **dnp;
-
-       if (dn == NULL)
-               return NULL;
-
-       /* count how many dirs or files there are */
-       dncnt = count_dirs(dn, which);
-
-       /* allocate a file array and a dir array */
-       dnp = dnalloc(dncnt);
-
-       /* copy the entrys into the file or dir array */
-       for (d = 0; *dn; dn++) {
-               if (S_ISDIR((*dn)->dstat.st_mode)) {
-                       const char *name;
-
-                       if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
-                               continue;
-                       name = (*dn)->name;
-                       if ((which & SPLIT_DIR)
-                        || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
-                       ) {
-                               dnp[d++] = *dn;
-                       }
-               } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
-                       dnp[d++] = *dn;
-               }
-       }
-       return dnp;
-}
-
-#if ENABLE_FEATURE_LS_SORTFILES
-static int sortcmp(const void *a, const void *b)
-{
-       struct dnode *d1 = *(struct dnode **)a;
-       struct dnode *d2 = *(struct dnode **)b;
-       unsigned sort_opts = all_fmt & SORT_MASK;
-       off_t dif;
-
-       dif = 0; /* assume SORT_NAME */
-       // TODO: use pre-initialized function pointer
-       // instead of branch forest
-       if (sort_opts == SORT_SIZE) {
-               dif = (d2->dstat.st_size - d1->dstat.st_size);
-       } else if (sort_opts == SORT_ATIME) {
-               dif = (d2->dstat.st_atime - d1->dstat.st_atime);
-       } else if (sort_opts == SORT_CTIME) {
-               dif = (d2->dstat.st_ctime - d1->dstat.st_ctime);
-       } else if (sort_opts == SORT_MTIME) {
-               dif = (d2->dstat.st_mtime - d1->dstat.st_mtime);
-       } else if (sort_opts == SORT_DIR) {
-               dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
-               /* } else if (sort_opts == SORT_VERSION) { */
-               /* } else if (sort_opts == SORT_EXT) { */
-       }
-       if (dif == 0) {
-               /* sort by name, or tie_breaker for other sorts */
-               if (ENABLE_LOCALE_SUPPORT)
-                       dif = strcoll(d1->name, d2->name);
-               else
-                       dif = strcmp(d1->name, d2->name);
-       }
-
-       /* Make dif fit into an int */
-       if (sizeof(dif) > sizeof(int)) {
-               enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
-               /* shift leaving only "int" worth of bits */
-               if (dif != 0) {
-                       dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
-               }
-       }
-
-       return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
-}
-
-static void dnsort(struct dnode **dn, int size)
-{
-       qsort(dn, size, sizeof(*dn), sortcmp);
-}
-#else
-#define dnsort(dn, size) ((void)0)
-#endif
-
-
 static unsigned calc_name_len(const char *name)
 {
        unsigned len;
@@ -547,9 +460,8 @@ static unsigned calc_name_len(const char *name)
        return len;
 }
 
-
 /* Return the number of used columns.
- * Note that only STYLE_COLUMNS uses return value.
+ * Note that only STYLE_COLUMNAR uses return value.
  * STYLE_SINGLE and STYLE_LONG don't care.
  * coreutils 7.2 also supports:
  * ls -b (--escape) = octal escapes (although it doesn't look like working)
@@ -575,99 +487,100 @@ static unsigned print_name(const char *name)
                        putchar('\\');
                        len++;
                }
-               putchar(*name++);
+               putchar(*name);
+               name++;
        }
        putchar('"');
        return len;
 }
 
 /* Return the number of used columns.
- * Note that only STYLE_COLUMNS uses return value,
+ * Note that only STYLE_COLUMNAR uses return value,
  * STYLE_SINGLE and STYLE_LONG don't care.
  */
-static NOINLINE unsigned list_single(const struct dnode *dn)
+static NOINLINE unsigned display_single(const struct dnode *dn)
 {
        unsigned column = 0;
-       char *lpath = lpath; /* for compiler */
+       char *lpath;
 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
-       struct stat info;
+       struct stat statbuf;
        char append;
 #endif
 
-       /* Never happens:
-       if (dn->fullname == NULL)
-               return 0;
-       */
-
 #if ENABLE_FEATURE_LS_FILETYPES
-       append = append_char(dn->dstat.st_mode);
+       append = append_char(dn->dn_mode);
 #endif
 
        /* Do readlink early, so that if it fails, error message
         * does not appear *inside* the "ls -l" line */
-       if (all_fmt & LIST_SYMLINK)
-               if (S_ISLNK(dn->dstat.st_mode))
+       lpath = NULL;
+       if (G.all_fmt & LIST_SYMLINK)
+               if (S_ISLNK(dn->dn_mode))
                        lpath = xmalloc_readlink_or_warn(dn->fullname);
 
-       if (all_fmt & LIST_INO)
-               column += printf("%7llu ", (long long) dn->dstat.st_ino);
-       if (all_fmt & LIST_BLOCKS)
-               column += printf("%4"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1));
-       if (all_fmt & LIST_MODEBITS)
-               column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
-       if (all_fmt & LIST_NLINKS)
-               column += printf("%4lu ", (long) dn->dstat.st_nlink);
+       if (G.all_fmt & LIST_INO)
+               column += printf("%7llu ", (long long) dn->dn_ino);
+//TODO: -h should affect -s too:
+       if (G.all_fmt & LIST_BLOCKS)
+               column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
+       if (G.all_fmt & LIST_MODEBITS)
+               column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
+       if (G.all_fmt & LIST_NLINKS)
+               column += printf("%4lu ", (long) dn->dn_nlink);
+       if (G.all_fmt & LIST_ID_NUMERIC) {
+               if (option_mask32 & OPT_g)
+                       column += printf("%-8u ", (int) dn->dn_gid);
+               else
+                       column += printf("%-8u %-8u ",
+                                       (int) dn->dn_uid,
+                                       (int) dn->dn_gid);
+       }
 #if ENABLE_FEATURE_LS_USERNAME
-       if (all_fmt & LIST_ID_NAME) {
+       else if (G.all_fmt & LIST_ID_NAME) {
                if (option_mask32 & OPT_g) {
                        column += printf("%-8.8s ",
-                               get_cached_username(dn->dstat.st_uid));
+                               get_cached_groupname(dn->dn_gid));
                } else {
                        column += printf("%-8.8s %-8.8s ",
-                               get_cached_username(dn->dstat.st_uid),
-                               get_cached_groupname(dn->dstat.st_gid));
+                               get_cached_username(dn->dn_uid),
+                               get_cached_groupname(dn->dn_gid));
                }
        }
 #endif
-       if (all_fmt & LIST_ID_NUMERIC) {
-               if (option_mask32 & OPT_g)
-                       column += printf("%-8u ", (int) dn->dstat.st_uid);
-               else
-                       column += printf("%-8u %-8u ",
-                                       (int) dn->dstat.st_uid,
-                                       (int) dn->dstat.st_gid);
-       }
-       if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
-               if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
+       if (G.all_fmt & LIST_SIZE) {
+               if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
                        column += printf("%4u, %3u ",
-                                       (int) major(dn->dstat.st_rdev),
-                                       (int) minor(dn->dstat.st_rdev));
+                                       dn->dn_rdev_maj,
+                                       dn->dn_rdev_min);
                } else {
-                       if (all_fmt & LS_DISP_HR) {
+                       if (option_mask32 & OPT_h) {
                                column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
-                                       /* print st_size, show one fractional, use suffixes */
-                                       make_human_readable_str(dn->dstat.st_size, 1, 0)
+                                       /* print size, show one fractional, use suffixes */
+                                       make_human_readable_str(dn->dn_size, 1, 0)
                                );
                        } else {
-                               column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
+                               column += printf("%9"OFF_FMT"u ", dn->dn_size);
                        }
                }
        }
 #if ENABLE_FEATURE_LS_TIMESTAMPS
-       if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
+       if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
                char *filetime;
-               time_t ttime = dn->dstat.st_mtime;
-               if (all_fmt & TIME_ACCESS)
-                       ttime = dn->dstat.st_atime;
-               if (all_fmt & TIME_CHANGE)
-                       ttime = dn->dstat.st_ctime;
+               time_t ttime = dn->dn_mtime;
+               if (G.all_fmt & TIME_ACCESS)
+                       ttime = dn->dn_atime;
+               if (G.all_fmt & TIME_CHANGE)
+                       ttime = dn->dn_ctime;
                filetime = ctime(&ttime);
                /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
-               if (all_fmt & LIST_FULLTIME)
+               if (G.all_fmt & LIST_FULLTIME) { /* -e */
+                       /* Note: coreutils 8.4 ls --full-time prints:
+                        * 2009-07-13 17:49:27.000000000 +0200
+                        */
                        column += printf("%.24s ", filetime);
-               else { /* LIST_DATE_TIME */
-                       /* current_time_t ~== time(NULL) */
-                       time_t age = current_time_t - ttime;
+               else { /* LIST_DATE_TIME */
+                       /* G.current_time_t ~== time(NULL) */
+                       time_t age = G.current_time_t - ttime;
                        printf("%.6s ", filetime + 4); /* "Jun 30" */
                        if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
                                /* hh:mm if less than 6 months old */
@@ -680,51 +593,52 @@ static NOINLINE unsigned list_single(const struct dnode *dn)
        }
 #endif
 #if ENABLE_SELINUX
-       if (all_fmt & LIST_CONTEXT) {
+       if (G.all_fmt & LIST_CONTEXT) {
                column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
                freecon(dn->sid);
        }
 #endif
-       if (all_fmt & LIST_FILENAME) {
+
 #if ENABLE_FEATURE_LS_COLOR
-               if (show_color) {
-                       info.st_mode = 0; /* for fgcolor() */
-                       lstat(dn->fullname, &info);
-                       printf("\033[%u;%um", bold(info.st_mode),
-                                       fgcolor(info.st_mode));
-               }
+       if (G_show_color) {
+               mode_t mode = dn->dn_mode_lstat;
+               if (!mode)
+                       if (lstat(dn->fullname, &statbuf) == 0)
+                               mode = statbuf.st_mode;
+               printf("\033[%u;%um", bold(mode), fgcolor(mode));
+       }
 #endif
-               column += print_name(dn->name);
-               if (show_color) {
-                       printf("\033[0m");
-               }
+       column += print_name(dn->name);
+       if (G_show_color) {
+               printf("\033[0m");
        }
-       if (all_fmt & LIST_SYMLINK) {
-               if (S_ISLNK(dn->dstat.st_mode) && lpath) {
-                       printf(" -> ");
+
+       if (lpath) {
+               printf(" -> ");
 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
-#if ENABLE_FEATURE_LS_COLOR
-                       info.st_mode = 0; /* for fgcolor() */
-#endif
-                       if (stat(dn->fullname, &info) == 0) {
-                               append = append_char(info.st_mode);
-                       }
-#endif
-#if ENABLE_FEATURE_LS_COLOR
-                       if (show_color) {
-                               printf("\033[%u;%um", bold(info.st_mode),
-                                          fgcolor(info.st_mode));
+               if ((G.all_fmt & LIST_FILETYPE) || G_show_color) {
+                       mode_t mode = dn->dn_mode_stat;
+                       if (!mode)
+                               if (stat(dn->fullname, &statbuf) == 0)
+                                       mode = statbuf.st_mode;
+# if ENABLE_FEATURE_LS_FILETYPES
+                       append = append_char(mode);
+# endif
+# if ENABLE_FEATURE_LS_COLOR
+                       if (G_show_color) {
+                               printf("\033[%u;%um", bold(mode), fgcolor(mode));
                        }
+# endif
+               }
 #endif
-                       column += print_name(lpath) + 4;
-                       if (show_color) {
-                               printf("\033[0m");
-                       }
-                       free(lpath);
+               column += print_name(lpath) + 4;
+               free(lpath);
+               if (G_show_color) {
+                       printf("\033[0m");
                }
        }
 #if ENABLE_FEATURE_LS_FILETYPES
-       if (all_fmt & LIST_FILETYPE) {
+       if (G.all_fmt & LIST_FILETYPE) {
                if (append) {
                        putchar(append);
                        column++;
@@ -735,14 +649,14 @@ static NOINLINE unsigned list_single(const struct dnode *dn)
        return column;
 }
 
-static void showfiles(struct dnode **dn, unsigned nfiles)
+static void display_files(struct dnode **dn, unsigned nfiles)
 {
        unsigned i, ncols, nrows, row, nc;
-       unsigned column = 0;
-       unsigned nexttab = 0;
-       unsigned column_width = 0; /* used only by STYLE_COLUMNS */
+       unsigned column;
+       unsigned nexttab;
+       unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
 
-       if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
+       if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
                ncols = 1;
        } else {
                /* find the longest file name, use that as the column width */
@@ -751,11 +665,11 @@ static void showfiles(struct dnode **dn, unsigned nfiles)
                        if (column_width < len)
                                column_width = len;
                }
-               column_width += tabstops +
-                       IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
-                               ((all_fmt & LIST_INO) ? 8 : 0) +
-                               ((all_fmt & LIST_BLOCKS) ? 5 : 0);
-               ncols = (int) (terminal_width / column_width);
+               column_width += 1 +
+                       IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + )
+                               ((G.all_fmt & LIST_INO) ? 8 : 0) +
+                               ((G.all_fmt & LIST_BLOCKS) ? 5 : 0);
+               ncols = (unsigned)G_terminal_width / column_width;
        }
 
        if (ncols > 1) {
@@ -767,21 +681,23 @@ static void showfiles(struct dnode **dn, unsigned nfiles)
                ncols = 1;
        }
 
+       column = 0;
+       nexttab = 0;
        for (row = 0; row < nrows; row++) {
                for (nc = 0; nc < ncols; nc++) {
                        /* reach into the array based on the column and row */
-                       if (all_fmt & DISP_ROWS)
+                       if (G.all_fmt & DISP_ROWS)
                                i = (row * ncols) + nc; /* display across row */
                        else
                                i = (nc * nrows) + row; /* display by column */
                        if (i < nfiles) {
                                if (column > 0) {
                                        nexttab -= column;
-                                       printf("%*s", nexttab, "");
-                                       column += nexttab;
+                                       printf("%*s ", nexttab, "");
+                                       column += nexttab + 1;
                                }
                                nexttab = column + column_width;
-                               column += list_single(dn[i]);
+                               column += display_single(dn[i]);
                        }
                }
                putchar('\n');
@@ -790,104 +706,241 @@ static void showfiles(struct dnode **dn, unsigned nfiles)
 }
 
 
-#if ENABLE_DESKTOP
-/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
- * If any of the -l, -n, -s options is specified, each list
- * of files within the directory shall be preceded by a
- * status line indicating the number of file system blocks
- * occupied by files in the directory in 512-byte units if
- * the -k option is not specified, or 1024-byte units if the
- * -k option is specified, rounded up to the next integral
- * number of units.
- */
-/* by Jorgen Overgaard (jorgen AT antistaten.se) */
-static off_t calculate_blocks(struct dnode **dn)
+/*** Dir scanning code ***/
+
+static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
 {
-       uoff_t blocks = 1;
-       if (dn) {
-               while (*dn) {
-                       /* st_blocks is in 512 byte blocks */
-                       blocks += (*dn)->dstat.st_blocks;
-                       dn++;
+       struct stat statbuf;
+       struct dnode *cur;
+
+       cur = xzalloc(sizeof(*cur));
+       cur->fullname = fullname;
+       cur->name = name;
+
+       if ((option_mask32 & OPT_L) || force_follow) {
+#if ENABLE_SELINUX
+               if (is_selinux_enabled())  {
+                       getfilecon(fullname, &cur->sid);
+               }
+#endif
+               if (stat(fullname, &statbuf)) {
+                       bb_simple_perror_msg(fullname);
+                       G.exit_code = EXIT_FAILURE;
+                       free(cur);
+                       return NULL;
+               }
+               cur->dn_mode_stat = statbuf.st_mode;
+       } else {
+#if ENABLE_SELINUX
+               if (is_selinux_enabled()) {
+                       lgetfilecon(fullname, &cur->sid);
                }
+#endif
+               if (lstat(fullname, &statbuf)) {
+                       bb_simple_perror_msg(fullname);
+                       G.exit_code = EXIT_FAILURE;
+                       free(cur);
+                       return NULL;
+               }
+               cur->dn_mode_lstat = statbuf.st_mode;
        }
 
-       /* Even though standard says use 512 byte blocks, coreutils use 1k */
-       /* Actually, we round up by calculating (blocks + 1) / 2,
-        * "+ 1" was done when we initialized blocks to 1 */
-       return blocks >> 1;
-}
+       /* cur->dstat = statbuf: */
+       cur->dn_mode   = statbuf.st_mode  ;
+       cur->dn_size   = statbuf.st_size  ;
+#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
+       cur->dn_atime  = statbuf.st_atime ;
+       cur->dn_mtime  = statbuf.st_mtime ;
+       cur->dn_ctime  = statbuf.st_ctime ;
 #endif
+       cur->dn_ino    = statbuf.st_ino   ;
+       cur->dn_blocks = statbuf.st_blocks;
+       cur->dn_nlink  = statbuf.st_nlink ;
+       cur->dn_uid    = statbuf.st_uid   ;
+       cur->dn_gid    = statbuf.st_gid   ;
+       cur->dn_rdev_maj = major(statbuf.st_rdev);
+       cur->dn_rdev_min = minor(statbuf.st_rdev);
 
+       return cur;
+}
 
-static struct dnode **list_dir(const char *, unsigned *);
-
-static void showdirs(struct dnode **dn, int first)
+static unsigned count_dirs(struct dnode **dn, int which)
 {
-       unsigned nfiles;
-       unsigned dndirs;
-       struct dnode **subdnp;
-       struct dnode **dnd;
+       unsigned dirs, all;
 
-       /* Never happens:
-       if (dn == NULL || ndirs < 1) {
-               return;
-       }
-       */
+       if (!dn)
+               return 0;
 
+       dirs = all = 0;
        for (; *dn; dn++) {
-               if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
-                       if (!first)
-                               bb_putchar('\n');
-                       first = 0;
-                       printf("%s:\n", (*dn)->fullname);
+               const char *name;
+
+               all++;
+               if (!S_ISDIR((*dn)->dn_mode))
+                       continue;
+
+               name = (*dn)->name;
+               if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
+                /* or if it's not . or .. */
+                || name[0] != '.'
+                || (name[1] && (name[1] != '.' || name[2]))
+               ) {
+                       dirs++;
                }
-               subdnp = list_dir((*dn)->fullname, &nfiles);
-#if ENABLE_DESKTOP
-               if ((all_fmt & STYLE_MASK) == STYLE_LONG)
-                       printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
+       }
+       return which != SPLIT_FILE ? dirs : all - dirs;
+}
+
+/* get memory to hold an array of pointers */
+static struct dnode **dnalloc(unsigned num)
+{
+       if (num < 1)
+               return NULL;
+
+       num++; /* so that we have terminating NULL */
+       return xzalloc(num * sizeof(struct dnode *));
+}
+
+#if ENABLE_FEATURE_LS_RECURSIVE
+static void dfree(struct dnode **dnp)
+{
+       unsigned i;
+
+       if (dnp == NULL)
+               return;
+
+       for (i = 0; dnp[i]; i++) {
+               struct dnode *cur = dnp[i];
+               if (cur->fname_allocated)
+                       free((char*)cur->fullname);
+               free(cur);
+       }
+       free(dnp);
+}
+#else
+#define dfree(...) ((void)0)
 #endif
-               if (nfiles > 0) {
-                       /* list all files at this level */
-                       dnsort(subdnp, nfiles);
-                       showfiles(subdnp, nfiles);
-                       if (ENABLE_FEATURE_LS_RECURSIVE
-                        && (all_fmt & DISP_RECURSIVE)
+
+/* Returns NULL-terminated malloced vector of pointers (or NULL) */
+static struct dnode **splitdnarray(struct dnode **dn, int which)
+{
+       unsigned dncnt, d;
+       struct dnode **dnp;
+
+       if (dn == NULL)
+               return NULL;
+
+       /* count how many dirs or files there are */
+       dncnt = count_dirs(dn, which);
+
+       /* allocate a file array and a dir array */
+       dnp = dnalloc(dncnt);
+
+       /* copy the entrys into the file or dir array */
+       for (d = 0; *dn; dn++) {
+               if (S_ISDIR((*dn)->dn_mode)) {
+                       const char *name;
+
+                       if (which == SPLIT_FILE)
+                               continue;
+
+                       name = (*dn)->name;
+                       if ((which & SPLIT_DIR) /* any dir... */
+                       /* ... or not . or .. */
+                        || name[0] != '.'
+                        || (name[1] && (name[1] != '.' || name[2]))
                        ) {
-                               /* recursive - list the sub-dirs */
-                               dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
-                               dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
-                               if (dndirs > 0) {
-                                       dnsort(dnd, dndirs);
-                                       showdirs(dnd, 0);
-                                       /* free the array of dnode pointers to the dirs */
-                                       free(dnd);
-                               }
+                               dnp[d++] = *dn;
                        }
-                       /* free the dnodes and the fullname mem */
-                       dfree(subdnp);
+               } else
+               if (which == SPLIT_FILE) {
+                       dnp[d++] = *dn;
+               }
+       }
+       return dnp;
+}
+
+#if ENABLE_FEATURE_LS_SORTFILES
+static int sortcmp(const void *a, const void *b)
+{
+       struct dnode *d1 = *(struct dnode **)a;
+       struct dnode *d2 = *(struct dnode **)b;
+       unsigned sort_opts = G.all_fmt & SORT_MASK;
+       off_t dif;
+
+       dif = 0; /* assume SORT_NAME */
+       // TODO: use pre-initialized function pointer
+       // instead of branch forest
+       if (sort_opts == SORT_SIZE) {
+               dif = (d2->dn_size - d1->dn_size);
+       } else
+       if (sort_opts == SORT_ATIME) {
+               dif = (d2->dn_atime - d1->dn_atime);
+       } else
+       if (sort_opts == SORT_CTIME) {
+               dif = (d2->dn_ctime - d1->dn_ctime);
+       } else
+       if (sort_opts == SORT_MTIME) {
+               dif = (d2->dn_mtime - d1->dn_mtime);
+       } else
+       if (sort_opts == SORT_DIR) {
+               dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
+       } else
+#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
+       if (sort_opts == SORT_VERSION) {
+               dif = strverscmp(d1->name, d2->name);
+       } else
+#endif
+       if (sort_opts == SORT_EXT) {
+               dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
+       }
+       if (dif == 0) {
+               /* sort by name, use as tie breaker for other sorts */
+               if (ENABLE_LOCALE_SUPPORT)
+                       dif = strcoll(d1->name, d2->name);
+               else
+                       dif = strcmp(d1->name, d2->name);
+       }
+
+       /* Make dif fit into an int */
+       if (sizeof(dif) > sizeof(int)) {
+               enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
+               /* shift leaving only "int" worth of bits */
+               if (dif != 0) {
+                       dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
                }
        }
+
+       return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
 }
 
+static void dnsort(struct dnode **dn, int size)
+{
+       qsort(dn, size, sizeof(*dn), sortcmp);
+}
+
+static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
+{
+       dnsort(dn, nfiles);
+       display_files(dn, nfiles);
+}
+#else
+# define dnsort(dn, size) ((void)0)
+# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
+#endif
 
 /* Returns NULL-terminated malloced vector of pointers (or NULL) */
-static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
+static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
 {
        struct dnode *dn, *cur, **dnp;
        struct dirent *entry;
        DIR *dir;
        unsigned i, nfiles;
 
-       /* Never happens:
-       if (path == NULL)
-               return NULL;
-       */
-
        *nfiles_p = 0;
        dir = warn_opendir(path);
        if (dir == NULL) {
-               exit_code = EXIT_FAILURE;
+               G.exit_code = EXIT_FAILURE;
                return NULL;    /* could not open the dir */
        }
        dn = NULL;
@@ -898,11 +951,11 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
                /* are we going to list the file- it may be . or .. or a hidden file */
                if (entry->d_name[0] == '.') {
                        if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
-                        && !(all_fmt & DISP_DOT)
+                        && !(G.all_fmt & DISP_DOT)
                        ) {
                                continue;
                        }
-                       if (!(all_fmt & DISP_HIDDEN))
+                       if (!(G.all_fmt & DISP_HIDDEN))
                                continue;
                }
                fullname = concat_path_file(path, entry->d_name);
@@ -912,7 +965,7 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
                        continue;
                }
                cur->fname_allocated = 1;
-               cur->next = dn;
+               cur->dn_next = dn;
                dn = cur;
                nfiles++;
        }
@@ -928,7 +981,7 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
        dnp = dnalloc(nfiles);
        for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
                dnp[i] = dn;    /* save pointer to node in array */
-               dn = dn->next;
+               dn = dn->dn_next;
                if (!dn)
                        break;
        }
@@ -936,6 +989,77 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
        return dnp;
 }
 
+#if ENABLE_DESKTOP
+/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
+ * If any of the -l, -n, -s options is specified, each list
+ * of files within the directory shall be preceded by a
+ * status line indicating the number of file system blocks
+ * occupied by files in the directory in 512-byte units if
+ * the -k option is not specified, or 1024-byte units if the
+ * -k option is specified, rounded up to the next integral
+ * number of units.
+ */
+/* by Jorgen Overgaard (jorgen AT antistaten.se) */
+static off_t calculate_blocks(struct dnode **dn)
+{
+       uoff_t blocks = 1;
+       if (dn) {
+               while (*dn) {
+                       /* st_blocks is in 512 byte blocks */
+                       blocks += (*dn)->dn_blocks;
+                       dn++;
+               }
+       }
+
+       /* Even though standard says use 512 byte blocks, coreutils use 1k */
+       /* Actually, we round up by calculating (blocks + 1) / 2,
+        * "+ 1" was done when we initialized blocks to 1 */
+       return blocks >> 1;
+}
+#endif
+
+static void scan_and_display_dirs_recur(struct dnode **dn, int first)
+{
+       unsigned nfiles;
+       struct dnode **subdnp;
+
+       for (; *dn; dn++) {
+               if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
+                       if (!first)
+                               bb_putchar('\n');
+                       first = 0;
+                       printf("%s:\n", (*dn)->fullname);
+               }
+               subdnp = scan_one_dir((*dn)->fullname, &nfiles);
+#if ENABLE_DESKTOP
+               if ((G.all_fmt & STYLE_MASK) == STYLE_LONG)
+                       printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
+#endif
+               if (nfiles > 0) {
+                       /* list all files at this level */
+                       sort_and_display_files(subdnp, nfiles);
+
+                       if (ENABLE_FEATURE_LS_RECURSIVE
+                        && (G.all_fmt & DISP_RECURSIVE)
+                       ) {
+                               struct dnode **dnd;
+                               unsigned dndirs;
+                               /* recursive - list the sub-dirs */
+                               dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
+                               dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
+                               if (dndirs > 0) {
+                                       dnsort(dnd, dndirs);
+                                       scan_and_display_dirs_recur(dnd, 0);
+                                       /* free the array of dnode pointers to the dirs */
+                                       free(dnd);
+                               }
+                       }
+                       /* free the dnodes and the fullname mem */
+                       dfree(subdnp);
+               }
+       }
+}
+
 
 int ls_main(int argc UNUSED_PARAM, char **argv)
 {
@@ -973,59 +1097,63 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
 
        init_unicode();
 
-       all_fmt = LIST_SHORT |
-               (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
+       if (ENABLE_FEATURE_LS_SORTFILES)
+               G.all_fmt = SORT_NAME;
 
 #if ENABLE_FEATURE_AUTOWIDTH
        /* obtain the terminal width */
-       get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
+       get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL);
        /* go one less... */
-       terminal_width--;
+       G_terminal_width--;
 #endif
 
        /* process options */
        IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
-#if ENABLE_FEATURE_AUTOWIDTH
-       opt_complementary = "T+:w+"; /* -T N, -w N */
-       opt = getopt32(argv, ls_options, &tabstops, &terminal_width
-                               IF_FEATURE_LS_COLOR(, &color_opt));
-#else
-       opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt));
-#endif
-       for (i = 0; opt_flags[i] != (1U<<31); i++) {
+       opt_complementary =
+               /* -e implies -l */
+               IF_FEATURE_LS_TIMESTAMPS("el")
+               /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
+                * in some pairs of opts, only last one takes effect:
+                */
+               IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
+               // ":m-l:l-m" - we don't have -m
+               IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
+               ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
+               ":C-1:1-C" /* bycols/oneline */
+               ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
+               IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
+               /* -w NUM: */
+               IF_FEATURE_AUTOWIDTH(":w+");
+       opt = getopt32(argv, ls_options
+               IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width)
+               IF_FEATURE_LS_COLOR(, &color_opt)
+       );
+       for (i = 0; opt_flags[i] != (1U << 31); i++) {
                if (opt & (1 << i)) {
-                       unsigned flags = opt_flags[i];
-
-                       if (flags & LIST_MASK_TRIGGER)
-                               all_fmt &= ~LIST_MASK;
-                       if (flags & STYLE_MASK_TRIGGER)
-                               all_fmt &= ~STYLE_MASK;
-                       if (flags & SORT_MASK_TRIGGER)
-                               all_fmt &= ~SORT_MASK;
-                       if (flags & DISP_MASK_TRIGGER)
-                               all_fmt &= ~DISP_MASK;
+                       uint32_t flags = opt_flags[i];
+
+                       if (flags & STYLE_MASK)
+                               G.all_fmt &= ~STYLE_MASK;
+                       if (flags & SORT_MASK)
+                               G.all_fmt &= ~SORT_MASK;
                        if (flags & TIME_MASK)
-                               all_fmt &= ~TIME_MASK;
-                       if (flags & LIST_CONTEXT)
-                               all_fmt |= STYLE_SINGLE;
-                       /* huh?? opt cannot be 'l' */
-                       //if (LS_DISP_HR && opt == 'l')
-                       //      all_fmt &= ~LS_DISP_HR;
-                       all_fmt |= flags;
+                               G.all_fmt &= ~TIME_MASK;
+
+                       G.all_fmt |= flags;
                }
        }
 
 #if ENABLE_FEATURE_LS_COLOR
-       /* find color bit value - last position for short getopt */
+       /* set G_show_color = 1/0 */
        if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
                char *p = getenv("LS_COLORS");
                /* LS_COLORS is unset, or (not empty && not "none") ? */
                if (!p || (p[0] && strcmp(p, "none") != 0))
-                       show_color = 1;
+                       G_show_color = 1;
        }
        if (opt & OPT_color) {
                if (color_opt[0] == 'n')
-                       show_color = 0;
+                       G_show_color = 0;
                else switch (index_in_substrings(color_str, color_opt)) {
                case 3:
                case 4:
@@ -1034,56 +1162,61 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
                case 0:
                case 1:
                case 2:
-                               show_color = 1;
+                               G_show_color = 1;
                        }
                }
        }
 #endif
 
        /* sort out which command line options take precedence */
-       if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
-               all_fmt &= ~DISP_RECURSIVE;     /* no recurse if listing only dir */
+       if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST))
+               G.all_fmt &= ~DISP_RECURSIVE;   /* no recurse if listing only dir */
        if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
-               if (all_fmt & TIME_CHANGE)
-                       all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
-               if (all_fmt & TIME_ACCESS)
-                       all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
+               if (G.all_fmt & TIME_CHANGE)
+                       G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME;
+               if (G.all_fmt & TIME_ACCESS)
+                       G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME;
        }
-       if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
-               all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
-       if (ENABLE_FEATURE_LS_USERNAME)
-               if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
-                       all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
+       if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
+               G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
 
-       /* choose a display format */
-       if (!(all_fmt & STYLE_MASK))
-               all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
+       /* choose a display format if one was not already specified by an option */
+       if (!(G.all_fmt & STYLE_MASK))
+               G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
 
        argv += optind;
        if (!argv[0])
                *--argv = (char*)".";
 
        if (argv[1])
-               all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
+               G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
 
        /* stuff the command line file names into a dnode array */
        dn = NULL;
        nfiles = 0;
        do {
-               /* NB: follow links on command line unless -l or -s */
-               cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
+               cur = my_stat(*argv, *argv,
+                       /* follow links on command line unless -l, -s or -F: */
+                       !((G.all_fmt & STYLE_MASK) == STYLE_LONG
+                         || (G.all_fmt & LIST_BLOCKS)
+                         || (option_mask32 & OPT_F)
+                       )
+                       /* ... or if -H: */
+                       || (option_mask32 & OPT_H)
+                       /* ... or if -L, but my_stat always follows links if -L */
+               );
                argv++;
                if (!cur)
                        continue;
-               cur->fname_allocated = 0;
-               cur->next = dn;
+               /*cur->fname_allocated = 0; - already is */
+               cur->dn_next = dn;
                dn = cur;
                nfiles++;
        } while (*argv);
 
        /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
        if (nfiles == 0)
-               return exit_code;
+               return G.exit_code;
 
        /* now that we know how many files there are
         * allocate memory for an array to hold dnode pointers
@@ -1091,33 +1224,32 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
        dnp = dnalloc(nfiles);
        for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
                dnp[i] = dn;    /* save pointer to node in array */
-               dn = dn->next;
+               dn = dn->dn_next;
                if (!dn)
                        break;
        }
 
-       if (all_fmt & DISP_NOLIST) {
-               dnsort(dnp, nfiles);
-               showfiles(dnp, nfiles);
+       if (G.all_fmt & DISP_NOLIST) {
+               sort_and_display_files(dnp, nfiles);
        } else {
                dnd = splitdnarray(dnp, SPLIT_DIR);
                dnf = splitdnarray(dnp, SPLIT_FILE);
                dndirs = count_dirs(dnp, SPLIT_DIR);
                dnfiles = nfiles - dndirs;
                if (dnfiles > 0) {
-                       dnsort(dnf, dnfiles);
-                       showfiles(dnf, dnfiles);
+                       sort_and_display_files(dnf, dnfiles);
                        if (ENABLE_FEATURE_CLEAN_UP)
                                free(dnf);
                }
                if (dndirs > 0) {
                        dnsort(dnd, dndirs);
-                       showdirs(dnd, dnfiles == 0);
+                       scan_and_display_dirs_recur(dnd, dnfiles == 0);
                        if (ENABLE_FEATURE_CLEAN_UP)
                                free(dnd);
                }
        }
+
        if (ENABLE_FEATURE_CLEAN_UP)
                dfree(dnp);
-       return exit_code;
+       return G.exit_code;
 }
index 3d50bb0..1a5342e 100644 (file)
@@ -3,22 +3,85 @@
  *  Copyright (C) 2003 Glenn L. McGrath
  *  Copyright (C) 2003-2004 Erik Andersen
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define md5sum_trivial_usage
+//usage:       IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
+//usage:#define md5sum_full_usage "\n\n"
+//usage:       "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " MD5 checksums"
+//usage:       IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n"
+//usage:     "\n       -c      Check sums against list in FILEs"
+//usage:     "\n       -s      Don't output anything, status code shows success"
+//usage:     "\n       -w      Warn about improperly formatted checksum lines"
+//usage:       )
+//usage:
+//usage:#define md5sum_example_usage
+//usage:       "$ md5sum < busybox\n"
+//usage:       "6fd11e98b98a58f64ff3398d7b324003\n"
+//usage:       "$ md5sum busybox\n"
+//usage:       "6fd11e98b98a58f64ff3398d7b324003  busybox\n"
+//usage:       "$ md5sum -c -\n"
+//usage:       "6fd11e98b98a58f64ff3398d7b324003  busybox\n"
+//usage:       "busybox: OK\n"
+//usage:       "^D\n"
+//usage:
+//usage:#define sha1sum_trivial_usage
+//usage:       IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
+//usage:#define sha1sum_full_usage "\n\n"
+//usage:       "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA1 checksums"
+//usage:       IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n"
+//usage:     "\n       -c      Check sums against list in FILEs"
+//usage:     "\n       -s      Don't output anything, status code shows success"
+//usage:     "\n       -w      Warn about improperly formatted checksum lines"
+//usage:       )
+//usage:
+//usage:#define sha256sum_trivial_usage
+//usage:       IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
+//usage:#define sha256sum_full_usage "\n\n"
+//usage:       "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA256 checksums"
+//usage:       IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n"
+//usage:     "\n       -c      Check sums against list in FILEs"
+//usage:     "\n       -s      Don't output anything, status code shows success"
+//usage:     "\n       -w      Warn about improperly formatted checksum lines"
+//usage:       )
+//usage:
+//usage:#define sha512sum_trivial_usage
+//usage:       IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
+//usage:#define sha512sum_full_usage "\n\n"
+//usage:       "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA512 checksums"
+//usage:       IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n"
+//usage:     "\n       -c      Check sums against list in FILEs"
+//usage:     "\n       -s      Don't output anything, status code shows success"
+//usage:     "\n       -w      Warn about improperly formatted checksum lines"
+//usage:       )
+//usage:
+//usage:#define sha3sum_trivial_usage
+//usage:       IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
+//usage:#define sha3sum_full_usage "\n\n"
+//usage:       "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA3-512 checksums"
+//usage:       IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n"
+//usage:     "\n       -c      Check sums against list in FILEs"
+//usage:     "\n       -s      Don't output anything, status code shows success"
+//usage:     "\n       -w      Warn about improperly formatted checksum lines"
+//usage:       )
+
 #include "libbb.h"
 
-typedef enum {
+/* This is a NOEXEC applet. Be very careful! */
+
+enum {
        /* 4th letter of applet_name is... */
        HASH_MD5 = 's', /* "md5>s<um" */
        HASH_SHA1 = '1',
        HASH_SHA256 = '2',
+       HASH_SHA3 = '3',
        HASH_SHA512 = '5',
-} hash_algo_t;
+};
 
-#define FLAG_SILENT    1
-#define FLAG_CHECK     2
-#define FLAG_WARN      4
+#define FLAG_SILENT  1
+#define FLAG_CHECK   2
+#define FLAG_WARN    4
 
 /* This might be useful elsewhere */
 static unsigned char *hash_bin_to_hex(unsigned char *hash_value,
@@ -30,27 +93,29 @@ static unsigned char *hash_bin_to_hex(unsigned char *hash_value,
        return (unsigned char *)hex_value;
 }
 
-static uint8_t *hash_file(const char *filename /*, hash_algo_t hash_algo*/)
+static uint8_t *hash_file(const char *filename)
 {
        int src_fd, hash_len, count;
        union _ctx_ {
+               sha3_ctx_t sha3;
                sha512_ctx_t sha512;
                sha256_ctx_t sha256;
                sha1_ctx_t sha1;
                md5_ctx_t md5;
        } context;
-       uint8_t *hash_value = NULL;
-       RESERVE_CONFIG_UBUFFER(in_buf, 4096);
-       void FAST_FUNC (*update)(const void*, size_t, void*);
+       uint8_t *hash_value;
+       void FAST_FUNC (*update)(void*, const void*, size_t);
        void FAST_FUNC (*final)(void*, void*);
-       hash_algo_t hash_algo = applet_name[3];
+       char hash_algo;
 
        src_fd = open_or_warn_stdin(filename);
        if (src_fd < 0) {
                return NULL;
        }
 
-       /* figure specific hash algorithims */
+       hash_algo = applet_name[3];
+
+       /* figure specific hash algorithms */
        if (ENABLE_MD5SUM && hash_algo == HASH_MD5) {
                md5_begin(&context.md5);
                update = (void*)md5_hash;
@@ -71,21 +136,30 @@ static uint8_t *hash_file(const char *filename /*, hash_algo_t hash_algo*/)
                update = (void*)sha512_hash;
                final = (void*)sha512_end;
                hash_len = 64;
+       } else if (ENABLE_SHA3SUM && hash_algo == HASH_SHA3) {
+               sha3_begin(&context.sha3);
+               update = (void*)sha3_hash;
+               final = (void*)sha3_end;
+               hash_len = 64;
        } else {
-               bb_error_msg_and_die("algorithm not supported");
-       }
-
-       while (0 < (count = safe_read(src_fd, in_buf, 4096))) {
-               update(in_buf, count, &context);
+               xfunc_die(); /* can't reach this */
        }
 
-       if (count == 0) {
-               final(in_buf, &context);
-               hash_value = hash_bin_to_hex(in_buf, hash_len);
+       {
+               RESERVE_CONFIG_UBUFFER(in_buf, 4096);
+               while ((count = safe_read(src_fd, in_buf, 4096)) > 0) {
+                       update(&context, in_buf, count);
+               }
+               hash_value = NULL;
+               if (count < 0)
+                       bb_perror_msg("can't read '%s'", filename);
+               else /* count == 0 */ {
+                       final(&context, in_buf);
+                       hash_value = hash_bin_to_hex(in_buf, hash_len);
+               }
+               RELEASE_CONFIG_BUFFER(in_buf);
        }
 
-       RELEASE_CONFIG_BUFFER(in_buf);
-
        if (src_fd != STDIN_FILENO) {
                close(src_fd);
        }
@@ -97,98 +171,90 @@ int md5_sha1_sum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv)
 {
        int return_value = EXIT_SUCCESS;
-       uint8_t *hash_value;
        unsigned flags;
-       /*hash_algo_t hash_algo = applet_name[3];*/
 
        if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK) {
                /* -b "binary", -t "text" are ignored (shaNNNsum compat) */
                flags = getopt32(argv, "scwbt");
+               argv += optind;
+               //argc -= optind;
+       } else {
+               argv += 1;
+               //argc -= 1;
        }
-       else optind = 1;
-       argv += optind;
-       //argc -= optind;
        if (!*argv)
                *--argv = (char*)"-";
 
        if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && !(flags & FLAG_CHECK)) {
                if (flags & FLAG_SILENT) {
-                       bb_error_msg_and_die
-                               ("-%c is meaningful only when verifying checksums", 's');
-               } else if (flags & FLAG_WARN) {
-                       bb_error_msg_and_die
-                               ("-%c is meaningful only when verifying checksums", 'w');
+                       bb_error_msg_and_die("-%c is meaningful only with -c", 's');
+               }
+               if (flags & FLAG_WARN) {
+                       bb_error_msg_and_die("-%c is meaningful only with -c", 'w');
                }
        }
 
-       if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) {
-               FILE *pre_computed_stream;
-               int count_total = 0;
-               int count_failed = 0;
-               char *line;
+       do {
+               if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) {
+                       FILE *pre_computed_stream;
+                       char *line;
+                       int count_total = 0;
+                       int count_failed = 0;
 
-               if (argv[1]) {
-                       bb_error_msg_and_die
-                               ("only one argument may be specified when using -c");
-               }
+                       pre_computed_stream = xfopen_stdin(*argv);
 
-               pre_computed_stream = xfopen_stdin(argv[0]);
+                       while ((line = xmalloc_fgetline(pre_computed_stream)) != NULL) {
+                               uint8_t *hash_value;
+                               char *filename_ptr;
 
-               while ((line = xmalloc_fgetline(pre_computed_stream)) != NULL) {
-                       char *filename_ptr;
+                               count_total++;
+                               filename_ptr = strstr(line, "  ");
+                               /* handle format for binary checksums */
+                               if (filename_ptr == NULL) {
+                                       filename_ptr = strstr(line, " *");
+                               }
+                               if (filename_ptr == NULL) {
+                                       if (flags & FLAG_WARN) {
+                                               bb_error_msg("invalid format");
+                                       }
+                                       count_failed++;
+                                       return_value = EXIT_FAILURE;
+                                       free(line);
+                                       continue;
+                               }
+                               *filename_ptr = '\0';
+                               filename_ptr += 2;
 
-                       count_total++;
-                       filename_ptr = strstr(line, "  ");
-                       /* handle format for binary checksums */
-                       if (filename_ptr == NULL) {
-                               filename_ptr = strstr(line, " *");
-                       }
-                       if (filename_ptr == NULL) {
-                               if (flags & FLAG_WARN) {
-                                       bb_error_msg("invalid format");
+                               hash_value = hash_file(filename_ptr);
+
+                               if (hash_value && (strcmp((char*)hash_value, line) == 0)) {
+                                       if (!(flags & FLAG_SILENT))
+                                               printf("%s: OK\n", filename_ptr);
+                               } else {
+                                       if (!(flags & FLAG_SILENT))
+                                               printf("%s: FAILED\n", filename_ptr);
+                                       count_failed++;
+                                       return_value = EXIT_FAILURE;
                                }
-                               count_failed++;
-                               return_value = EXIT_FAILURE;
+                               /* possible free(NULL) */
+                               free(hash_value);
                                free(line);
-                               continue;
                        }
-                       *filename_ptr = '\0';
-                       filename_ptr += 2;
-
-                       hash_value = hash_file(filename_ptr /*, hash_algo*/);
-
-                       if (hash_value && (strcmp((char*)hash_value, line) == 0)) {
-                               if (!(flags & FLAG_SILENT))
-                                       printf("%s: OK\n", filename_ptr);
-                       } else {
-                               if (!(flags & FLAG_SILENT))
-                                       printf("%s: FAILED\n", filename_ptr);
-                               count_failed++;
-                               return_value = EXIT_FAILURE;
+                       if (count_failed && !(flags & FLAG_SILENT)) {
+                               bb_error_msg("WARNING: %d of %d computed checksums did NOT match",
+                                               count_failed, count_total);
                        }
-                       /* possible free(NULL) */
-                       free(hash_value);
-                       free(line);
-               }
-               if (count_failed && !(flags & FLAG_SILENT)) {
-                       bb_error_msg("WARNING: %d of %d computed checksums did NOT match",
-                                                count_failed, count_total);
-               }
-               /*
-               if (fclose_if_not_stdin(pre_computed_stream) == EOF) {
-                       bb_perror_msg_and_die("can't close file %s", file_ptr);
-               }
-               */
-       } else {
-               do {
-                       hash_value = hash_file(*argv/*, hash_algo*/);
+                       fclose_if_not_stdin(pre_computed_stream);
+               } else {
+                       uint8_t *hash_value = hash_file(*argv);
                        if (hash_value == NULL) {
                                return_value = EXIT_FAILURE;
                        } else {
                                printf("%s  %s\n", hash_value, *argv);
                                free(hash_value);
                        }
-               } while (*++argv);
-       }
+               }
+       } while (*++argv);
+
        return return_value;
 }
index c837e97..4a8e43e 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
 /* Nov 28, 2006      Yoshinori Sato <ysato@users.sourceforge.jp>: Add SELinux Support.
  */
 
+//usage:#define mkdir_trivial_usage
+//usage:       "[OPTIONS] DIRECTORY..."
+//usage:#define mkdir_full_usage "\n\n"
+//usage:       "Create DIRECTORY\n"
+//usage:     "\n       -m MODE Mode"
+//usage:     "\n       -p      No error if exists; make parent directories as needed"
+//usage:       IF_SELINUX(
+//usage:     "\n       -Z      Set security context"
+//usage:       )
+//usage:
+//usage:#define mkdir_example_usage
+//usage:       "$ mkdir /tmp/foo\n"
+//usage:       "$ mkdir /tmp/foo\n"
+//usage:       "/tmp/foo: File exists\n"
+//usage:       "$ mkdir /tmp/foo/bar/baz\n"
+//usage:       "/tmp/foo/bar/baz: No such file or directory\n"
+//usage:       "$ mkdir -p /tmp/foo/bar/baz\n"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
@@ -30,13 +48,14 @@ static const char mkdir_longopts[] ALIGN1 =
 #if ENABLE_SELINUX
        "context\0" Required_argument "Z"
 #endif
+       "verbose\0" No_argument       "v"
        ;
 #endif
 
 int mkdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int mkdir_main(int argc UNUSED_PARAM, char **argv)
 {
-       mode_t mode = (mode_t)(-1);
+       long mode = -1;
        int status = EXIT_SUCCESS;
        int flags = 0;
        unsigned opt;
@@ -48,12 +67,13 @@ int mkdir_main(int argc UNUSED_PARAM, char **argv)
 #if ENABLE_FEATURE_MKDIR_LONG_OPTIONS
        applet_long_options = mkdir_longopts;
 #endif
-       opt = getopt32(argv, "m:p" IF_SELINUX("Z:"), &smode IF_SELINUX(,&scontext));
+       opt = getopt32(argv, "m:p" IF_SELINUX("Z:") "v", &smode IF_SELINUX(,&scontext));
        if (opt & 1) {
-               mode = 0777;
-               if (!bb_parse_mode(smode, &mode)) {
+               mode_t mmode = 0777;
+               if (!bb_parse_mode(smode, &mmode)) {
                        bb_error_msg_and_die("invalid mode '%s'", smode);
                }
+               mode = mmode;
        }
        if (opt & 2)
                flags |= FILEUTILS_RECUR;
index 6549460..ef58325 100644 (file)
@@ -4,15 +4,26 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/mkfifo.html */
 
+//usage:#define mkfifo_trivial_usage
+//usage:       "[-m MODE] " IF_SELINUX("[-Z] ") "NAME"
+//usage:#define mkfifo_full_usage "\n\n"
+//usage:       "Create named pipe\n"
+//usage:     "\n       -m MODE Mode (default a=rw)"
+//usage:       IF_SELINUX(
+//usage:     "\n       -Z      Set security context"
+//usage:       )
+
 #include "libbb.h"
 #include "libcoreutils/coreutils.h"
 
+/* This is a NOEXEC applet. Be very careful! */
+
 int mkfifo_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int mkfifo_main(int argc UNUSED_PARAM, char **argv)
 {
@@ -28,7 +39,7 @@ int mkfifo_main(int argc UNUSED_PARAM, char **argv)
 
        do {
                if (mkfifo(*argv, mode) < 0) {
-                       bb_simple_perror_msg(*argv);    /* Avoid multibyte problems. */
+                       bb_simple_perror_msg(*argv);  /* Avoid multibyte problems. */
                        retval = EXIT_FAILURE;
                }
        } while (*++argv);
index 0c69494..aa04504 100644 (file)
@@ -4,16 +4,35 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
 
+//usage:#define mknod_trivial_usage
+//usage:       "[-m MODE] " IF_SELINUX("[-Z] ") "NAME TYPE MAJOR MINOR"
+//usage:#define mknod_full_usage "\n\n"
+//usage:       "Create a special file (block, character, or pipe)\n"
+//usage:     "\n       -m MODE Creation mode (default a=rw)"
+//usage:       IF_SELINUX(
+//usage:     "\n       -Z      Set security context"
+//usage:       )
+//usage:     "\nTYPE:"
+//usage:     "\n       b       Block device"
+//usage:     "\n       c or u  Character device"
+//usage:     "\n       p       Named pipe (MAJOR and MINOR are ignored)"
+//usage:
+//usage:#define mknod_example_usage
+//usage:       "$ mknod /dev/fd0 b 2 0\n"
+//usage:       "$ mknod -m 644 /tmp/pipe p\n"
+
 #include <sys/sysmacros.h>  // For makedev
 
 #include "libbb.h"
 #include "libcoreutils/coreutils.h"
 
+/* This is a NOEXEC applet. Be very careful! */
+
 static const char modes_chars[] ALIGN1 = { 'p', 'c', 'u', 'b', 0, 1, 1, 2 };
 static const mode_t modes_cubp[] = { S_IFIFO, S_IFCHR, S_IFBLK };
 
@@ -40,7 +59,7 @@ int mknod_main(int argc, char **argv)
                                        /* Autodetect what the system supports; these macros should
                                         * optimize out to two constants. */
                                        dev = makedev(xatoul_range(argv[2], 0, major(UINT_MAX)),
-                                                     xatoul_range(argv[3], 0, minor(UINT_MAX)));
+                                                       xatoul_range(argv[3], 0, minor(UINT_MAX)));
                                }
                        }
 
index 1c5a467..f127dfa 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
  * SELinux support by Yuichi Nakamura <ynakam@hitachisoft.jp>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
 #include "libbb.h"
 #include "libcoreutils/coreutils.h"
 
+//usage:#define mv_trivial_usage
+//usage:       "[-fin] SOURCE DEST\n"
+//usage:       "or: mv [-fin] SOURCE... DIRECTORY"
+//usage:#define mv_full_usage "\n\n"
+//usage:       "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY\n"
+//usage:     "\n       -f      Don't prompt before overwriting"
+//usage:     "\n       -i      Interactive, prompt before overwrite"
+//usage:     "\n       -n      Don't overwrite an existing file"
+//usage:
+//usage:#define mv_example_usage
+//usage:       "$ mv /tmp/foo /bin/bar\n"
+
 #if ENABLE_FEATURE_MV_LONG_OPTIONS
 static const char mv_longopts[] ALIGN1 =
        "interactive\0" No_argument "i"
        "force\0"       No_argument "f"
+       "no-clobber\0"  No_argument "n"
+       "verbose\0"     No_argument "v"
        ;
 #endif
 
-#define OPT_FILEUTILS_FORCE       1
-#define OPT_FILEUTILS_INTERACTIVE 2
+#define OPT_FORCE       (1 << 0)
+#define OPT_INTERACTIVE (1 << 1)
+#define OPT_NOCLOBBER   (1 << 2)
 
 int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int mv_main(int argc, char **argv)
@@ -40,10 +55,13 @@ int mv_main(int argc, char **argv)
 #if ENABLE_FEATURE_MV_LONG_OPTIONS
        applet_long_options = mv_longopts;
 #endif
-       // Need at least two arguments
-       // -f unsets -i, -i unsets -f
-       opt_complementary = "-2:f-i:i-f";
-       flags = getopt32(argv, "fi");
+       /* Need at least two arguments.
+        * If more than one of -f, -i, -n is specified , only the final one
+        * takes effect (it unsets previous options).
+        * -v is accepted but ignored.
+        */
+       opt_complementary = "-2:f-in:i-fn:n-fi";
+       flags = getopt32(argv, "finv");
        argc -= optind;
        argv += optind;
        last = argv[argc - 1];
@@ -68,18 +86,22 @@ int mv_main(int argc, char **argv)
                }
 
  DO_MOVE:
-               if (dest_exists
-                && !(flags & OPT_FILEUTILS_FORCE)
-                && ((access(dest, W_OK) < 0 && isatty(0))
-                   || (flags & OPT_FILEUTILS_INTERACTIVE))
-               ) {
-                       if (fprintf(stderr, "mv: overwrite '%s'? ", dest) < 0) {
-                               goto RET_1;     /* Ouch! fprintf failed! */
-                       }
-                       if (!bb_ask_confirmation()) {
+               if (dest_exists) {
+                       if (flags & OPT_NOCLOBBER)
                                goto RET_0;
+                       if (!(flags & OPT_FORCE)
+                        && ((access(dest, W_OK) < 0 && isatty(0))
+                           || (flags & OPT_INTERACTIVE))
+                       ) {
+                               if (fprintf(stderr, "mv: overwrite '%s'? ", dest) < 0) {
+                                       goto RET_1;  /* Ouch! fprintf failed! */
+                               }
+                               if (!bb_ask_confirmation()) {
+                                       goto RET_0;
+                               }
                        }
                }
+
                if (rename(*argv, dest) < 0) {
                        struct stat source_stat;
                        int source_exists;
index ff3eb11..ce75991 100644 (file)
@@ -4,9 +4,15 @@
  *
  * Copyright (C) 2005  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define nice_trivial_usage
+//usage:       "[-n ADJUST] [PROG ARGS]"
+//usage:#define nice_full_usage "\n\n"
+//usage:       "Change scheduling priority, run PROG\n"
+//usage:     "\n       -n ADJUST       Adjust priority by ADJUST"
+
 #include <sys/resource.h>
 #include "libbb.h"
 
@@ -17,12 +23,12 @@ int nice_main(int argc, char **argv)
 
        old_priority = getpriority(PRIO_PROCESS, 0);
 
-       if (!*++argv) { /* No args, so (GNU) output current nice value. */
+       if (!*++argv) { /* No args, so (GNU) output current nice value. */
                printf("%d\n", old_priority);
                fflush_stdout_and_exit(EXIT_SUCCESS);
        }
 
-       adjustment = 10;                        /* Set default adjustment. */
+       adjustment = 10;  /* Set default adjustment. */
 
        if (argv[0][0] == '-') {
                if (argv[0][1] == 'n') { /* -n */
@@ -32,7 +38,7 @@ int nice_main(int argc, char **argv)
                } else { /* -NNN (NNN may be negative) == -n NNN */
                        argv[0] += 1; argv--; argc++;
                }
-               if (argc < 4) {                 /* Missing priority and/or utility! */
+               if (argc < 4) {  /* Missing priority and/or utility! */
                        bb_show_usage();
                }
                adjustment = xatoi_range(argv[1], INT_MIN/2, INT_MAX/2);
index 3dc5314..63853fd 100644 (file)
@@ -7,9 +7,17 @@
  * Copyright 2006 Rob Landley <rob@landley.net>
  * Copyright 2006 Bernhard Reutner-Fischer
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define nohup_trivial_usage
+//usage:       "PROG ARGS"
+//usage:#define nohup_full_usage "\n\n"
+//usage:       "Run PROG immune to hangups, with output to a non-tty"
+//usage:
+//usage:#define nohup_example_usage
+//usage:       "$ nohup make &"
+
 #include "libbb.h"
 
 /* Compat info: nohup (GNU coreutils 6.8) does this:
index 228db19..fb11fcf 100644 (file)
@@ -4,13 +4,19 @@
  * Based on code from util-linux v 2.11l
  *
  * Copyright (c) 1990
- *     The Regents of the University of California.  All rights reserved.
+ * The Regents of the University of California.  All rights reserved.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Original copyright notice is retained at the end of this file.
  */
 
+//usage:#if !ENABLE_DESKTOP
+//usage:#define od_trivial_usage
+//usage:       "[-aBbcDdeFfHhIiLlOovXx] [FILE]"
+//usage:#define od_full_usage "\n\n"
+//usage:       "Print FILE (or stdin) unambiguously, as octal bytes by default"
+//usage:#endif
 
 #include "libbb.h"
 #if ENABLE_DESKTOP
@@ -110,7 +116,7 @@ odoffset(dumper_t *dumper, int argc, char ***argvp)
                         * the offset is changed as well.  This isn't pretty,
                         * but it's easy.
                         */
-#define        TYPE_OFFSET     7
+#define TYPE_OFFSET 7
                        {
                                char x_or_d;
                                if (base == 16) {
@@ -130,19 +136,19 @@ odoffset(dumper_t *dumper, int argc, char ***argvp)
 }
 
 static const char *const add_strings[] = {
-       "16/1 \"%3_u \" \"\\n\"",                               /* a */
-       "8/2 \" %06o \" \"\\n\"",                               /* B, o */
-       "16/1 \"%03o \" \"\\n\"",                               /* b */
-       "16/1 \"%3_c \" \"\\n\"",                               /* c */
-       "8/2 \"  %05u \" \"\\n\"",                              /* d */
-       "4/4 \"     %010u \" \"\\n\"",                  /* D */
-       "2/8 \"          %21.14e \" \"\\n\"",   /* e (undocumented in od), F */
-       "4/4 \" %14.7e \" \"\\n\"",                             /* f */
-       "4/4 \"       %08x \" \"\\n\"",                 /* H, X */
-       "8/2 \"   %04x \" \"\\n\"",                             /* h, x */
-       "4/4 \"    %11d \" \"\\n\"",                    /* I, L, l */
-       "8/2 \" %6d \" \"\\n\"",                                /* i */
-       "4/4 \"    %011o \" \"\\n\"",                   /* O */
+       "16/1 \"%3_u \" \"\\n\"",              /* a */
+       "8/2 \" %06o \" \"\\n\"",              /* B, o */
+       "16/1 \"%03o \" \"\\n\"",              /* b */
+       "16/1 \"%3_c \" \"\\n\"",              /* c */
+       "8/2 \"  %05u \" \"\\n\"",             /* d */
+       "4/4 \"     %010u \" \"\\n\"",         /* D */
+       "2/8 \"          %21.14e \" \"\\n\"",  /* e (undocumented in od), F */
+       "4/4 \" %14.7e \" \"\\n\"",            /* f */
+       "4/4 \"       %08x \" \"\\n\"",        /* H, X */
+       "8/2 \"   %04x \" \"\\n\"",            /* h, x */
+       "4/4 \"    %11d \" \"\\n\"",           /* I, L, l */
+       "8/2 \" %6d \" \"\\n\"",               /* i */
+       "4/4 \"    %011o \" \"\\n\"",          /* O */
 };
 
 static const char od_opts[] ALIGN1 = "aBbcDdeFfHhIiLlOoXxv";
@@ -174,7 +180,7 @@ int od_main(int argc, char **argv)
                                bb_dump_add(dumper, "\"         \"");
                        }
                        bb_dump_add(dumper, add_strings[(int)od_o2si[(p - od_opts)]]);
-               } else {        /* P, p, s, w, or other unhandled */
+               } else {  /* P, p, s, w, or other unhandled */
                        bb_show_usage();
                }
        }
index a9a45c8..2c26dda 100644 (file)
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software Foundation,
-   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
-
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
 /* Written by Jim Meyering.  */
+/* Busyboxed by Denys Vlasenko, based on od.c from coreutils-5.2.1 */
 
-/* Busyboxed by Denys Vlasenko
-
-Based on od.c from coreutils-5.2.1
-Top bloat sources:
 
-00000073 t parse_old_offset
-0000007b t get_lcm
-00000090 r long_options
-00000092 t print_named_ascii
-000000bf t print_ascii
-00000168 t write_block
-00000366 t decode_format_string
-00000a71 T od_main
-
-Tested for compat with coreutils 6.3
-using this script. Minor differences fixed.
+/* #include "libbb.h" - done in od.c */
+#define assert(a) ((void)0)
 
-#!/bin/sh
-echo STD
-time /path/to/coreutils/od \
-...params... \
->std
-echo Exit code $?
-echo BBOX
-time ./busybox od \
-...params... \
->bbox
-echo Exit code $?
-diff -u -a std bbox >bbox.diff || { echo Different!; sleep 1; }
 
-*/
+//usage:#if ENABLE_DESKTOP
+//usage:#define od_trivial_usage
+//usage:       "[-abcdfhilovxs] [-t TYPE] [-A RADIX] [-N SIZE] [-j SKIP] [-S MINSTR] [-w WIDTH] [FILE]..."
+// We don't support:
+// ... [FILE] [[+]OFFSET[.][b]]
+// Support is buggy for:
+// od --traditional [OPTION]... [FILE] [[+]OFFSET[.][b] [+][LABEL][.][b]]
+
+//usage:#define od_full_usage "\n\n"
+//usage:       "Print FILEs (or stdin) unambiguously, as octal bytes by default"
+//usage:#endif
+
+enum {
+       OPT_A = 1 << 0,
+       OPT_N = 1 << 1,
+       OPT_a = 1 << 2,
+       OPT_b = 1 << 3,
+       OPT_c = 1 << 4,
+       OPT_d = 1 << 5,
+       OPT_f = 1 << 6,
+       OPT_h = 1 << 7,
+       OPT_i = 1 << 8,
+       OPT_j = 1 << 9,
+       OPT_l = 1 << 10,
+       OPT_o = 1 << 11,
+       OPT_t = 1 << 12,
+       /* When zero and two or more consecutive blocks are equal, format
+          only the first block and output an asterisk alone on the following
+          line to indicate that identical blocks have been elided: */
+       OPT_v = 1 << 13,
+       OPT_x = 1 << 14,
+       OPT_s = 1 << 15,
+       OPT_S = 1 << 16,
+       OPT_w = 1 << 17,
+       OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS,
+};
 
-#include "libbb.h"
+#define OD_GETOPT32() getopt32(argv, \
+       "A:N:abcdfhij:lot:vxsS:w::", \
+       /* -w with optional param */ \
+       /* -S was -s and also had optional parameter */ \
+       /* but in coreutils 6.3 it was renamed and now has */ \
+       /* _mandatory_ parameter */ \
+       &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block)
 
-#define assert(a) ((void)0)
 
 /* Check for 0x7f is a coreutils 6.3 addition */
-#define ISPRINT(c) (((c)>=' ') && (c) != 0x7f)
+#define ISPRINT(c) (((c) >= ' ') && (c) < 0x7f)
 
 typedef long double longdouble_t;
 typedef unsigned long long ulonglong_t;
@@ -158,17 +174,9 @@ struct ERR_width_bytes_has_bad_size {
        char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1];
 };
 
-static smallint flag_dump_strings;
-/* Non-zero if an old-style 'pseudo-address' was specified.  */
-static smallint flag_pseudo_start;
-static smallint limit_bytes_to_format;
-/* When zero and two or more consecutive blocks are equal, format
-   only the first block and output an asterisk alone on the following
-   line to indicate that identical blocks have been elided.  */
-static smallint verbose;
-static smallint ioerror;
+static smallint exit_code;
 
-static size_t string_min;
+static unsigned string_min;
 
 /* An array of specs describing how to format each input block.  */
 static size_t n_specs;
@@ -179,7 +187,11 @@ static struct tspec *spec;
 static void (*format_address)(off_t, char);
 /* The difference between the old-style pseudo starting address and
    the number of bytes to skip.  */
+#if ENABLE_LONG_OPTS
 static off_t pseudo_offset;
+#else
+enum { pseudo_offset = 0 };
+#endif
 /* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all
    input is formatted.  */
 
@@ -214,7 +226,7 @@ static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1
 
 #define MAX_FP_TYPE_SIZE sizeof(longdouble_t)
 static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = {
-       /* gcc seems to allow repeated indexes. Last one stays */
+       /* gcc seems to allow repeated indexes. Last one wins */
        [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE,
        [sizeof(double)] = FLOAT_DOUBLE,
        [sizeof(float)] = FLOAT_SINGLE
@@ -376,7 +388,7 @@ print_named_ascii(size_t n_bytes, const char *block,
        };
        // buf[N] pos:  01234 56789
        char buf[12] = "   x\0 0xx\0";
-       // actually "   x\0 xxx\0", but I want to share the string with below.
+       // actually "   x\0 xxx\0", but want to share string with print_ascii.
        // [12] because we take three 32bit stack slots anyway, and
        // gcc is too dumb to initialize with constant stores,
        // it copies initializer from rodata. Oh well.
@@ -472,10 +484,10 @@ open_next_file(void)
                if (in_stream) {
                        break;
                }
-               ioerror = 1;
+               exit_code = 1;
        }
 
-       if (limit_bytes_to_format && !flag_dump_strings)
+       if ((option_mask32 & (OPT_N|OPT_S)) == OPT_N)
                setbuf(in_stream, NULL);
 }
 
@@ -495,15 +507,14 @@ check_and_close(void)
                                        ? bb_msg_standard_input
                                        : file_list[-1]
                        );
-                       ioerror = 1;
+                       exit_code = 1;
                }
                fclose_if_not_stdin(in_stream);
                in_stream = NULL;
        }
 
        if (ferror(stdout)) {
-               bb_error_msg(bb_msg_write_error);
-               ioerror = 1;
+               bb_error_msg_and_die(bb_msg_write_error);
        }
 }
 
@@ -535,7 +546,6 @@ decode_one_format(const char *s_orig, const char *s, struct tspec *tspec)
        unsigned field_width = 0;
        int pos;
 
-
        switch (*s) {
        case 'd':
        case 'o':
@@ -781,7 +791,7 @@ skip(off_t n_skip)
                                /* take "check & close / open_next" route */
                        } else {
                                if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
-                                       ioerror = 1;
+                                       exit_code = 1;
                                return;
                        }
                } else {
@@ -882,7 +892,8 @@ write_block(off_t current_offset, size_t n_bytes,
        static char prev_pair_equal = 0;
        size_t i;
 
-       if (!verbose && !first
+       if (!(option_mask32 & OPT_v)
+        && !first
         && n_bytes == bytes_per_block
         && memcmp(prev_block, curr_block, bytes_per_block) == 0
        ) {
@@ -904,9 +915,9 @@ write_block(off_t current_offset, size_t n_bytes,
                        (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
                        if (spec[i].hexl_mode_trailer) {
                                /* space-pad out to full line width, then dump the trailer */
-                               int datum_width = width_bytes[spec[i].size];
-                               int blank_fields = (bytes_per_block - n_bytes) / datum_width;
-                               int field_width = spec[i].field_width + 1;
+                               unsigned datum_width = width_bytes[spec[i].size];
+                               unsigned blank_fields = (bytes_per_block - n_bytes) / datum_width;
+                               unsigned field_width = spec[i].field_width + 1;
                                printf("%*s", blank_fields * field_width, "");
                                dump_hexl_mode_trailer(n_bytes, curr_block);
                        }
@@ -954,42 +965,6 @@ get_lcm(void)
        return l_c_m;
 }
 
-#if ENABLE_LONG_OPTS
-/* If S is a valid traditional offset specification with an optional
-   leading '+' return nonzero and set *OFFSET to the offset it denotes.  */
-
-static int
-parse_old_offset(const char *s, off_t *offset)
-{
-       static const struct suffix_mult Bb[] = {
-               { "B", 1024 },
-               { "b", 512 },
-               { "", 0 }
-       };
-       char *p;
-       int radix;
-
-       /* Skip over any leading '+'. */
-       if (s[0] == '+') ++s;
-
-       /* Determine the radix we'll use to interpret S.  If there is a '.',
-        * it's decimal, otherwise, if the string begins with '0X'or '0x',
-        * it's hexadecimal, else octal.  */
-       p = strchr(s, '.');
-       radix = 8;
-       if (p) {
-               p[0] = '\0'; /* cheating */
-               radix = 10;
-       } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
-               radix = 16;
-
-       *offset = xstrtooff_sfx(s, radix, Bb);
-       if (p) p[0] = '.';
-
-       return (*offset >= 0);
-}
-#endif
-
 /* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
    formatted block to standard output, and repeat until the specified
    maximum number of bytes has been read or until all input has been
@@ -1007,27 +982,25 @@ dump(off_t current_offset, off_t end_offset)
        int idx;
        size_t n_bytes_read;
 
-       block[0] = xmalloc(2*bytes_per_block);
+       block[0] = xmalloc(2 * bytes_per_block);
        block[1] = block[0] + bytes_per_block;
 
        idx = 0;
-       if (limit_bytes_to_format) {
+       if (option_mask32 & OPT_N) {
                while (1) {
                        size_t n_needed;
                        if (current_offset >= end_offset) {
                                n_bytes_read = 0;
                                break;
                        }
-                       n_needed = MIN(end_offset - current_offset,
-                               (off_t) bytes_per_block);
+                       n_needed = MIN(end_offset - current_offset, (off_t) bytes_per_block);
                        read_block(n_needed, block[idx], &n_bytes_read);
                        if (n_bytes_read < bytes_per_block)
                                break;
                        assert(n_bytes_read == bytes_per_block);
-                       write_block(current_offset, n_bytes_read,
-                              block[!idx], block[idx]);
+                       write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]);
                        current_offset += n_bytes_read;
-                       idx = !idx;
+                       idx ^= 1;
                }
        } else {
                while (1) {
@@ -1035,10 +1008,9 @@ dump(off_t current_offset, off_t end_offset)
                        if (n_bytes_read < bytes_per_block)
                                break;
                        assert(n_bytes_read == bytes_per_block);
-                       write_block(current_offset, n_bytes_read,
-                              block[!idx], block[idx]);
+                       write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]);
                        current_offset += n_bytes_read;
-                       idx = !idx;
+                       idx ^= 1;
                }
        }
 
@@ -1049,46 +1021,23 @@ dump(off_t current_offset, off_t end_offset)
                l_c_m = get_lcm();
 
                /* Make bytes_to_write the smallest multiple of l_c_m that
-                        is at least as large as n_bytes_read.  */
+                  is at least as large as n_bytes_read.  */
                bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
 
                memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
                write_block(current_offset, bytes_to_write,
-                                  block[!idx], block[idx]);
+                               block[idx ^ 1], block[idx]);
                current_offset += n_bytes_read;
        }
 
        format_address(current_offset, '\n');
 
-       if (limit_bytes_to_format && current_offset >= end_offset)
+       if ((option_mask32 & OPT_N) && current_offset >= end_offset)
                check_and_close();
 
        free(block[0]);
 }
 
-/* Read a single byte into *C from the concatenation of the input files
-   named in the global array FILE_LIST.  On the first call to this
-   function, the global variable IN_STREAM is expected to be an open
-   stream associated with the input file INPUT_FILENAME.  If IN_STREAM
-   is at end-of-file, close it and update the global variables IN_STREAM
-   and INPUT_FILENAME so they correspond to the next file in the list.
-   Then try to read a byte from the newly opened file.  Repeat if
-   necessary until EOF is reached for the last file in FILE_LIST, then
-   set *C to EOF and return.  Subsequent calls do likewise.  */
-
-static void
-read_char(int *c)
-{
-       while (in_stream) { /* !EOF */
-               *c = fgetc(in_stream);
-               if (*c != EOF)
-                       return;
-               check_and_close();
-               open_next_file();
-       }
-       *c = EOF;
-}
-
 /* Read N bytes into BLOCK from the concatenation of the input files
    named in the global array FILE_LIST.  On the first call to this
    function, the global variable IN_STREAM is expected to be an open
@@ -1112,8 +1061,8 @@ read_char(int *c)
 static void
 dump_strings(off_t address, off_t end_offset)
 {
-       size_t bufsize = MAX(100, string_min);
-       char *buf = xmalloc(bufsize);
+       unsigned bufsize = MAX(100, string_min);
+       unsigned char *buf = xmalloc(bufsize);
 
        while (1) {
                size_t i;
@@ -1121,19 +1070,25 @@ dump_strings(off_t address, off_t end_offset)
 
                /* See if the next 'string_min' chars are all printing chars.  */
  tryline:
-               if (limit_bytes_to_format && (end_offset - string_min <= address))
+               if ((option_mask32 & OPT_N) && (end_offset - string_min <= address))
                        break;
                i = 0;
-               while (!limit_bytes_to_format || address < end_offset) {
+               while (!(option_mask32 & OPT_N) || address < end_offset) {
                        if (i == bufsize) {
                                bufsize += bufsize/8;
                                buf = xrealloc(buf, bufsize);
                        }
-                       read_char(&c);
-                       if (c < 0) { /* EOF */
-                               free(buf);
-                               return;
+
+                       while (in_stream) { /* !EOF */
+                               c = fgetc(in_stream);
+                               if (c != EOF)
+                                       goto got_char;
+                               check_and_close();
+                               open_next_file();
                        }
+                       /* EOF */
+                       goto ret;
+ got_char:
                        address++;
                        if (!c)
                                break;
@@ -1145,8 +1100,7 @@ dump_strings(off_t address, off_t end_offset)
                if (i < string_min)             /* Too short! */
                        goto tryline;
 
-               /* If we get here, the string is all printable and NUL-terminated,
-                * so print it.  It is all in 'buf' and 'i' is its length.  */
+               /* If we get here, the string is all printable and NUL-terminated */
                buf[i] = 0;
                format_address(address - i - 1, ' ');
 
@@ -1167,41 +1121,51 @@ dump_strings(off_t address, off_t end_offset)
 
        /* We reach this point only if we search through
           (max_bytes_to_format - string_min) bytes before reaching EOF.  */
-       free(buf);
-
        check_and_close();
+ ret:
+       free(buf);
 }
 
-int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int od_main(int argc, char **argv)
+#if ENABLE_LONG_OPTS
+/* If S is a valid traditional offset specification with an optional
+   leading '+' return nonzero and set *OFFSET to the offset it denotes.  */
+
+static int
+parse_old_offset(const char *s, off_t *offset)
 {
-       static const struct suffix_mult bkm[] = {
+       static const struct suffix_mult Bb[] = {
+               { "B", 1024 },
                { "b", 512 },
-               { "k", 1024 },
-               { "m", 1024*1024 },
                { "", 0 }
        };
-       enum {
-               OPT_A = 1 << 0,
-               OPT_N = 1 << 1,
-               OPT_a = 1 << 2,
-               OPT_b = 1 << 3,
-               OPT_c = 1 << 4,
-               OPT_d = 1 << 5,
-               OPT_f = 1 << 6,
-               OPT_h = 1 << 7,
-               OPT_i = 1 << 8,
-               OPT_j = 1 << 9,
-               OPT_l = 1 << 10,
-               OPT_o = 1 << 11,
-               OPT_t = 1 << 12,
-               OPT_v = 1 << 13,
-               OPT_x = 1 << 14,
-               OPT_s = 1 << 15,
-               OPT_S = 1 << 16,
-               OPT_w = 1 << 17,
-               OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS,
-       };
+       char *p;
+       int radix;
+
+       /* Skip over any leading '+'. */
+       if (s[0] == '+') ++s;
+       if (!isdigit(s[0])) return 0; /* not a number */
+
+       /* Determine the radix we'll use to interpret S.  If there is a '.',
+        * it's decimal, otherwise, if the string begins with '0X'or '0x',
+        * it's hexadecimal, else octal.  */
+       p = strchr(s, '.');
+       radix = 8;
+       if (p) {
+               p[0] = '\0'; /* cheating */
+               radix = 10;
+       } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
+               radix = 16;
+
+       *offset = xstrtooff_sfx(s, radix, Bb);
+       if (p) p[0] = '.';
+
+       return (*offset >= 0);
+}
+#endif
+
+int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int od_main(int argc UNUSED_PARAM, char **argv)
+{
 #if ENABLE_LONG_OPTS
        static const char od_longopts[] ALIGN1 =
                "skip-bytes\0"        Required_argument "j"
@@ -1209,18 +1173,18 @@ int od_main(int argc, char **argv)
                "read-bytes\0"        Required_argument "N"
                "format\0"            Required_argument "t"
                "output-duplicates\0" No_argument       "v"
+               /* Yes, it's true: -S NUM, but --strings[=NUM]!
+                * that is, NUM is mandatory for -S but optional for --strings!
+                */
                "strings\0"           Optional_argument "S"
                "width\0"             Optional_argument "w"
                "traditional\0"       No_argument       "\xff"
                ;
 #endif
-       char *str_A, *str_N, *str_j, *str_S;
+       const char *str_A, *str_N, *str_j, *str_S = "3";
        llist_t *lst_t = NULL;
        unsigned opt;
        int l_c_m;
-       /* The old-style 'pseudo starting address' to be printed in parentheses
-          after any true address.  */
-       off_t pseudo_start = pseudo_start; // for gcc
        /* The number of input bytes to skip before formatting and writing.  */
        off_t n_bytes_to_skip = 0;
        /* The offset of the first byte after the last byte to be formatted.  */
@@ -1232,20 +1196,13 @@ int od_main(int argc, char **argv)
        format_address = format_address_std;
        address_base_char = 'o';
        address_pad_len_char = '7';
-       /* flag_dump_strings = 0; - already is */
 
        /* Parse command line */
        opt_complementary = "w+:t::"; /* -w N, -t is a list */
 #if ENABLE_LONG_OPTS
        applet_long_options = od_longopts;
 #endif
-       opt = getopt32(argv, "A:N:abcdfhij:lot:vxsS:"
-               "w::", // -w with optional param
-               // -S was -s and also had optional parameter
-               // but in coreutils 6.3 it was renamed and now has
-               // _mandatory_ parameter
-               &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block);
-       argc -= optind;
+       opt = OD_GETOPT32();
        argv += optind;
        if (opt & OPT_A) {
                static const char doxn[] ALIGN1 = "doxn";
@@ -1267,8 +1224,7 @@ int od_main(int argc, char **argv)
                address_pad_len_char = doxn_address_pad_len_char[pos];
        }
        if (opt & OPT_N) {
-               limit_bytes_to_format = 1;
-               max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm);
+               max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm_suffixes);
        }
        if (opt & OPT_a) decode_format_string("a");
        if (opt & OPT_b) decode_format_string("oC");
@@ -1277,31 +1233,26 @@ int od_main(int argc, char **argv)
        if (opt & OPT_f) decode_format_string("fF");
        if (opt & OPT_h) decode_format_string("x2");
        if (opt & OPT_i) decode_format_string("d2");
-       if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm);
+       if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm_suffixes);
        if (opt & OPT_l) decode_format_string("d4");
        if (opt & OPT_o) decode_format_string("o2");
-       //if (opt & OPT_t)...
        while (lst_t) {
                decode_format_string(llist_pop(&lst_t));
        }
-       if (opt & OPT_v) verbose = 1;
        if (opt & OPT_x) decode_format_string("x2");
        if (opt & OPT_s) decode_format_string("d2");
        if (opt & OPT_S) {
-               string_min = 3;
-               string_min = xstrtou_sfx(str_S, 0, bkm);
-               flag_dump_strings = 1;
+               string_min = xstrtou_sfx(str_S, 0, bkm_suffixes);
        }
-       //if (opt & OPT_w)...
-       //if (opt & OPT_traditional)...
 
-       if (flag_dump_strings && n_specs > 0)
-               bb_error_msg_and_die("no type may be specified when dumping strings");
+       // Bloat:
+       //if ((option_mask32 & OPT_S) && n_specs > 0)
+       //      bb_error_msg_and_die("no type may be specified when dumping strings");
 
        /* If the --traditional option is used, there may be from
         * 0 to 3 remaining command line arguments;  handle each case
         * separately.
-        * od [file] [[+]offset[.][b] [[+]label[.][b]]]
+        * od [FILE] [[+]OFFSET[.][b] [[+]LABEL[.][b]]]
         * The offset and pseudo_start have the same syntax.
         *
         * FIXME: POSIX 1003.1-2001 with XSI requires support for the
@@ -1309,93 +1260,91 @@ int od_main(int argc, char **argv)
 
 #if ENABLE_LONG_OPTS
        if (opt & OPT_traditional) {
-               off_t o1, o2;
-
-               if (argc == 1) {
-                       if (parse_old_offset(argv[0], &o1)) {
-                               n_bytes_to_skip = o1;
-                               --argc;
-                               ++argv;
-                       }
-               } else if (argc == 2) {
-                       if (parse_old_offset(argv[0], &o1)
-                        && parse_old_offset(argv[1], &o2)
-                       ) {
-                               n_bytes_to_skip = o1;
-                               flag_pseudo_start = 1;
-                               pseudo_start = o2;
-                               argv += 2;
-                               argc -= 2;
-                       } else if (parse_old_offset(argv[1], &o2)) {
-                               n_bytes_to_skip = o2;
-                               --argc;
-                               argv[1] = argv[0];
-                               ++argv;
-                       } else {
-                               bb_error_msg_and_die("invalid second operand "
-                                       "in compatibility mode '%s'", argv[1]);
-                       }
-               } else if (argc == 3) {
-                       if (parse_old_offset(argv[1], &o1)
-                        && parse_old_offset(argv[2], &o2)
-                       ) {
-                               n_bytes_to_skip = o1;
-                               flag_pseudo_start = 1;
-                               pseudo_start = o2;
-                               argv[2] = argv[0];
-                               argv += 2;
-                               argc -= 2;
-                       } else {
-                               bb_error_msg_and_die("in compatibility mode "
-                                       "the last two arguments must be offsets");
+               if (argv[0]) {
+                       off_t pseudo_start = -1;
+                       off_t o1, o2;
+
+                       if (!argv[1]) { /* one arg */
+                               if (parse_old_offset(argv[0], &o1)) {
+                                       /* od --traditional OFFSET */
+                                       n_bytes_to_skip = o1;
+                                       argv++;
+                               }
+                               /* od --traditional FILE */
+                       } else if (!argv[2]) { /* two args */
+                               if (parse_old_offset(argv[0], &o1)
+                                && parse_old_offset(argv[1], &o2)
+                               ) {
+                                       /* od --traditional OFFSET LABEL */
+                                       n_bytes_to_skip = o1;
+                                       pseudo_start = o2;
+                                       argv += 2;
+                               } else if (parse_old_offset(argv[1], &o2)) {
+                                       /* od --traditional FILE OFFSET */
+                                       n_bytes_to_skip = o2;
+                                       argv[1] = NULL;
+                               } else {
+                                       bb_error_msg_and_die("invalid second argument '%s'", argv[1]);
+                               }
+                       } else if (!argv[3]) { /* three args */
+                               if (parse_old_offset(argv[1], &o1)
+                                && parse_old_offset(argv[2], &o2)
+                               ) {
+                                       /* od --traditional FILE OFFSET LABEL */
+                                       n_bytes_to_skip = o1;
+                                       pseudo_start = o2;
+                                       argv[1] = NULL;
+                               } else {
+                                       bb_error_msg_and_die("the last two arguments must be offsets");
+                               }
+                       } else { /* >3 args */
+                               bb_error_msg_and_die("too many arguments");
                        }
-               } else if (argc > 3)    {
-                       bb_error_msg_and_die("compatibility mode supports "
-                               "at most three arguments");
-               }
 
-               if (flag_pseudo_start) {
-                       if (format_address == format_address_none) {
-                               address_base_char = 'o';
-                               address_pad_len_char = '7';
-                               format_address = format_address_paren;
-                       } else
-                               format_address = format_address_label;
+                       if (pseudo_start >= 0) {
+                               if (format_address == format_address_none) {
+                                       address_base_char = 'o';
+                                       address_pad_len_char = '7';
+                                       format_address = format_address_paren;
+                               } else {
+                                       format_address = format_address_label;
+                               }
+                               pseudo_offset = pseudo_start - n_bytes_to_skip;
+                       }
                }
+               /* else: od --traditional (without args) */
        }
 #endif
 
-       if (limit_bytes_to_format) {
+       if (option_mask32 & OPT_N) {
                end_offset = n_bytes_to_skip + max_bytes_to_format;
                if (end_offset < n_bytes_to_skip)
-                       bb_error_msg_and_die("skip-bytes + read-bytes is too large");
+                       bb_error_msg_and_die("SKIP + SIZE is too large");
        }
 
        if (n_specs == 0) {
                decode_format_string("o2");
-               n_specs = 1;
+               /*n_specs = 1; - done by decode_format_string */
        }
 
        /* If no files were listed on the command line,
           set the global pointer FILE_LIST so that it
           references the null-terminated list of one name: "-".  */
        file_list = bb_argv_dash;
-       if (argc > 0) {
+       if (argv[0]) {
                /* Set the global pointer FILE_LIST so that it
                   references the first file-argument on the command-line.  */
                file_list = (char const *const *) argv;
        }
 
-       /* open the first input file */
+       /* Open the first input file */
        open_next_file();
-       /* skip over any unwanted header bytes */
+       /* Skip over any unwanted header bytes */
        skip(n_bytes_to_skip);
        if (!in_stream)
                return EXIT_FAILURE;
 
-       pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0);
-
-       /* Compute output block length.  */
+       /* Compute output block length */
        l_c_m = get_lcm();
 
        if (opt & OPT_w) { /* -w: width */
@@ -1417,13 +1366,13 @@ int od_main(int argc, char **argv)
        }
 #endif
 
-       if (flag_dump_strings)
+       if (option_mask32 & OPT_S)
                dump_strings(n_bytes_to_skip, end_offset);
        else
                dump(n_bytes_to_skip, end_offset);
 
-       if (fclose(stdin) == EOF)
+       if (fclose(stdin))
                bb_perror_msg_and_die(bb_msg_standard_input);
 
-       return ioerror;
+       return exit_code;
 }
index 2430f3a..bd5db70 100644 (file)
@@ -5,11 +5,19 @@
  * Copyright (C) 2005 by Erik Andersen <andersen@codepoet.org>
  * Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define printenv_trivial_usage
+//usage:       "[VARIABLE]..."
+//usage:#define printenv_full_usage "\n\n"
+//usage:       "Print environment VARIABLEs.\n"
+//usage:       "If no VARIABLE specified, print all."
+
 #include "libbb.h"
 
+/* This is a NOFORK applet. Be very careful! */
+
 int printenv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int printenv_main(int argc UNUSED_PARAM, char **argv)
 {
@@ -17,9 +25,14 @@ int printenv_main(int argc UNUSED_PARAM, char **argv)
 
        /* no variables specified, show whole env */
        if (!argv[1]) {
-               int e = 0;
-               while (environ[e])
-                       puts(environ[e++]);
+               char **e = environ;
+
+               /* environ can be NULL! (for example, after clearenv())
+                * Check for that:
+                */
+               if (e)
+                       while (*e)
+                               puts(*e++);
        } else {
                /* search for specified variables and print them out if found */
                char *arg, *env;
index eb53fa4..3dd43a9 100644 (file)
@@ -4,7 +4,7 @@
    Copyright 1999 Dave Cinege
    Portions copyright (C) 1990-1996 Free Software Foundation, Inc.
 
-   Licensed under GPL v2 or later, see file LICENSE in this tarball for details.
+   Licensed under GPLv2 or later, see file LICENSE in this source tree.
 */
 
 /* Usage: printf format [argument...]
    David MacKenzie <djm@gnu.ai.mit.edu>
 */
 
-//   19990508 Busy Boxed! Dave Cinege
+/* 19990508 Busy Boxed! Dave Cinege */
+
+//usage:#define printf_trivial_usage
+//usage:       "FORMAT [ARG]..."
+//usage:#define printf_full_usage "\n\n"
+//usage:       "Format and print ARG(s) according to FORMAT (a-la C printf)"
+//usage:
+//usage:#define printf_example_usage
+//usage:       "$ printf \"Val=%d\\n\" 5\n"
+//usage:       "Val=5\n"
 
 #include "libbb.h"
 
@@ -66,7 +75,7 @@ static int multiconvert(const char *arg, void *result, converter convert)
        errno = 0;
        convert(arg, result);
        if (errno) {
-               bb_error_msg("%s: invalid number", arg);
+               bb_error_msg("invalid number '%s'", arg);
                return 1;
        }
        return 0;
@@ -122,16 +131,29 @@ static double my_xstrtod(const char *arg)
        return result;
 }
 
-static void print_esc_string(char *str)
+/* Handles %b */
+static void print_esc_string(const char *str)
 {
-       while (*str) {
-               if (*str == '\\') {
-                       str++;
-                       bb_putchar(bb_process_escape_sequence((const char **)&str));
-               } else {
-                       bb_putchar(*str);
-                       str++;
+       char c;
+       while ((c = *str) != '\0') {
+               str++;
+               if (c == '\\') {
+                       /* %b also accepts 4-digit octals of the form \0### */
+                       if (*str == '0') {
+                               if ((unsigned char)(str[1] - '0') < 8) {
+                                       /* 2nd char is 0..7: skip leading '0' */
+                                       str++;
+                               }
+                       }
+                       {
+                               /* optimization: don't force arg to be on-stack,
+                                * use another variable for that. */
+                               const char *z = str;
+                               c = bb_process_escape_sequence(&z);
+                               str = z;
+                       }
                }
+               putchar(c);
        }
 }
 
@@ -230,7 +252,7 @@ static int get_width_prec(const char *str)
 {
        int v = bb_strtoi(str, NULL, 10);
        if (errno) {
-               bb_error_msg("%s: invalid number", str);
+               bb_error_msg("invalid number '%s'", str);
                v = 0;
        }
        return v;
@@ -344,7 +366,7 @@ static char **print_formatted(char *f, char **argv, int *conv_err)
                        f--;
                        break;
                default:
-                       bb_putchar(*f);
+                       putchar(*f);
                }
        }
 
index 11278a2..bb3ad04 100644 (file)
@@ -4,20 +4,79 @@
  *
  * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define pwd_trivial_usage
+//usage:       ""
+//usage:#define pwd_full_usage "\n\n"
+//usage:       "Print the full filename of the current working directory"
+//usage:
+//usage:#define pwd_example_usage
+//usage:       "$ pwd\n"
+//usage:       "/root\n"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
 
+static int logical_getcwd(void)
+{
+       struct stat st1;
+       struct stat st2;
+       char *wd;
+       char *p;
+
+       wd = getenv("PWD");
+       if (!wd || wd[0] != '/')
+               return 0;
+
+       p = wd;
+       while (*p) {
+               /* doing strstr(p, "/.") by hand is smaller and faster... */
+               if (*p++ != '/')
+                       continue;
+               if (*p != '.')
+                       continue;
+               /* we found "/.", skip to next char */
+               p++;
+               if (*p == '.')
+                       p++; /* we found "/.." */
+               if (*p == '\0' || *p == '/')
+                       return 0; /* "/./" or "/../" component: bad */
+       }
+
+       if (stat(wd, &st1) != 0)
+               return 0;
+       if (stat(".", &st2) != 0)
+               return 0;
+       if (st1.st_ino != st2.st_ino)
+               return 0;
+       if (st1.st_dev != st2.st_dev)
+               return 0;
+
+       puts(wd);
+       return 1;
+}
+
 int pwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int pwd_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 {
        char *buf;
 
+       if (ENABLE_DESKTOP) {
+               /* TODO: assume -L if $POSIXLY_CORRECT? (coreutils does that)
+                * Rationale:
+                * POSIX requires a default of -L, but most scripts expect -P
+                */
+               unsigned opt = getopt32(argv, "LP");
+               if ((opt & 1) && logical_getcwd())
+                       return fflush_all();
+       }
+
        buf = xrealloc_getcwd_or_warn(NULL);
-       if (buf != NULL) {
+
+       if (buf) {
                puts(buf);
                free(buf);
                return fflush_all();
index 20df38b..d73ef4d 100644 (file)
@@ -4,8 +4,19 @@
  *
  * Copyright (C) 2000,2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
  *
- * Licensed under GPL v2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define readlink_trivial_usage
+//usage:       IF_FEATURE_READLINK_FOLLOW("[-fnv] ") "FILE"
+//usage:#define readlink_full_usage "\n\n"
+//usage:       "Display the value of a symlink"
+//usage:       IF_FEATURE_READLINK_FOLLOW( "\n"
+//usage:     "\n       -f      Canonicalize by following all symlinks"
+//usage:     "\n       -n      Don't add newline"
+//usage:     "\n       -v      Verbose"
+//usage:       )
+
 #include "libbb.h"
 
 /*
  *   -q, --quiet, -s, --silent     suppress most error messages
  *   -v, --verbose                 report error messages
  *
- * bbox supports: -f -n -v (fully), -q -s (accepts but ignores)
+ * bbox supports: -f (partially) -n -v (fully), -q -s (accepts but ignores)
+ * Note: we export the -f flag, but our -f behaves like coreutils' -e.
+ * Unfortunately, there isn't a C lib function we can leverage to get this
+ * behavior which means we'd have to implement the full stack ourselves :(.
  */
 
 int readlink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -36,7 +50,6 @@ int readlink_main(int argc UNUSED_PARAM, char **argv)
 {
        char *buf;
        char *fname;
-       char pathbuf[PATH_MAX];
 
        IF_FEATURE_READLINK_FOLLOW(
                unsigned opt;
@@ -56,7 +69,7 @@ int readlink_main(int argc UNUSED_PARAM, char **argv)
                logmode = LOGMODE_NONE;
 
        if (opt & 1) { /* -f */
-               buf = realpath(fname, pathbuf);
+               buf = xmalloc_realpath(fname);
        } else {
                buf = xmalloc_readlink_or_warn(fname);
        }
@@ -65,7 +78,7 @@ int readlink_main(int argc UNUSED_PARAM, char **argv)
                return EXIT_FAILURE;
        printf((opt & 2) ? "%s" : "%s\n", buf);
 
-       if (ENABLE_FEATURE_CLEAN_UP && !opt)
+       if (ENABLE_FEATURE_CLEAN_UP)
                free(buf);
 
        fflush_stdout_and_exit(EXIT_SUCCESS);
index 3bc40ee..c513b55 100644 (file)
@@ -7,9 +7,14 @@
  * Now does proper error checking on output and returns a failure exit code
  * if one or more paths cannot be resolved.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define realpath_trivial_usage
+//usage:       "FILE..."
+//usage:#define realpath_full_usage "\n\n"
+//usage:       "Return the absolute pathnames of given FILE"
+
 #include "libbb.h"
 
 int realpath_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
index 3090cc3..042fba1 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
  * Size reduction.
  */
 
+//usage:#define rm_trivial_usage
+//usage:       "[-irf] FILE..."
+//usage:#define rm_full_usage "\n\n"
+//usage:       "Remove (unlink) FILEs\n"
+//usage:     "\n       -i      Always prompt before removing"
+//usage:     "\n       -f      Never prompt"
+//usage:     "\n       -R,-r   Recurse"
+//usage:
+//usage:#define rm_example_usage
+//usage:       "$ rm -rf /tmp/foo\n"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
index 2450a43..cc2dea0 100644 (file)
@@ -4,19 +4,35 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/rmdir.html */
 
+//usage:#define rmdir_trivial_usage
+//usage:       "[OPTIONS] DIRECTORY..."
+//usage:#define rmdir_full_usage "\n\n"
+//usage:       "Remove DIRECTORY if it is empty\n"
+//usage:       IF_FEATURE_RMDIR_LONG_OPTIONS(
+//usage:     "\n       -p|--parents    Include parents"
+//usage:     "\n       --ignore-fail-on-non-empty"
+//usage:       )
+//usage:       IF_NOT_FEATURE_RMDIR_LONG_OPTIONS(
+//usage:     "\n       -p      Include parents"
+//usage:       )
+//usage:
+//usage:#define rmdir_example_usage
+//usage:       "# rmdir /tmp/foo\n"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
 
 
-#define PARENTS 0x01
-#define IGNORE_NON_EMPTY 0x02
+#define PARENTS          (1 << 0)
+//efine VERBOSE          (1 << 1) //accepted but ignored
+#define IGNORE_NON_EMPTY (1 << 2)
 
 int rmdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int rmdir_main(int argc UNUSED_PARAM, char **argv)
@@ -28,13 +44,14 @@ int rmdir_main(int argc UNUSED_PARAM, char **argv)
 #if ENABLE_FEATURE_RMDIR_LONG_OPTIONS
        static const char rmdir_longopts[] ALIGN1 =
                "parents\0"                  No_argument "p"
+               "verbose\0"                  No_argument "v"
                /* Debian etch: many packages fail to be purged or installed
                 * because they desperately want this option: */
                "ignore-fail-on-non-empty\0" No_argument "\xff"
                ;
        applet_long_options = rmdir_longopts;
 #endif
-       flags = getopt32(argv, "p");
+       flags = getopt32(argv, "pv");
        argv += optind;
 
        if (!*argv) {
@@ -50,7 +67,7 @@ int rmdir_main(int argc UNUSED_PARAM, char **argv)
                                if ((flags & IGNORE_NON_EMPTY) && errno == ENOTEMPTY)
                                        break;
 #endif
-                               bb_perror_msg("'%s'", path);    /* Match gnu rmdir msg. */
+                               bb_perror_msg("'%s'", path);  /* Match gnu rmdir msg. */
                                status = EXIT_FAILURE;
                        } else if (flags & PARENTS) {
                                /* Note: path was not "" since rmdir succeeded. */
index 84d11fd..8986192 100644 (file)
@@ -4,8 +4,17 @@
  *
  * Copyright (C) 2004, Glenn McGrath
  *
- * Licensed under the GPL v2, see the file LICENSE in this tarball.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define seq_trivial_usage
+//usage:       "[-w] [-s SEP] [FIRST [INC]] LAST"
+//usage:#define seq_full_usage "\n\n"
+//usage:       "Print numbers from FIRST to LAST, in steps of INC.\n"
+//usage:       "FIRST, INC default to 1.\n"
+//usage:     "\n       -w      Pad to last with leading zeros"
+//usage:     "\n       -s SEP  String separator"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
@@ -86,7 +95,8 @@ int seq_main(int argc, char **argv)
        v = first;
        n = 0;
        while (increment >= 0 ? v <= last : v >= last) {
-               printf("%s%0*.*f", sep, width, frac_part, v);
+               if (printf("%s%0*.*f", sep, width, frac_part, v) < 0)
+                       break; /* I/O error, bail out (yes, this really happens) */
                sep = opt_s;
                /* v += increment; - would accumulate floating point errors */
                n++;
index 399a38d..0ffbd16 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
  * time suffixes for seconds, minutes, hours, and days.
  */
 
+//usage:#define sleep_trivial_usage
+//usage:       IF_FEATURE_FANCY_SLEEP("[") "N" IF_FEATURE_FANCY_SLEEP("]...")
+//usage:#define sleep_full_usage "\n\n"
+//usage:       IF_NOT_FEATURE_FANCY_SLEEP("Pause for N seconds")
+//usage:       IF_FEATURE_FANCY_SLEEP(
+//usage:       "Pause for a time equal to the total of the args given, where each arg can\n"
+//usage:       "have an optional suffix of (s)econds, (m)inutes, (h)ours, or (d)ays")
+//usage:
+//usage:#define sleep_example_usage
+//usage:       "$ sleep 2\n"
+//usage:       "[2 second delay results]\n"
+//usage:       IF_FEATURE_FANCY_SLEEP(
+//usage:       "$ sleep 1d 3h 22m 8s\n"
+//usage:       "[98528 second delay results]\n")
+
 #include "libbb.h"
 
-/* This is a NOFORK applet. Be very careful! */
+/* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells */
 
 
 #if ENABLE_FEATURE_FANCY_SLEEP || ENABLE_FEATURE_FLOAT_SLEEP
@@ -49,6 +64,10 @@ int sleep_main(int argc UNUSED_PARAM, char **argv)
 
 #if ENABLE_FEATURE_FLOAT_SLEEP
 
+# if ENABLE_LOCALE_SUPPORT
+       /* undo busybox.c setlocale */
+       setlocale(LC_NUMERIC, "C");
+# endif
        duration = 0;
        do {
                char *arg = *argv;
@@ -62,14 +81,15 @@ int sleep_main(int argc UNUSED_PARAM, char **argv)
                        d = strtod(arg, &pp);
                        if (errno || *pp)
                                bb_show_usage();
-                       arg[len] = sv;
-                       len--;
-                       sv = arg[len];
-                       arg[len] = '1';
-                       duration += d * xatoul_sfx(&arg[len], sfx);
-                       arg[len] = sv;
-               } else
+                       arg += len;
+                       *arg-- = sv;
+                       sv = *arg;
+                       *arg = '1';
+                       duration += d * xatoul_sfx(arg, sfx);
+                       *arg = sv;
+               } else {
                        duration += xatoul_sfx(arg, sfx);
+               }
        } while (*++argv);
 
        ts.tv_sec = MAXINT(typeof(ts.tv_sec));
index 5c3fa1a..a1625fc 100644 (file)
@@ -6,12 +6,59 @@
  *
  * MAINTAINER: Rob Landley <rob@landley.net>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * See SuS3 sort standard at:
  * http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html
  */
 
+//usage:#define sort_trivial_usage
+//usage:       "[-nru"
+//usage:       IF_FEATURE_SORT_BIG("gMcszbdfimSTokt] [-o FILE] [-k start[.offset][opts][,end[.offset][opts]] [-t CHAR")
+//usage:       "] [FILE]..."
+//usage:#define sort_full_usage "\n\n"
+//usage:       "Sort lines of text\n"
+//usage:       IF_FEATURE_SORT_BIG(
+//usage:     "\n       -b      Ignore leading blanks"
+//usage:     "\n       -c      Check whether input is sorted"
+//usage:     "\n       -d      Dictionary order (blank or alphanumeric only)"
+//usage:     "\n       -f      Ignore case"
+//usage:     "\n       -g      General numerical sort"
+//usage:     "\n       -i      Ignore unprintable characters"
+//usage:     "\n       -k      Sort key"
+//usage:     "\n       -M      Sort month"
+//usage:       )
+//usage:     "\n       -n      Sort numbers"
+//usage:       IF_FEATURE_SORT_BIG(
+//usage:     "\n       -o      Output to file"
+//usage:     "\n       -k      Sort by key"
+//usage:     "\n       -t CHAR Key separator"
+//usage:       )
+//usage:     "\n       -r      Reverse sort order"
+//usage:       IF_FEATURE_SORT_BIG(
+//usage:     "\n       -s      Stable (don't sort ties alphabetically)"
+//usage:       )
+//usage:     "\n       -u      Suppress duplicate lines"
+//usage:       IF_FEATURE_SORT_BIG(
+//usage:     "\n       -z      Lines are terminated by NUL, not newline"
+//usage:     "\n       -mST    Ignored for GNU compatibility")
+//usage:
+//usage:#define sort_example_usage
+//usage:       "$ echo -e \"e\\nf\\nb\\nd\\nc\\na\" | sort\n"
+//usage:       "a\n"
+//usage:       "b\n"
+//usage:       "c\n"
+//usage:       "d\n"
+//usage:       "e\n"
+//usage:       "f\n"
+//usage:       IF_FEATURE_SORT_BIG(
+//usage:               "$ echo -e \"c 3\\nb 2\\nd 2\" | $SORT -k 2,2n -k 1,1r\n"
+//usage:               "d 2\n"
+//usage:               "b 2\n"
+//usage:               "c 3\n"
+//usage:       )
+//usage:       ""
+
 #include "libbb.h"
 
 /* This is a NOEXEC applet. Be very careful! */
@@ -52,8 +99,8 @@ enum {
 static char key_separator;
 
 static struct sort_key {
-       struct sort_key *next_key;      /* linked list */
-       unsigned range[4];      /* start word, start char, end word, end char */
+       struct sort_key *next_key;  /* linked list */
+       unsigned range[4];          /* start word, start char, end word, end char */
        unsigned flags;
 } *key_list;
 
@@ -172,7 +219,7 @@ static int compare_keys(const void *xarg, const void *yarg)
                y = get_key(*(char **)yarg, key, flags);
 #else
        /* This curly bracket serves no purpose but to match the nesting
-          level of the for () loop we're not using */
+        * level of the for () loop we're not using */
        {
                x = *(char **)xarg;
                y = *(char **)yarg;
@@ -412,7 +459,7 @@ int sort_main(int argc UNUSED_PARAM, char **argv)
 #if ENABLE_FEATURE_SORT_BIG
        /* Open output file _after_ we read all input ones */
        if (option_mask32 & FLAG_o)
-               xmove_fd(xopen3(str_o, O_WRONLY, 0666), STDOUT_FILENO);
+               xmove_fd(xopen(str_o, O_WRONLY|O_CREAT|O_TRUNC), STDOUT_FILENO);
 #endif
        flag = (option_mask32 & FLAG_z) ? '\0' : '\n';
        for (i = 0; i < linecount; i++)
index c2f3885..1e1673e 100644 (file)
@@ -3,25 +3,35 @@
  * split - split a file into pieces
  * Copyright (c) 2007 Bernhard Reutner-Fischer
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 /* BB_AUDIT: SUSv3 compliant
  * SUSv3 requirements:
  * http://www.opengroup.org/onlinepubs/009695399/utilities/split.html
  */
+
+//usage:#define split_trivial_usage
+//usage:       "[OPTIONS] [INPUT [PREFIX]]"
+//usage:#define split_full_usage "\n\n"
+//usage:       "       -b N[k|m]       Split by N (kilo|mega)bytes"
+//usage:     "\n       -l N            Split by N lines"
+//usage:     "\n       -a N            Use N letters as suffix"
+//usage:
+//usage:#define split_example_usage
+//usage:       "$ split TODO foo\n"
+//usage:       "$ cat TODO | split -a 2 -l 2 TODO_\n"
+
 #include "libbb.h"
 
-static const struct suffix_mult split_suffices[] = {
 #if ENABLE_FEATURE_SPLIT_FANCY
+static const struct suffix_mult split_suffixes[] = {
        { "b", 512 },
-#endif
        { "k", 1024 },
        { "m", 1024*1024 },
-#if ENABLE_FEATURE_SPLIT_FANCY
        { "g", 1024*1024*1024 },
-#endif
        { "", 0 }
 };
+#endif
 
 /* Increment the suffix part of the filename.
  * Returns NULL if we are out of filenames.
@@ -32,7 +42,7 @@ static char *next_file(char *old, unsigned suffix_len)
        unsigned i = 1;
        char *curr;
 
-       do {
+       while (1) {
                curr = old + end - i;
                if (*curr < 'z') {
                        *curr += 1;
@@ -43,7 +53,7 @@ static char *next_file(char *old, unsigned suffix_len)
                        return NULL;
                }
                *curr = 'a';
-       } while (1);
+       }
 
        return old;
 }
@@ -74,7 +84,10 @@ int split_main(int argc UNUSED_PARAM, char **argv)
        if (opt & SPLIT_OPT_l)
                cnt = XATOOFF(count_p);
        if (opt & SPLIT_OPT_b) // FIXME: also needs XATOOFF
-               cnt = xatoull_sfx(count_p, split_suffices);
+               cnt = xatoull_sfx(count_p,
+                               IF_FEATURE_SPLIT_FANCY(split_suffixes)
+                               IF_NOT_FEATURE_SPLIT_FANCY(km_suffixes)
+               );
        sfx = "x";
 
        argv += optind;
@@ -82,9 +95,7 @@ int split_main(int argc UNUSED_PARAM, char **argv)
                int fd;
                if (argv[1])
                        sfx = argv[1];
-               fd = open_or_warn_stdin(argv[0]);
-               if (fd == -1)
-                       return EXIT_FAILURE;
+               fd = xopen_stdin(argv[0]);
                xmove_fd(fd, STDIN_FILENO);
        } else {
                argv[0] = (char *) bb_msg_standard_input;
index e7c24e6..dc9d81c 100644 (file)
  * Written by Michael Meskes
  * Taken from coreutils and turned into a busybox applet by Mike Frysinger
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define stat_trivial_usage
+//usage:       "[OPTIONS] FILE..."
+//usage:#define stat_full_usage "\n\n"
+//usage:       "Display file (default) or filesystem status\n"
+//usage:       IF_FEATURE_STAT_FORMAT(
+//usage:     "\n       -c fmt  Use the specified format"
+//usage:       )
+//usage:     "\n       -f      Display filesystem status"
+//usage:     "\n       -L      Follow links"
+//usage:     "\n       -t      Display info in terse form"
+//usage:       IF_SELINUX(
+//usage:     "\n       -Z      Print security context"
+//usage:       )
+//usage:       IF_FEATURE_STAT_FORMAT(
+//usage:       "\n\nValid format sequences for files:\n"
+//usage:       " %a    Access rights in octal\n"
+//usage:       " %A    Access rights in human readable form\n"
+//usage:       " %b    Number of blocks allocated (see %B)\n"
+//usage:       " %B    The size in bytes of each block reported by %b\n"
+//usage:       " %d    Device number in decimal\n"
+//usage:       " %D    Device number in hex\n"
+//usage:       " %f    Raw mode in hex\n"
+//usage:       " %F    File type\n"
+//usage:       " %g    Group ID of owner\n"
+//usage:       " %G    Group name of owner\n"
+//usage:       " %h    Number of hard links\n"
+//usage:       " %i    Inode number\n"
+//usage:       " %n    File name\n"
+//usage:       " %N    File name, with -> TARGET if symlink\n"
+//usage:       " %o    I/O block size\n"
+//usage:       " %s    Total size, in bytes\n"
+//usage:       " %t    Major device type in hex\n"
+//usage:       " %T    Minor device type in hex\n"
+//usage:       " %u    User ID of owner\n"
+//usage:       " %U    User name of owner\n"
+//usage:       " %x    Time of last access\n"
+//usage:       " %X    Time of last access as seconds since Epoch\n"
+//usage:       " %y    Time of last modification\n"
+//usage:       " %Y    Time of last modification as seconds since Epoch\n"
+//usage:       " %z    Time of last change\n"
+//usage:       " %Z    Time of last change as seconds since Epoch\n"
+//usage:       "\nValid format sequences for file systems:\n"
+//usage:       " %a    Free blocks available to non-superuser\n"
+//usage:       " %b    Total data blocks in file system\n"
+//usage:       " %c    Total file nodes in file system\n"
+//usage:       " %d    Free file nodes in file system\n"
+//usage:       " %f    Free blocks in file system\n"
+//usage:       IF_SELINUX(
+//usage:       " %C    Security context in selinux\n"
+//usage:       )
+//usage:       " %i    File System ID in hex\n"
+//usage:       " %l    Maximum length of filenames\n"
+//usage:       " %n    File name\n"
+//usage:       " %s    Block size (for faster transfer)\n"
+//usage:       " %S    Fundamental block size (for block counts)\n"
+//usage:       " %t    Type in hex\n"
+//usage:       " %T    Type in human readable form"
+//usage:       )
+
 #include "libbb.h"
 
 #define OPT_FILESYS     (1 << 0)
@@ -39,9 +99,15 @@ static const char *file_type(const struct stat *st)
        if (S_ISFIFO(st->st_mode)) return "fifo";
        if (S_ISLNK(st->st_mode))  return "symbolic link";
        if (S_ISSOCK(st->st_mode)) return "socket";
+#ifdef S_TYPEISMQ
        if (S_TYPEISMQ(st))        return "message queue";
+#endif
+#ifdef S_TYPEISSEM
        if (S_TYPEISSEM(st))       return "semaphore";
+#endif
+#ifdef S_TYPEISSHM
        if (S_TYPEISSHM(st))       return "shared memory object";
+#endif
 #ifdef S_TYPEISTMO
        if (S_TYPEISTMO(st))       return "typed memory object";
 #endif
@@ -61,7 +127,7 @@ static const char *human_time(time_t t)
        /*static char buf[sizeof("YYYY-MM-DD HH:MM:SS.000000000")] ALIGN1;*/
 #define buf bb_common_bufsiz1
 
-       strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S.000000000", localtime(&t));
+       strcpy(strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &t), ".000000000");
        return buf;
 #undef buf
 }
@@ -247,14 +313,12 @@ static void FAST_FUNC print_stat(char *pformat, const char m,
                strcat(pformat, "lu");
                printf(pformat, (unsigned long) statbuf->st_uid);
        } else if (m == 'U') {
-               setpwent();
                pw_ent = getpwuid(statbuf->st_uid);
                printfs(pformat, (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN");
        } else if (m == 'g') {
                strcat(pformat, "lu");
                printf(pformat, (unsigned long) statbuf->st_gid);
        } else if (m == 'G') {
-               setgrent();
                gw_ent = getgrgid(statbuf->st_gid);
                printfs(pformat, (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN");
        } else if (m == 't') {
@@ -378,7 +442,7 @@ static bool do_statfs(const char *filename, const char *format)
                     : getfilecon(filename, &scontext)
                    ) < 0
                ) {
-                       bb_perror_msg(filename);
+                       bb_simple_perror_msg(filename);
                        return 0;
                }
        }
@@ -471,7 +535,7 @@ static bool do_statfs(const char *filename, const char *format)
        if (scontext)
                freecon(scontext);
 # endif
-#endif /* FEATURE_STAT_FORMAT */
+#endif  /* FEATURE_STAT_FORMAT */
        return 1;
 }
 
@@ -491,7 +555,7 @@ static bool do_stat(const char *filename, const char *format)
                     : getfilecon(filename, &scontext)
                    ) < 0
                ) {
-                       bb_perror_msg(filename);
+                       bb_simple_perror_msg(filename);
                        return 0;
                }
        }
@@ -527,37 +591,43 @@ static bool do_stat(const char *filename, const char *format)
 # else
                if (option_mask32 & OPT_TERSE) {
                        format = (option_mask32 & OPT_SELINUX ?
-                                 "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C\n":
-                                 "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n");
+                               "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C\n"
+                               :
+                               "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n"
+                               );
                } else {
                        if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
                                format = (option_mask32 & OPT_SELINUX ?
-                                         "  File: %N\n"
-                                         "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
-                                         "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
-                                         " Device type: %t,%T\n"
-                                         "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
-                                         "   S_Context: %C\n"
-                                         "Access: %x\n" "Modify: %y\n" "Change: %z\n":
-                                         "  File: %N\n"
-                                         "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
-                                         "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
-                                         " Device type: %t,%T\n"
-                                         "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
-                                         "Access: %x\n" "Modify: %y\n" "Change: %z\n");
+                                       "  File: %N\n"
+                                       "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
+                                       "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
+                                       " Device type: %t,%T\n"
+                                       "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
+                                       "   S_Context: %C\n"
+                                       "Access: %x\n" "Modify: %y\n" "Change: %z\n"
+                                       :
+                                       "  File: %N\n"
+                                       "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
+                                       "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
+                                       " Device type: %t,%T\n"
+                                       "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
+                                       "Access: %x\n" "Modify: %y\n" "Change: %z\n"
+                                       );
                        } else {
                                format = (option_mask32 & OPT_SELINUX ?
-                                         "  File: %N\n"
-                                         "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
-                                         "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
-                                         "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
-                                         "S_Context: %C\n"
-                                         "Access: %x\n" "Modify: %y\n" "Change: %z\n":
-                                         "  File: %N\n"
-                                         "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
-                                         "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
-                                         "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
-                                         "Access: %x\n" "Modify: %y\n" "Change: %z\n");
+                                       "  File: %N\n"
+                                       "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
+                                       "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
+                                       "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
+                                       "S_Context: %C\n"
+                                       "Access: %x\n" "Modify: %y\n" "Change: %z\n"
+                                       :
+                                       "  File: %N\n"
+                                       "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
+                                       "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
+                                       "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
+                                       "Access: %x\n" "Modify: %y\n" "Change: %z\n"
+                                       );
                        }
                }
 # endif
@@ -591,20 +661,20 @@ static bool do_stat(const char *filename, const char *format)
 # endif
        } else {
                char *linkname = NULL;
-
                struct passwd *pw_ent;
                struct group *gw_ent;
-               setgrent();
+
                gw_ent = getgrgid(statbuf.st_gid);
-               setpwent();
                pw_ent = getpwuid(statbuf.st_uid);
 
                if (S_ISLNK(statbuf.st_mode))
                        linkname = xmalloc_readlink_or_warn(filename);
-               if (linkname)
+               if (linkname) {
                        printf("  File: '%s' -> '%s'\n", filename, linkname);
-               else
+                       free(linkname);
+               } else {
                        printf("  File: '%s'\n", filename);
+               }
 
                printf("  Size: %-10llu\tBlocks: %-10llu IO Block: %-6lu %s\n"
                       "Device: %llxh/%llud\tInode: %-10llu  Links: %-5lu",
@@ -632,12 +702,11 @@ static bool do_stat(const char *filename, const char *format)
 # if ENABLE_SELINUX
                printf("   S_Context: %lc\n", *scontext);
 # endif
-               printf("Access: %s\n" "Modify: %s\n" "Change: %s\n",
-                      human_time(statbuf.st_atime),
-                      human_time(statbuf.st_mtime),
-                      human_time(statbuf.st_ctime));
+               printf("Access: %s\n", human_time(statbuf.st_atime));
+               printf("Modify: %s\n", human_time(statbuf.st_mtime));
+               printf("Change: %s\n", human_time(statbuf.st_ctime));
        }
-#endif /* FEATURE_STAT_FORMAT */
+#endif  /* FEATURE_STAT_FORMAT */
        return 1;
 }
 
index c40d718..378a848 100644 (file)
@@ -2,7 +2,7 @@
 /* stty -- change and print terminal line settings
    Copyright (C) 1990-1999 Free Software Foundation, Inc.
 
-   Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+   Licensed under GPLv2 or later, see file LICENSE in this source tree.
 */
 /* Usage: stty [-ag] [-F device] [setting...]
 
 
    */
 
+//usage:#define stty_trivial_usage
+//usage:       "[-a|g] [-F DEVICE] [SETTING]..."
+//usage:#define stty_full_usage "\n\n"
+//usage:       "Without arguments, prints baud rate, line discipline,\n"
+//usage:       "and deviations from stty sane\n"
+//usage:     "\n       -F DEVICE       Open device instead of stdin"
+//usage:     "\n       -a              Print all current settings in human-readable form"
+//usage:     "\n       -g              Print in stty-readable form"
+//usage:     "\n       [SETTING]       See manpage"
+
 #include "libbb.h"
 
 #ifndef _POSIX_VDISABLE
 #if defined(VEOL2) && !defined(CEOL2)
 # define CEOL2 _POSIX_VDISABLE
 #endif
+/* glibc-2.12.1 uses only VSWTC name */
+#if defined(VSWTC) && !defined(VSWTCH)
+# define VSWTCH VSWTC
+#endif
 /* ISC renamed swtch to susp for termios, but we'll accept either name */
 #if defined(VSUSP) && !defined(VSWTCH)
 # define VSWTCH VSUSP
 # define CSTATUS Control('t')
 #endif
 
+/* Save us from #ifdef forest plague */
+#ifndef BSDLY
+# define BSDLY 0
+#endif
+#ifndef CIBAUD
+# define CIBAUD 0
+#endif
+#ifndef CRDLY
+# define CRDLY 0
+#endif
+#ifndef CRTSCTS
+# define CRTSCTS 0
+#endif
+#ifndef ECHOCTL
+# define ECHOCTL 0
+#endif
+#ifndef ECHOKE
+# define ECHOKE 0
+#endif
+#ifndef ECHOPRT
+# define ECHOPRT 0
+#endif
+#ifndef FFDLY
+# define FFDLY 0
+#endif
+#ifndef IEXTEN
+# define IEXTEN 0
+#endif
+#ifndef IMAXBEL
+# define IMAXBEL 0
+#endif
+#ifndef IUCLC
+# define IUCLC 0
+#endif
+#ifndef IXANY
+# define IXANY 0
+#endif
+#ifndef NLDLY
+# define NLDLY 0
+#endif
+#ifndef OCRNL
+# define OCRNL 0
+#endif
+#ifndef OFDEL
+# define OFDEL 0
+#endif
+#ifndef OFILL
+# define OFILL 0
+#endif
+#ifndef OLCUC
+# define OLCUC 0
+#endif
+#ifndef ONLCR
+# define ONLCR 0
+#endif
+#ifndef ONLRET
+# define ONLRET 0
+#endif
+#ifndef ONOCR
+# define ONOCR 0
+#endif
+#ifndef OXTABS
+# define OXTABS 0
+#endif
+#ifndef TABDLY
+# define TABDLY 0
+#endif
+#ifndef TAB1
+# define TAB1 0
+#endif
+#ifndef TAB2
+# define TAB2 0
+#endif
+#ifndef TOSTOP
+# define TOSTOP 0
+#endif
+#ifndef VDSUSP
+# define VDSUSP 0
+#endif
+#ifndef VEOL2
+# define VEOL2 0
+#endif
+#ifndef VFLUSHO
+# define VFLUSHO 0
+#endif
+#ifndef VLNEXT
+# define VLNEXT 0
+#endif
+#ifndef VREPRINT
+# define VREPRINT 0
+#endif
+#ifndef VSTATUS
+# define VSTATUS 0
+#endif
+#ifndef VSWTCH
+# define VSWTCH 0
+#endif
+#ifndef VTDLY
+# define VTDLY 0
+#endif
+#ifndef VWERASE
+# define VWERASE 0
+#endif
+#ifndef XCASE
+# define XCASE 0
+#endif
+#ifndef IUTF8
+# define IUTF8 0
+#endif
+
 /* Which speeds to set */
 enum speed_setting {
        input_speed, output_speed, both_speeds
@@ -122,10 +246,21 @@ enum speed_setting {
 
 /* Which member(s) of 'struct termios' a mode uses */
 enum {
-       /* Do NOT change the order or values, as mode_type_flag()
-        * depends on them */
        control, input, output, local, combination
 };
+static tcflag_t *get_ptr_to_tcflag(unsigned type, const struct termios *mode)
+{
+       static const uint8_t tcflag_offsets[] ALIGN1 = {
+               offsetof(struct termios, c_cflag), /* control */
+               offsetof(struct termios, c_iflag), /* input */
+               offsetof(struct termios, c_oflag), /* output */
+               offsetof(struct termios, c_lflag)  /* local */
+       };
+       if (type <= local) {
+               return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
+       }
+       return NULL;
+}
 
 /* Flags for 'struct mode_info' */
 #define SANE_SET 1              /* Set in 'sane' mode                  */
@@ -167,13 +302,13 @@ enum {
        IDX_cbreak,
        IDX_crt,
        IDX_dec,
-#ifdef IXANY
+#if IXANY
        IDX_decctlq,
 #endif
-#if defined(TABDLY) || defined(OXTABS)
+#if TABDLY || OXTABS
        IDX_tabs,
 #endif
-#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
+#if XCASE && IUCLC && OLCUC
        IDX_lcase,
        IDX_LCASE,
 #endif
@@ -196,13 +331,13 @@ static const char mode_name[] =
        MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
        MI_ENTRY("crt",      combination, OMIT,              0,          0 )
        MI_ENTRY("dec",      combination, OMIT,              0,          0 )
-#ifdef IXANY
+#if IXANY
        MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
 #endif
-#if defined(TABDLY) || defined(OXTABS)
+#if TABDLY || OXTABS
        MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
 #endif
-#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
+#if XCASE && IUCLC && OLCUC
        MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
        MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
 #endif
@@ -217,7 +352,7 @@ static const char mode_name[] =
        MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
        MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
        MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
-#ifdef CRTSCTS
+#if CRTSCTS
        MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
 #endif
        MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
@@ -231,100 +366,107 @@ static const char mode_name[] =
        MI_ENTRY("icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 )
        MI_ENTRY("ixon",     input,       REV,               IXON,       0 )
        MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 )
-       MI_ENTRY("tandem",   input,       REV        | OMIT, IXOFF,      0 )
-#ifdef IUCLC
+       MI_ENTRY("tandem",   input,       OMIT       | REV,  IXOFF,      0 )
+#if IUCLC
        MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
 #endif
-#ifdef IXANY
+#if IXANY
        MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
 #endif
-#ifdef IMAXBEL
+#if IMAXBEL
        MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 )
 #endif
+#if IUTF8
+       MI_ENTRY("iutf8",    input,       SANE_UNSET | REV,  IUTF8,      0 )
+#endif
        MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 )
-#ifdef OLCUC
+#if OLCUC
        MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
 #endif
-#ifdef OCRNL
+#if OCRNL
        MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
 #endif
-#ifdef ONLCR
+#if ONLCR
        MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
 #endif
-#ifdef ONOCR
+#if ONOCR
        MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
 #endif
-#ifdef ONLRET
+#if ONLRET
        MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
 #endif
-#ifdef OFILL
+#if OFILL
        MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
 #endif
-#ifdef OFDEL
+#if OFDEL
        MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
 #endif
-#ifdef NLDLY
+#if NLDLY
        MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
        MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
 #endif
-#ifdef CRDLY
+#if CRDLY
        MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY)
        MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY)
        MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY)
        MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY)
 #endif
 
-#ifdef TABDLY
+#if TABDLY
        MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY)
+# if TAB2
        MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY)
+# endif
+# if TAB1
        MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY)
+# endif
        MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY)
 #else
-# ifdef OXTABS
+# if OXTABS
        MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
 # endif
 #endif
 
-#ifdef BSDLY
+#if BSDLY
        MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
        MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
 #endif
-#ifdef VTDLY
+#if VTDLY
        MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
        MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
 #endif
-#ifdef FFDLY
+#if FFDLY
        MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY)
        MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY)
 #endif
        MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 )
        MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 )
-#ifdef IEXTEN
+#if IEXTEN
        MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 )
 #endif
        MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 )
        MI_ENTRY("echoe",    local,       SANE_SET   | REV,  ECHOE,      0 )
-       MI_ENTRY("crterase", local,       REV        | OMIT, ECHOE,      0 )
+       MI_ENTRY("crterase", local,       OMIT       | REV,  ECHOE,      0 )
        MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 )
        MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 )
        MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 )
-#ifdef XCASE
+#if XCASE
        MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
 #endif
-#ifdef TOSTOP
+#if TOSTOP
        MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
 #endif
-#ifdef ECHOPRT
+#if ECHOPRT
        MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
-       MI_ENTRY("prterase", local,       REV | OMIT,        ECHOPRT,    0 )
+       MI_ENTRY("prterase", local,       OMIT       | REV,  ECHOPRT,    0 )
 #endif
-#ifdef ECHOCTL
+#if ECHOCTL
        MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
-       MI_ENTRY("ctlecho",  local,       REV        | OMIT, ECHOCTL,    0 )
+       MI_ENTRY("ctlecho",  local,       OMIT       | REV,  ECHOCTL,    0 )
 #endif
-#ifdef ECHOKE
+#if ECHOKE
        MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
-       MI_ENTRY("crtkill",  local,       REV        | OMIT, ECHOKE,     0 )
+       MI_ENTRY("crtkill",  local,       OMIT       | REV,  ECHOKE,     0 )
 #endif
        ;
 
@@ -346,13 +488,13 @@ static const struct mode_info mode_info[] = {
        MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
        MI_ENTRY("crt",      combination, OMIT,              0,          0 )
        MI_ENTRY("dec",      combination, OMIT,              0,          0 )
-#ifdef IXANY
+#if IXANY
        MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
 #endif
-#if defined(TABDLY) || defined(OXTABS)
+#if TABDLY || OXTABS
        MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
 #endif
-#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
+#if XCASE && IUCLC && OLCUC
        MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
        MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
 #endif
@@ -367,7 +509,7 @@ static const struct mode_info mode_info[] = {
        MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
        MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
        MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
-#ifdef CRTSCTS
+#if CRTSCTS
        MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
 #endif
        MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
@@ -381,100 +523,107 @@ static const struct mode_info mode_info[] = {
        MI_ENTRY("icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 )
        MI_ENTRY("ixon",     input,       REV,               IXON,       0 )
        MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 )
-       MI_ENTRY("tandem",   input,       REV        | OMIT, IXOFF,      0 )
-#ifdef IUCLC
+       MI_ENTRY("tandem",   input,       OMIT       | REV,  IXOFF,      0 )
+#if IUCLC
        MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
 #endif
-#ifdef IXANY
+#if IXANY
        MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
 #endif
-#ifdef IMAXBEL
+#if IMAXBEL
        MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 )
 #endif
+#if IUTF8
+       MI_ENTRY("iutf8",    input,       SANE_UNSET | REV,  IUTF8,      0 )
+#endif
        MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 )
-#ifdef OLCUC
+#if OLCUC
        MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
 #endif
-#ifdef OCRNL
+#if OCRNL
        MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
 #endif
-#ifdef ONLCR
+#if ONLCR
        MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
 #endif
-#ifdef ONOCR
+#if ONOCR
        MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
 #endif
-#ifdef ONLRET
+#if ONLRET
        MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
 #endif
-#ifdef OFILL
+#if OFILL
        MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
 #endif
-#ifdef OFDEL
+#if OFDEL
        MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
 #endif
-#ifdef NLDLY
+#if NLDLY
        MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
        MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
 #endif
-#ifdef CRDLY
+#if CRDLY
        MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY)
        MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY)
        MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY)
        MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY)
 #endif
 
-#ifdef TABDLY
+#if TABDLY
        MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY)
+# if TAB2
        MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY)
+# endif
+# if TAB1
        MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY)
+# endif
        MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY)
 #else
-# ifdef OXTABS
+# if OXTABS
        MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
 # endif
 #endif
 
-#ifdef BSDLY
+#if BSDLY
        MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
        MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
 #endif
-#ifdef VTDLY
+#if VTDLY
        MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
        MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
 #endif
-#ifdef FFDLY
+#if FFDLY
        MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY)
        MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY)
 #endif
        MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 )
        MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 )
-#ifdef IEXTEN
+#if IEXTEN
        MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 )
 #endif
        MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 )
        MI_ENTRY("echoe",    local,       SANE_SET   | REV,  ECHOE,      0 )
-       MI_ENTRY("crterase", local,       REV        | OMIT, ECHOE,      0 )
+       MI_ENTRY("crterase", local,       OMIT       | REV,  ECHOE,      0 )
        MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 )
        MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 )
        MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 )
-#ifdef XCASE
+#if XCASE
        MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
 #endif
-#ifdef TOSTOP
+#if TOSTOP
        MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
 #endif
-#ifdef ECHOPRT
+#if ECHOPRT
        MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
-       MI_ENTRY("prterase", local,       REV | OMIT,        ECHOPRT,    0 )
+       MI_ENTRY("prterase", local,       OMIT       | REV,  ECHOPRT,    0 )
 #endif
-#ifdef ECHOCTL
+#if ECHOCTL
        MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
-       MI_ENTRY("ctlecho",  local,       REV        | OMIT, ECHOCTL,    0 )
+       MI_ENTRY("ctlecho",  local,       OMIT       | REV,  ECHOCTL,    0 )
 #endif
-#ifdef ECHOKE
+#if ECHOKE
        MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
-       MI_ENTRY("crtkill",  local,       REV        | OMIT, ECHOKE,     0 )
+       MI_ENTRY("crtkill",  local,       OMIT       | REV,  ECHOKE,     0 )
 #endif
 };
 
@@ -497,31 +646,31 @@ enum {
        CIDX_kill,
        CIDX_eof,
        CIDX_eol,
-#ifdef VEOL2
+#if VEOL2
        CIDX_eol2,
 #endif
-#ifdef VSWTCH
+#if VSWTCH
        CIDX_swtch,
 #endif
        CIDX_start,
        CIDX_stop,
        CIDX_susp,
-#ifdef VDSUSP
+#if VDSUSP
        CIDX_dsusp,
 #endif
-#ifdef VREPRINT
+#if VREPRINT
        CIDX_rprnt,
 #endif
-#ifdef VWERASE
+#if VWERASE
        CIDX_werase,
 #endif
-#ifdef VLNEXT
+#if VLNEXT
        CIDX_lnext,
 #endif
-#ifdef VFLUSHO
+#if VFLUSHO
        CIDX_flush,
 #endif
-#ifdef VSTATUS
+#if VSTATUS
        CIDX_status,
 #endif
        CIDX_min,
@@ -538,31 +687,31 @@ static const char control_name[] =
        CI_ENTRY("kill",     CKILL,   VKILL   )
        CI_ENTRY("eof",      CEOF,    VEOF    )
        CI_ENTRY("eol",      CEOL,    VEOL    )
-#ifdef VEOL2
+#if VEOL2
        CI_ENTRY("eol2",     CEOL2,   VEOL2   )
 #endif
-#ifdef VSWTCH
+#if VSWTCH
        CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
 #endif
        CI_ENTRY("start",    CSTART,  VSTART  )
        CI_ENTRY("stop",     CSTOP,   VSTOP   )
        CI_ENTRY("susp",     CSUSP,   VSUSP   )
-#ifdef VDSUSP
+#if VDSUSP
        CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
 #endif
-#ifdef VREPRINT
+#if VREPRINT
        CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
 #endif
-#ifdef VWERASE
+#if VWERASE
        CI_ENTRY("werase",   CWERASE, VWERASE )
 #endif
-#ifdef VLNEXT
+#if VLNEXT
        CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
 #endif
-#ifdef VFLUSHO
+#if VFLUSHO
        CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
 #endif
-#ifdef VSTATUS
+#if VSTATUS
        CI_ENTRY("status",   CSTATUS, VSTATUS )
 #endif
        /* These must be last because of the display routines */
@@ -581,31 +730,31 @@ static const struct control_info control_info[] = {
        CI_ENTRY("kill",     CKILL,   VKILL   )
        CI_ENTRY("eof",      CEOF,    VEOF    )
        CI_ENTRY("eol",      CEOL,    VEOL    )
-#ifdef VEOL2
+#if VEOL2
        CI_ENTRY("eol2",     CEOL2,   VEOL2   )
 #endif
-#ifdef VSWTCH
+#if VSWTCH
        CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
 #endif
        CI_ENTRY("start",    CSTART,  VSTART  )
        CI_ENTRY("stop",     CSTOP,   VSTOP   )
        CI_ENTRY("susp",     CSUSP,   VSUSP   )
-#ifdef VDSUSP
+#if VDSUSP
        CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
 #endif
-#ifdef VREPRINT
+#if VREPRINT
        CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
 #endif
-#ifdef VWERASE
+#if VWERASE
        CI_ENTRY("werase",   CWERASE, VWERASE )
 #endif
-#ifdef VLNEXT
+#if VLNEXT
        CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
 #endif
-#ifdef VFLUSHO
+#if VFLUSHO
        CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
 #endif
-#ifdef VSTATUS
+#if VSTATUS
        CI_ENTRY("status",   CSTATUS, VSTATUS )
 #endif
        /* These must be last because of the display routines */
@@ -632,51 +781,6 @@ struct globals {
        G.max_col = 80; \
 } while (0)
 
-
-/* Return a string that is the printable representation of character CH */
-/* Adapted from 'cat' by Torbjorn Granlund */
-static const char *visible(unsigned ch)
-{
-       char *bpout = G.buf;
-
-       if (ch == _POSIX_VDISABLE)
-               return "<undef>";
-
-       if (ch >= 128) {
-               ch -= 128;
-               *bpout++ = 'M';
-               *bpout++ = '-';
-       }
-
-       if (ch < 32) {
-               *bpout++ = '^';
-               *bpout++ = ch + 64;
-       } else if (ch < 127) {
-               *bpout++ = ch;
-       } else {
-               *bpout++ = '^';
-               *bpout++ = '?';
-       }
-
-       *bpout = '\0';
-       return G.buf;
-}
-
-static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
-{
-       static const uint8_t tcflag_offsets[] ALIGN1 = {
-               offsetof(struct termios, c_cflag), /* control */
-               offsetof(struct termios, c_iflag), /* input */
-               offsetof(struct termios, c_oflag), /* output */
-               offsetof(struct termios, c_lflag)  /* local */
-       };
-
-       if (type <= local) {
-               return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
-       }
-       return NULL;
-}
-
 static void set_speed_or_die(enum speed_setting type, const char *arg,
                                        struct termios *mode)
 {
@@ -740,6 +844,7 @@ static void newline(void)
                wrapf("\n");
 }
 
+#ifdef TIOCGWINSZ
 static void set_window_size(int rows, int cols)
 {
        struct winsize win = { 0, 0, 0, 0 };
@@ -760,6 +865,7 @@ static void set_window_size(int rows, int cols)
 bail:
                perror_on_device("%s");
 }
+#endif
 
 static void display_window_size(int fancy)
 {
@@ -874,8 +980,9 @@ static void display_speed(const struct termios *mode, int fancy)
        const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
        unsigned long ispeed, ospeed;
 
-       ospeed = ispeed = cfgetispeed(mode);
-       if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
+       ispeed = cfgetispeed(mode);
+       ospeed = cfgetospeed(mode);
+       if (ispeed == 0 || ispeed == ospeed) {
                ispeed = ospeed;                /* in case ispeed was 0 */
                //________ 0123 4 5 6 7 8 9
                fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
@@ -894,13 +1001,14 @@ static void do_display(const struct termios *mode, int all)
        display_speed(mode, 1);
        if (all)
                display_window_size(1);
-#ifdef HAVE_C_LINE
+#ifdef __linux__
        wrapf("line = %u;\n", mode->c_line);
 #else
        newline();
 #endif
 
        for (i = 0; i != CIDX_min; ++i) {
+               char ch;
                /* If swtch is the same as susp, don't print both */
 #if VSWTCH == VSUSP
                if (i == CIDX_swtch)
@@ -914,8 +1022,12 @@ static void do_display(const struct termios *mode, int all)
                        continue;
                }
 #endif
-               wrapf("%s = %s;", nth_string(control_name, i),
-                         visible(mode->c_cc[control_info[i].offset]));
+               ch = mode->c_cc[control_info[i].offset];
+               if (ch == _POSIX_VDISABLE)
+                       strcpy(G.buf, "<undef>");
+               else
+                       visible(ch, G.buf, 0);
+               wrapf("%s = %s;", nth_string(control_name, i), G.buf);
        }
 #if VEOF == VMIN
        if ((mode->c_lflag & ICANON) == 0)
@@ -931,7 +1043,7 @@ static void do_display(const struct termios *mode, int all)
                        prev_type = mode_info[i].type;
                }
 
-               bitsp = mode_type_flag(mode_info[i].type, mode);
+               bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
                mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
                if ((*bitsp & mask) == mode_info[i].bits) {
                        if (all || (mode_info[i].flags & SANE_UNSET))
@@ -950,7 +1062,6 @@ static void do_display(const struct termios *mode, int all)
 static void sane_mode(struct termios *mode)
 {
        int i;
-       tcflag_t *bitsp;
 
        for (i = 0; i < NUM_control_info; ++i) {
 #if VMIN == VEOF
@@ -961,83 +1072,52 @@ static void sane_mode(struct termios *mode)
        }
 
        for (i = 0; i < NUM_mode_info; ++i) {
+               tcflag_t val;
+               tcflag_t *bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
+
+               if (!bitsp)
+                       continue;
+               val = *bitsp & ~((unsigned long)mode_info[i].mask);
                if (mode_info[i].flags & SANE_SET) {
-                       bitsp = mode_type_flag(mode_info[i].type, mode);
-                       *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
-                               | mode_info[i].bits;
-               } else if (mode_info[i].flags & SANE_UNSET) {
-                       bitsp = mode_type_flag(mode_info[i].type, mode);
-                       *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
-                               & ~mode_info[i].bits;
+                       *bitsp = val | mode_info[i].bits;
+               } else
+               if (mode_info[i].flags & SANE_UNSET) {
+                       *bitsp = val & ~mode_info[i].bits;
                }
        }
 }
 
-/* Save set_mode from #ifdef forest plague */
-#ifndef ONLCR
-#define ONLCR 0
-#endif
-#ifndef OCRNL
-#define OCRNL 0
-#endif
-#ifndef ONLRET
-#define ONLRET 0
-#endif
-#ifndef XCASE
-#define XCASE 0
-#endif
-#ifndef IXANY
-#define IXANY 0
-#endif
-#ifndef TABDLY
-#define TABDLY 0
-#endif
-#ifndef OXTABS
-#define OXTABS 0
-#endif
-#ifndef IUCLC
-#define IUCLC 0
-#endif
-#ifndef OLCUC
-#define OLCUC 0
-#endif
-#ifndef ECHOCTL
-#define ECHOCTL 0
-#endif
-#ifndef ECHOKE
-#define ECHOKE 0
-#endif
-
 static void set_mode(const struct mode_info *info, int reversed,
                                        struct termios *mode)
 {
        tcflag_t *bitsp;
 
-       bitsp = mode_type_flag(info->type, mode);
+       bitsp = get_ptr_to_tcflag(info->type, mode);
 
        if (bitsp) {
+               tcflag_t val = *bitsp & ~info->mask;
                if (reversed)
-                       *bitsp = *bitsp & ~info->mask & ~info->bits;
+                       *bitsp = val & ~info->bits;
                else
-                       *bitsp = (*bitsp & ~info->mask) | info->bits;
+                       *bitsp = val | info->bits;
                return;
        }
 
-       /* Combination mode */
+       /* !bitsp - it's a "combination" mode */
        if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
                if (reversed)
                        mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
                else
-                       mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
+                       mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
        } else if (info == &mode_info[IDX_oddp]) {
                if (reversed)
                        mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
                else
-                       mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
+                       mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
        } else if (info == &mode_info[IDX_nl]) {
                if (reversed) {
                        mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
-                       mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
+                       mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
                } else {
                        mode->c_iflag = mode->c_iflag & ~ICRNL;
                        if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
@@ -1093,27 +1173,32 @@ static void set_mode(const struct mode_info *info, int reversed,
                        mode->c_cc[VTIME] = 0;
                }
        }
-       else if (IXANY && info == &mode_info[IDX_decctlq]) {
+#if IXANY
+       else if (info == &mode_info[IDX_decctlq]) {
                if (reversed)
                        mode->c_iflag |= IXANY;
                else
                        mode->c_iflag &= ~IXANY;
        }
-       else if (TABDLY && info == &mode_info[IDX_tabs]) {
+#endif
+#if TABDLY
+       else if (info == &mode_info[IDX_tabs]) {
                if (reversed)
                        mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
                else
                        mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
        }
-       else if (OXTABS && info == &mode_info[IDX_tabs]) {
+#endif
+#if OXTABS
+       else if (info == &mode_info[IDX_tabs]) {
                if (reversed)
                        mode->c_oflag |= OXTABS;
                else
                        mode->c_oflag &= ~OXTABS;
-       } else
-       if (XCASE && IUCLC && OLCUC
-        && (info == &mode_info[IDX_lcase] || info == &mode_info[IDX_LCASE])
-       ) {
+       }
+#endif
+#if XCASE && IUCLC && OLCUC
+       else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) {
                if (reversed) {
                        mode->c_lflag &= ~XCASE;
                        mode->c_iflag &= ~IUCLC;
@@ -1123,7 +1208,9 @@ static void set_mode(const struct mode_info *info, int reversed,
                        mode->c_iflag |= IUCLC;
                        mode->c_oflag |= OLCUC;
                }
-       } else if (info == &mode_info[IDX_crt]) {
+       }
+#endif
+       else if (info == &mode_info[IDX_crt]) {
                mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
        } else if (info == &mode_info[IDX_dec]) {
                mode->c_cc[VINTR] = 3; /* ^C */
@@ -1255,7 +1342,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
                }
 
                switch (param) {
-#ifdef HAVE_C_LINE
+#ifdef __linux__
                case param_line:
 # ifndef TIOCGWINSZ
                        xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
@@ -1291,13 +1378,15 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
 
        /* Specifying both -a and -g is an error */
        if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
-               (STTY_verbose_output | STTY_recoverable_output))
-               bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
+               (STTY_verbose_output | STTY_recoverable_output)
+       ) {
+               bb_error_msg_and_die("-a and -g are mutually exclusive");
+       }
        /* Specifying -a or -g with non-options is an error */
-       if (!(stty_state & STTY_noargs)
-        && (stty_state & (STTY_verbose_output | STTY_recoverable_output))
+       if ((stty_state & (STTY_verbose_output | STTY_recoverable_output))
+        && !(stty_state & STTY_noargs)
        ) {
-               bb_error_msg_and_die("modes may not be set when specifying an output style");
+               bb_error_msg_and_die("modes may not be set when -a or -g is used");
        }
 
        /* Now it is safe to start doing things */
@@ -1359,7 +1448,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
                }
 
                switch (param) {
-#ifdef HAVE_C_LINE
+#ifdef __linux__
                case param_line:
                        mode.c_line = xatoul_sfx(argnext, stty_suffixes);
                        stty_state |= STTY_require_set_attr;
@@ -1419,7 +1508,12 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
                        perror_on_device_and_die("%s");
 
                if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
-#ifdef CIBAUD
+/*
+ * I think the below chunk is not necessary on Linux.
+ * If you are deleting it, also delete STTY_speed_was_set bit -
+ * it is only ever checked here.
+ */
+#if 0 /* was "if CIBAUD" */
                        /* SunOS 4.1.3 (at least) has the problem that after this sequence,
                           tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
                           sometimes (m1 != m2).  The only difference is in the four bits
index 35e89a6..deb068e 100644 (file)
  * Written by Kayvan Aghaiepour and David MacKenzie
  * Taken from coreutils and turned into a busybox applet by Mike Frysinger
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define sum_trivial_usage
+//usage:       "[-rs] [FILE]..."
+//usage:#define sum_full_usage "\n\n"
+//usage:       "Checksum and count the blocks in a file\n"
+//usage:     "\n       -r      Use BSD sum algorithm (1K blocks)"
+//usage:     "\n       -s      Use System V sum algorithm (512byte blocks)"
+
 #include "libbb.h"
 
 enum { SUM_BSD, PRINT_NAME, SUM_SYSV };
@@ -63,9 +70,9 @@ static unsigned sum_file(const char *file, unsigned type)
        if (type >= SUM_SYSV) {
                r = (s & 0xffff) + ((s & 0xffffffff) >> 16);
                s = (r & 0xffff) + (r >> 16);
-               printf("%d %llu %s\n", s, (total_bytes + 511) / 512, file);
+               printf("%u %llu %s\n", s, (total_bytes + 511) / 512, file);
        } else
-               printf("%05d %5llu %s\n", s, (total_bytes + 1023) / 1024, file);
+               printf("%05u %5llu %s\n", s, (total_bytes + 1023) / 1024, file);
        return 1;
 #undef buf
 }
@@ -87,8 +94,8 @@ int sum_main(int argc UNUSED_PARAM, char **argv)
                n = sum_file("-", type);
        } else {
                /* Need to print the name if either
-                  - more than one file given
-                  - doing sysv */
+                * - more than one file given
+                * - doing sysv */
                type += (argv[1] || type == SUM_SYSV);
                n = 1;
                do {
index 59305c6..7d98a1e 100644 (file)
@@ -4,11 +4,16 @@
  *
  * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
 
+//usage:#define sync_trivial_usage
+//usage:       ""
+//usage:#define sync_full_usage "\n\n"
+//usage:       "Write all buffered blocks to disk"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
index d70e23a..94d669d 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (C) 2007  Natanael Copa  <natanael.copa@gmail.com>
  * Copyright (C) 2007  Tito Ragusa    <farmatito@tiscali.it>
  *
- * Licensed under GPLv2, see file License in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  *
  */
 
  * http://www.uclibc.org/lists/busybox/2003-July/008813.html
  */
 
+//usage:#define tac_trivial_usage
+//usage:       "[FILE]..."
+//usage:#define tac_full_usage "\n\n"
+//usage:       "Concatenate FILEs and print them in reverse"
+
 #include "libbb.h"
 
 /* This is a NOEXEC applet. Be very careful! */
index 6397702..eab502b 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant (need fancy for -c) */
  * 7) lseek attempted when count==0 even if arg was +0 (from top)
  */
 
-#include "libbb.h"
+//kbuild:lib-$(CONFIG_TAIL) += tail.o
+
+//usage:#define tail_trivial_usage
+//usage:       "[OPTIONS] [FILE]..."
+//usage:#define tail_full_usage "\n\n"
+//usage:       "Print last 10 lines of each FILE (or stdin) to stdout.\n"
+//usage:       "With more than one FILE, precede each with a filename header.\n"
+//usage:     "\n       -f              Print data as file grows"
+//usage:       IF_FEATURE_FANCY_TAIL(
+//usage:     "\n       -s SECONDS      Wait SECONDS between reads with -f"
+//usage:       )
+//usage:     "\n       -n N[kbm]       Print last N lines"
+//usage:     "\n       -n +N[kbm]      Start on Nth line and print the rest"
+//usage:       IF_FEATURE_FANCY_TAIL(
+//usage:     "\n       -c [+]N[kbm]    Print last N bytes"
+//usage:     "\n       -q              Never print headers"
+//usage:     "\n       -v              Always print headers"
+//usage:     "\n"
+//usage:     "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)."
+//usage:       )
+//usage:
+//usage:#define tail_example_usage
+//usage:       "$ tail -n 1 /etc/resolv.conf\n"
+//usage:       "nameserver 10.0.0.1\n"
 
-static const struct suffix_mult tail_suffixes[] = {
-       { "b", 512 },
-       { "k", 1024 },
-       { "m", 1024*1024 },
-       { "", 0 }
-};
+#include "libbb.h"
 
 struct globals {
-       bool status;
+       bool from_top;
+       bool exitcode;
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { } while (0)
 
 static void tail_xprint_header(const char *fmt, const char *filename)
 {
@@ -47,20 +67,11 @@ static void tail_xprint_header(const char *fmt, const char *filename)
 static ssize_t tail_read(int fd, char *buf, size_t count)
 {
        ssize_t r;
-       off_t current;
-       struct stat sbuf;
-
-       /* /proc files report zero st_size, don't lseek them. */
-       if (fstat(fd, &sbuf) == 0 && sbuf.st_size > 0) {
-               current = lseek(fd, 0, SEEK_CUR);
-               if (sbuf.st_size < current)
-                       xlseek(fd, 0, SEEK_SET);
-       }
 
        r = full_read(fd, buf, count);
        if (r < 0) {
                bb_perror_msg(bb_msg_read_error);
-               G.status = EXIT_FAILURE;
+               G.exitcode = EXIT_FAILURE;
        }
 
        return r;
@@ -74,9 +85,9 @@ static unsigned eat_num(const char *p)
                p++;
        else if (*p == '+') {
                p++;
-               G.status = 1; /* mark that we saw "+" */
+               G.from_top = 1;
        }
-       return xatou_sfx(p, tail_suffixes);
+       return xatou_sfx(p, bkm_suffixes);
 }
 
 int tail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -84,7 +95,6 @@ int tail_main(int argc, char **argv)
 {
        unsigned count = 10;
        unsigned sleep_period = 1;
-       bool from_top;
        const char *str_c, *str_n;
 
        char *tailbuf;
@@ -95,6 +105,9 @@ int tail_main(int argc, char **argv)
 
        int *fds;
        const char *fmt;
+       int prev_fd;
+
+       INIT_G();
 
 #if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_TAIL
        /* Allow legacy syntax of an initial numeric option without -n. */
@@ -127,8 +140,6 @@ int tail_main(int argc, char **argv)
 #endif
        argc -= optind;
        argv += optind;
-       from_top = G.status; /* 1 if there was "-c +N" or "-n +N" */
-       G.status = EXIT_SUCCESS;
 
        /* open all the files */
        fds = xmalloc(sizeof(fds[0]) * (argc + 1));
@@ -146,7 +157,7 @@ int tail_main(int argc, char **argv)
        do {
                int fd = open_or_warn_stdin(argv[i]);
                if (fd < 0 && !FOLLOW_RETRY) {
-                       G.status = EXIT_FAILURE;
+                       G.exitcode = EXIT_FAILURE;
                        continue;
                }
                fds[nfiles] = fd;
@@ -158,15 +169,19 @@ int tail_main(int argc, char **argv)
 
        /* prepare the buffer */
        tailbufsize = BUFSIZ;
-       if (!from_top && COUNT_BYTES) {
+       if (!G.from_top && COUNT_BYTES) {
                if (tailbufsize < count + BUFSIZ) {
                        tailbufsize = count + BUFSIZ;
                }
        }
-       tailbuf = xmalloc(tailbufsize);
+       /* tail -c1024m REGULAR_FILE doesn't really need 1G mem block.
+        * (In fact, it doesn't need ANY memory). So delay allocation.
+        */
+       tailbuf = NULL;
 
        /* tail the files */
-       fmt = header_fmt_str + 1; /* skip header leading newline on first output */
+
+       fmt = header_fmt_str + 1; /* skip leading newline in the header on the first output */
        i = 0;
        do {
                char *buf;
@@ -177,14 +192,14 @@ int tail_main(int argc, char **argv)
                int fd = fds[i];
 
                if (ENABLE_FEATURE_FANCY_TAIL && fd < 0)
-                       continue; /* may happen with -E */
+                       continue; /* may happen with -F */
 
                if (nfiles > header_threshhold) {
                        tail_xprint_header(fmt, argv[i]);
                        fmt = header_fmt_str;
                }
 
-               if (!from_top) {
+               if (!G.from_top) {
                        off_t current = lseek(fd, 0, SEEK_END);
                        if (current > 0) {
                                unsigned off;
@@ -217,20 +232,23 @@ int tail_main(int argc, char **argv)
                        }
                }
 
+               if (!tailbuf)
+                       tailbuf = xmalloc(tailbufsize);
+
                buf = tailbuf;
                taillen = 0;
                /* "We saw 1st line/byte".
                 * Used only by +N code ("start from Nth", 1-based): */
                seen = 1;
                newlines_seen = 0;
-               while ((nread = tail_read(fd, buf, tailbufsize-taillen)) > 0) {
-                       if (from_top) {
+               while ((nread = tail_read(fd, buf, tailbufsize - taillen)) > 0) {
+                       if (G.from_top) {
                                int nwrite = nread;
                                if (seen < count) {
                                        /* We need to skip a few more bytes/lines */
                                        if (COUNT_BYTES) {
                                                nwrite -= (count - seen);
-                                               seen = count;
+                                               seen += nread;
                                        } else {
                                                char *s = buf;
                                                do {
@@ -288,10 +306,11 @@ int tail_main(int argc, char **argv)
                                buf = tailbuf + taillen;
                        }
                } /* while (tail_read() > 0) */
-               if (!from_top) {
+               if (!G.from_top) {
                        xwrite(STDOUT_FILENO, tailbuf, taillen);
                }
        } while (++i < nfiles);
+       prev_fd = fds[i-1];
 
        tailbuf = xrealloc(tailbuf, BUFSIZ);
 
@@ -335,17 +354,32 @@ int tail_main(int argc, char **argv)
                        if (nfiles > header_threshhold) {
                                fmt = header_fmt_str;
                        }
-                       while ((nread = tail_read(fd, tailbuf, BUFSIZ)) > 0) {
-                               if (fmt) {
+                       for (;;) {
+                               /* tail -f keeps following files even if they are truncated */
+                               struct stat sbuf;
+                               /* /proc files report zero st_size, don't lseek them */
+                               if (fstat(fd, &sbuf) == 0 && sbuf.st_size > 0) {
+                                       off_t current = lseek(fd, 0, SEEK_CUR);
+                                       if (sbuf.st_size < current)
+                                               xlseek(fd, 0, SEEK_SET);
+                               }
+
+                               nread = tail_read(fd, tailbuf, BUFSIZ);
+                               if (nread <= 0)
+                                       break;
+                               if (fmt && (fd != prev_fd)) {
                                        tail_xprint_header(fmt, filename);
                                        fmt = NULL;
+                                       prev_fd = fd;
                                }
                                xwrite(STDOUT_FILENO, tailbuf, nread);
                        }
                } while (++i < nfiles);
-       }
+       } /* while (1) */
+
        if (ENABLE_FEATURE_CLEAN_UP) {
                free(fds);
+               free(tailbuf);
        }
-       return G.status;
+       return G.exitcode;
 }
index 0f24246..48cc050 100644 (file)
@@ -4,12 +4,24 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/tee.html */
 
+//usage:#define tee_trivial_usage
+//usage:       "[-ai] [FILE]..."
+//usage:#define tee_full_usage "\n\n"
+//usage:       "Copy stdin to each FILE, and also to stdout\n"
+//usage:     "\n       -a      Append to the given FILEs, don't overwrite"
+//usage:     "\n       -i      Ignore interrupt signals (SIGINT)"
+//usage:
+//usage:#define tee_example_usage
+//usage:       "$ echo \"Hello\" | tee /tmp/foo\n"
+//usage:       "$ cat /tmp/foo\n"
+//usage:       "Hello\n"
+
 #include "libbb.h"
 
 int tee_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -42,7 +54,7 @@ int tee_main(int argc, char **argv)
         * that doesn't consume all its input.  Good idea... */
        signal(SIGPIPE, SIG_IGN);
 
-       /* Allocate an array of FILE *'s, with one extra for a sentinal. */
+       /* Allocate an array of FILE *'s, with one extra for a sentinel. */
        fp = files = xzalloc(sizeof(FILE *) * (argc + 2));
        np = names = argv - 1;
 
@@ -70,8 +82,8 @@ int tee_main(int argc, char **argv)
        while ((c = safe_read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
                fp = files;
                do
-                       fwrite(buf, 1, c, *fp++);
-               while (*fp);
+                       fwrite(buf, 1, c, *fp);
+               while (*++fp);
        }
        if (c < 0) {            /* Make sure read errors are signaled. */
                retval = EXIT_FAILURE;
@@ -81,8 +93,8 @@ int tee_main(int argc, char **argv)
        while ((c = getchar()) != EOF) {
                fp = files;
                do
-                       putc(c, *fp++);
-               while (*fp);
+                       putc(c, *fp);
+               while (*++fp);
        }
 #endif
 
index 70eac5f..4df505a 100644 (file)
@@ -14,7 +14,7 @@
  *     in busybox.
  *     modified by Bernhard Reutner-Fischer to be useable (i.e. a bit less bloaty).
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Original copyright notice states:
  *     "This program is in the Public Domain."
 //config:      help
 //config:        Enable 64-bit support in test.
 
+/* "test --help" does not print help (POSIX compat), only "[ --help" does.
+ * We display "<applet> EXPRESSION ]" here (not "<applet> EXPRESSION")
+ * Unfortunately, it screws up generated BusyBox.html. TODO. */
+//usage:#define test_trivial_usage
+//usage:       "EXPRESSION ]"
+//usage:#define test_full_usage "\n\n"
+//usage:       "Check file types, compare values etc. Return a 0/1 exit code\n"
+//usage:       "depending on logical value of EXPRESSION"
+//usage:
+//usage:#define test_example_usage
+//usage:       "$ test 1 -eq 2\n"
+//usage:       "$ echo $?\n"
+//usage:       "1\n"
+//usage:       "$ test 1 -eq 1\n"
+//usage:       "$ echo $?\n"
+//usage:       "0\n"
+//usage:       "$ [ -d /etc ]\n"
+//usage:       "$ echo $?\n"
+//usage:       "0\n"
+//usage:       "$ [ -d /junk ]\n"
+//usage:       "$ echo $?\n"
+//usage:       "1\n"
+
 #include "libbb.h"
 #include <setjmp.h>
 
 /* This is a NOFORK applet. Be very careful! */
 
 /* test_main() is called from shells, and we need to be extra careful here.
- * This is true regardless of PREFER_APPLETS and STANDALONE_SHELL
+ * This is true regardless of PREFER_APPLETS and SH_STANDALONE
  * state. */
 
 /* test(1) accepts the following grammar:
-       oexpr   ::= aexpr | aexpr "-o" oexpr ;
-       aexpr   ::= nexpr | nexpr "-a" aexpr ;
-       nexpr   ::= primary | "!" primary
+       oexpr   ::= aexpr | aexpr "-o" oexpr ;
+       aexpr   ::= nexpr | nexpr "-a" aexpr ;
+       nexpr   ::= primary | "!" primary
        primary ::= unary-operator operand
                | operand binary-operator operand
                | operand
@@ -587,7 +610,7 @@ static int test_eaccess(char *path, int mode)
                        return 0;
 
                /* Root can execute any file that has any one of the execute
-                  bits set. */
+                * bits set. */
                if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
                        return 0;
        }
@@ -687,7 +710,8 @@ static number_t nexpr(enum token n)
                if (n == EOI) {
                        /* special case: [ ! ], [ a -a ! ] are valid */
                        /* IOW, "! ARG" may miss ARG */
-                       unnest_msg("<nexpr:1 (!EOI)\n");
+                       args--;
+                       unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
                        return 1;
                }
                res = !nexpr(n);
@@ -706,15 +730,15 @@ static number_t aexpr(enum token n)
 
        nest_msg(">aexpr(%s)\n", TOKSTR[n]);
        res = nexpr(n);
-       dbg_msg("aexpr: nexpr:%lld, next args:%s\n", res, args[1]);
+       dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
        if (check_operator(*++args) == BAND) {
-               dbg_msg("aexpr: arg is AND, next args:%s\n", args[1]);
+               dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
                res = aexpr(check_operator(*++args)) && res;
                unnest_msg("<aexpr:%lld\n", res);
                return res;
        }
        args--;
-       unnest_msg("<aexpr:%lld, args:%s\n", res, args[0]);
+       unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
        return res;
 }
 
@@ -725,15 +749,15 @@ static number_t oexpr(enum token n)
 
        nest_msg(">oexpr(%s)\n", TOKSTR[n]);
        res = aexpr(n);
-       dbg_msg("oexpr: aexpr:%lld, next args:%s\n", res, args[1]);
+       dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
        if (check_operator(*++args) == BOR) {
-               dbg_msg("oexpr: next arg is OR, next args:%s\n", args[1]);
+               dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
                res = oexpr(check_operator(*++args)) || res;
                unnest_msg("<oexpr:%lld\n", res);
                return res;
        }
        args--;
-       unnest_msg("<oexpr:%lld, args:%s\n", res, args[0]);
+       unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
        return res;
 }
 
@@ -878,7 +902,10 @@ int test_main(int argc, char **argv)
        res = !oexpr(check_operator(*args));
 
        if (*args != NULL && *++args != NULL) {
-               /* TODO: example when this happens? */
+               /* Examples:
+                * test 3 -lt 5 6
+                * test -t 1 2
+                */
                bb_error_msg("%s: unknown operand", *args);
                res = 2;
        }
index a05203d..5ba9dcc 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2008 by Denys Vlasenko <vda.linux@googlemail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 struct test_statics;
index dceb7c1..293a968 100644 (file)
@@ -4,10 +4,10 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
-/* BB_AUDIT SUSv3 _NOT_ compliant -- options -a, -m, -r, -t not supported. */
+/* BB_AUDIT SUSv3 _NOT_ compliant -- options -a, -m not supported. */
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/touch.html */
 
 /* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
 
 #include "libbb.h"
 
+//config:config TOUCH
+//config:      bool "touch"
+//config:      default y
+//config:      help
+//config:        touch is used to create or change the access and/or
+//config:        modification timestamp of specified files.
+//config:
+//config:config FEATURE_TOUCH_NODEREF
+//config:      bool "Add support for -h"
+//config:      default y
+//config:      depends on TOUCH
+//config:      help
+//config:        Enable touch to have the -h option.
+//config:        This requires libc support for lutimes() function.
+//config:
+//config:config FEATURE_TOUCH_SUSV3
+//config:      bool "Add support for SUSV3 features (-d -t -r)"
+//config:      default y
+//config:      depends on TOUCH
+//config:      help
+//config:        Enable touch to use a reference file or a given date/time argument.
+
+//applet:IF_TOUCH(APPLET_NOFORK(touch, touch, BB_DIR_BIN, BB_SUID_DROP, touch))
+
+//kbuild:lib-$(CONFIG_TOUCH) += touch.o
+
+//usage:#define touch_trivial_usage
+//usage:       "[-c]" IF_FEATURE_TOUCH_SUSV3(" [-d DATE] [-t DATE] [-r FILE]") " FILE..."
+//usage:#define touch_full_usage "\n\n"
+//usage:       "Update the last-modified date on the given FILE[s]\n"
+//usage:     "\n       -c      Don't create files"
+//usage:       IF_FEATURE_TOUCH_NODEREF(
+//usage:     "\n       -h      Don't follow links"
+//usage:       )
+//usage:       IF_FEATURE_TOUCH_SUSV3(
+//usage:     "\n       -d DT   Date/time to use"
+//usage:     "\n       -t DT   Date/time to use"
+//usage:     "\n       -r FILE Use FILE's date/time"
+//usage:       )
+//usage:
+//usage:#define touch_example_usage
+//usage:       "$ ls -l /tmp/foo\n"
+//usage:       "/bin/ls: /tmp/foo: No such file or directory\n"
+//usage:       "$ touch /tmp/foo\n"
+//usage:       "$ ls -l /tmp/foo\n"
+//usage:       "-rw-rw-r--    1 andersen andersen        0 Apr 15 01:11 /tmp/foo\n"
+
 /* This is a NOFORK applet. Be very careful! */
 
 /* coreutils implements:
@@ -29,6 +76,7 @@
  *      parse STRING and use it instead of current time
  * -f   (ignored, BSD compat)
  * -m   change only the modification time
+ * -h, --no-dereference
  * -r, --reference=FILE
  *      use this file's times instead of current time
  * -t STAMP
@@ -43,13 +91,21 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
        int fd;
        int status = EXIT_SUCCESS;
        int opts;
-#if ENABLE_DESKTOP
+       enum {
+               OPT_c = (1 << 0),
+               OPT_r = (1 << 1) * ENABLE_FEATURE_TOUCH_SUSV3,
+               OPT_d = (1 << 2) * ENABLE_FEATURE_TOUCH_SUSV3,
+               OPT_t = (1 << 3) * ENABLE_FEATURE_TOUCH_SUSV3,
+               OPT_h = (1 << 4) * ENABLE_FEATURE_TOUCH_NODEREF,
+       };
+#if ENABLE_FEATURE_TOUCH_SUSV3
 # if ENABLE_LONG_OPTS
        static const char touch_longopts[] ALIGN1 =
                /* name, has_arg, val */
                "no-create\0"         No_argument       "c"
                "reference\0"         Required_argument "r"
                "date\0"              Required_argument "d"
+               IF_FEATURE_TOUCH_NODEREF("no-dereference\0" No_argument "h")
        ;
 # endif
        char *reference_file = NULL;
@@ -62,20 +118,20 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
 # define timebuf        ((struct timeval*)NULL)
 #endif
 
-#if ENABLE_DESKTOP && ENABLE_LONG_OPTS
+#if ENABLE_FEATURE_TOUCH_SUSV3 && ENABLE_LONG_OPTS
        applet_long_options = touch_longopts;
 #endif
        /* -d and -t both set time. In coreutils,
         * accepted data format differs a bit between -d and -t.
         * We accept the same formats for both */
-       opts = getopt32(argv, "c" IF_DESKTOP("r:d:t:")
+       opts = getopt32(argv, "c" IF_FEATURE_TOUCH_SUSV3("r:d:t:")
+                               IF_FEATURE_TOUCH_NODEREF("h")
                                /*ignored:*/ "fma"
-                               IF_DESKTOP(, &reference_file)
-                               IF_DESKTOP(, &date_str)
-                               IF_DESKTOP(, &date_str)
+                               IF_FEATURE_TOUCH_SUSV3(, &reference_file)
+                               IF_FEATURE_TOUCH_SUSV3(, &date_str)
+                               IF_FEATURE_TOUCH_SUSV3(, &date_str)
        );
 
-       opts &= 1; /* only -c bit is left */
        argv += optind;
        if (!*argv) {
                bb_show_usage();
@@ -85,28 +141,40 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
                struct stat stbuf;
                xstat(reference_file, &stbuf);
                timebuf[1].tv_sec = timebuf[0].tv_sec = stbuf.st_mtime;
+               /* Can use .st_mtim.tv_nsec
+                * (or is it .st_mtimensec?? see date.c)
+                * to set microseconds too.
+                */
        }
 
        if (date_str) {
                struct tm tm_time;
                time_t t;
 
-               //time(&t);
-               //localtime_r(&t, &tm_time);
-               memset(&tm_time, 0, sizeof(tm_time));
+               //memset(&tm_time, 0, sizeof(tm_time));
+               /* Better than memset: makes "HH:MM" dates meaningful */
+               time(&t);
+               localtime_r(&t, &tm_time);
                parse_datestr(date_str, &tm_time);
 
                /* Correct any day of week and day of year etc. fields */
-               tm_time.tm_isdst = -1;  /* Be sure to recheck dst */
+               tm_time.tm_isdst = -1;  /* Be sure to recheck dst */
                t = validate_tm_time(date_str, &tm_time);
 
                timebuf[1].tv_sec = timebuf[0].tv_sec = t;
        }
 
        do {
-               if (utimes(*argv, (reference_file || date_str) ? timebuf : NULL) != 0) {
-                       if (errno == ENOENT) { /* no such file */
-                               if (opts) { /* creation is disabled, so ignore */
+               int result;
+               result = (
+#if ENABLE_FEATURE_TOUCH_NODEREF
+                       (opts & OPT_h) ? lutimes :
+#endif
+                       utimes)(*argv, (reference_file || date_str) ? timebuf : NULL);
+               if (result != 0) {
+                       if (errno == ENOENT) { /* no such file? */
+                               if (opts & OPT_c) {
+                                       /* Creation is disabled, so ignore */
                                        continue;
                                }
                                /* Try to create the file */
index f3db379..e67948a 100644 (file)
@@ -13,7 +13,7 @@
  * This version of tr is adapted from Minix tr and was modified
  * by Erik Andersen <andersen@codepoet.org> to be used in busybox.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 /* http://www.opengroup.org/onlinepubs/009695399/utilities/tr.html
  * TODO: graph, print
 //config:        useful for cases when no other way of expressing a character
 //config:        is possible.
 
+//usage:#define tr_trivial_usage
+//usage:       "[-cds] STRING1 [STRING2]"
+//usage:#define tr_full_usage "\n\n"
+//usage:       "Translate, squeeze, or delete characters from stdin, writing to stdout\n"
+//usage:     "\n       -c      Take complement of STRING1"
+//usage:     "\n       -d      Delete input characters coded STRING1"
+//usage:     "\n       -s      Squeeze multiple output characters of STRING2 into one character"
+//usage:
+//usage:#define tr_example_usage
+//usage:       "$ echo \"gdkkn vnqkc\" | tr [a-y] [b-z]\n"
+//usage:       "hello world\n"
+
 #include "libbb.h"
 
 enum {
@@ -203,7 +215,7 @@ static unsigned expand(const char *arg, char **buffer_p)
                                buffer[pos++] = *arg; /* copy CHAR */
                                if (!arg[0] || arg[1] != '=' || arg[2] != ']')
                                        bb_show_usage();
-                               arg += 3;       /* skip CHAR=] */
+                               arg += 3;  /* skip CHAR=] */
                                continue;
                        }
                        /* The rest of "[xyz..." cases is treated as normal
@@ -258,9 +270,9 @@ int tr_main(int argc UNUSED_PARAM, char **argv)
        char *invec  = vector + ASCII;
        char *outvec = vector + ASCII * 2;
 
-#define TR_OPT_complement      (3 << 0)
-#define TR_OPT_delete          (1 << 2)
-#define TR_OPT_squeeze_reps    (1 << 3)
+#define TR_OPT_complement   (3 << 0)
+#define TR_OPT_delete       (1 << 2)
+#define TR_OPT_squeeze_reps (1 << 3)
 
        for (i = 0; i < ASCII; i++) {
                vector[i] = i;
@@ -324,5 +336,11 @@ int tr_main(int argc UNUSED_PARAM, char **argv)
                str2[out_index++] = last = coded;
        }
 
+       if (ENABLE_FEATURE_CLEAN_UP) {
+               free(vector);
+               free(str2);
+               free(str1);
+       }
+
        return EXIT_SUCCESS;
 }
index 8a7e6ae..382e476 100644 (file)
@@ -4,12 +4,22 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/true.html */
 
+//usage:#define true_trivial_usage
+//usage:       ""
+//usage:#define true_full_usage "\n\n"
+//usage:       "Return an exit code of TRUE (0)"
+//usage:
+//usage:#define true_example_usage
+//usage:       "$ true\n"
+//usage:       "$ echo $?\n"
+//usage:       "0\n"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
index 69352ec..4517505 100644 (file)
@@ -4,22 +4,34 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv4 compliant */
 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/tty.html */
 
+//usage:#define tty_trivial_usage
+//usage:       ""
+//usage:#define tty_full_usage "\n\n"
+//usage:       "Print file name of stdin's terminal"
+//usage:       IF_INCLUDE_SUSv2( "\n"
+//usage:     "\n       -s      Print nothing, only return exit status"
+//usage:       )
+//usage:
+//usage:#define tty_example_usage
+//usage:       "$ tty\n"
+//usage:       "/dev/tty2\n"
+
 #include "libbb.h"
 
 int tty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int tty_main(int argc UNUSED_PARAM, char **argv)
 {
        const char *s;
-       IF_INCLUDE_SUSv2(int silent;)   /* Note: No longer relevant in SUSv3. */
+       IF_INCLUDE_SUSv2(int silent;)  /* Note: No longer relevant in SUSv3. */
        int retval;
 
-       xfunc_error_retval = 2; /* SUSv3 requires > 1 for error. */
+       xfunc_error_retval = 2;  /* SUSv3 requires > 1 for error. */
 
        IF_INCLUDE_SUSv2(silent = getopt32(argv, "s");)
        IF_INCLUDE_SUSv2(argv += optind;)
index 9822e49..b96d76b 100644 (file)
@@ -2,7 +2,7 @@
 /* uname -- print system information
  * Copyright (C) 1989-1999 Free Software Foundation, Inc.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
  *  Fix handling of -a to not print "unknown", add -o and -i support.
  */
 
+//usage:#define uname_trivial_usage
+//usage:       "[-amnrspv]"
+//usage:#define uname_full_usage "\n\n"
+//usage:       "Print system information\n"
+//usage:     "\n       -a      Print all"
+//usage:     "\n       -m      The machine (hardware) type"
+//usage:     "\n       -n      Hostname"
+//usage:     "\n       -r      OS release"
+//usage:     "\n       -s      OS name (default)"
+//usage:     "\n       -p      Processor type"
+//usage:     "\n       -v      OS version"
+//usage:
+//usage:#define uname_example_usage
+//usage:       "$ uname -a\n"
+//usage:       "Linux debian 2.4.23 #2 Tue Dec 23 17:09:10 MST 2003 i686 GNU/Linux\n"
+
 #include "libbb.h"
 /* After libbb.h, since it needs sys/types.h on some systems */
 #include <sys/utsname.h>
index e566dc1..9208d34 100644 (file)
@@ -4,12 +4,29 @@
  *
  * Copyright (C) 2005  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 compliant */
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/uniq.html */
 
+//usage:#define uniq_trivial_usage
+//usage:       "[-cdu][-f,s,w N] [INPUT [OUTPUT]]"
+//usage:#define uniq_full_usage "\n\n"
+//usage:       "Discard duplicate lines\n"
+//usage:     "\n       -c      Prefix lines by the number of occurrences"
+//usage:     "\n       -d      Only print duplicate lines"
+//usage:     "\n       -u      Only print unique lines"
+//usage:     "\n       -f N    Skip first N fields"
+//usage:     "\n       -s N    Skip first N chars (after any skipped fields)"
+//usage:     "\n       -w N    Compare N characters in line"
+//usage:
+//usage:#define uniq_example_usage
+//usage:       "$ echo -e \"a\\na\\nb\\nc\\nc\\na\" | sort | uniq\n"
+//usage:       "a\n"
+//usage:       "b\n"
+//usage:       "c\n"
+
 #include "libbb.h"
 
 int uniq_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -52,8 +69,8 @@ int uniq_main(int argc UNUSED_PARAM, char **argv)
                        if (output[0] != '-' || output[1]) {
                                // Won't work with "uniq - FILE" and closed stdin:
                                //close(STDOUT_FILENO);
-                               //xopen3(output, O_WRONLY | O_CREAT | O_TRUNC, 0666);
-                               xmove_fd(xopen3(output, O_WRONLY | O_CREAT | O_TRUNC, 0666), STDOUT_FILENO);
+                               //xopen(output, O_WRONLY | O_CREAT | O_TRUNC);
+                               xmove_fd(xopen(output, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO);
                        }
                }
        }
@@ -86,7 +103,7 @@ int uniq_main(int argc UNUSED_PARAM, char **argv)
                        }
 
                        free(cur_line);
-                       ++dups;  /* testing for overflow seems excessive */
+                       ++dups;  /* testing for overflow seems excessive */
                }
 
                if (old_line) {
index e7acd5f..2e4eb57 100644 (file)
@@ -4,11 +4,20 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */
 
+//usage:#define usleep_trivial_usage
+//usage:       "N"
+//usage:#define usleep_full_usage "\n\n"
+//usage:       "Pause for N microseconds"
+//usage:
+//usage:#define usleep_example_usage
+//usage:       "$ usleep 1000000\n"
+//usage:       "[pauses for 1 second]\n"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
@@ -20,9 +29,7 @@ int usleep_main(int argc UNUSED_PARAM, char **argv)
                bb_show_usage();
        }
 
-       if (usleep(xatou(argv[1]))) {
-               bb_perror_nomsg_and_die();
-       }
+       usleep(xatou(argv[1]));
 
        return EXIT_SUCCESS;
 }
index 0298a4b..b298fcb 100644 (file)
@@ -1,20 +1,31 @@
 /* vi: set sw=4 ts=4: */
 /*
- *  Copyright 2003, Glenn McGrath
+ * Copyright 2003, Glenn McGrath
  *
- *  Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
- *  Based on specification from
- *  http://www.opengroup.org/onlinepubs/007904975/utilities/uuencode.html
+ * Based on specification from
+ * http://www.opengroup.org/onlinepubs/007904975/utilities/uuencode.html
  *
- *  Bugs: the spec doesn't mention anything about "`\n`\n" prior to the
- *        "end" line
+ * Bugs: the spec doesn't mention anything about "`\n`\n" prior to the
+ * "end" line
  */
 
+//usage:#define uudecode_trivial_usage
+//usage:       "[-o OUTFILE] [INFILE]"
+//usage:#define uudecode_full_usage "\n\n"
+//usage:       "Uudecode a file\n"
+//usage:       "Finds OUTFILE in uuencoded source unless -o is given"
+//usage:
+//usage:#define uudecode_example_usage
+//usage:       "$ uudecode -o busybox busybox.uu\n"
+//usage:       "$ ls -l busybox\n"
+//usage:       "-rwxr-xr-x   1 ams      ams        245264 Jun  7 21:35 busybox\n"
 
 #include "libbb.h"
 
-static void read_stduu(FILE *src_stream, FILE *dst_stream)
+#if ENABLE_UUDECODE
+static void FAST_FUNC read_stduu(FILE *src_stream, FILE *dst_stream, int flags UNUSED_PARAM)
 {
        char *line;
 
@@ -74,67 +85,9 @@ static void read_stduu(FILE *src_stream, FILE *dst_stream)
        }
        bb_error_msg_and_die("short file");
 }
+#endif
 
-static void read_base64(FILE *src_stream, FILE *dst_stream)
-{
-       int term_count = 1;
-
-       while (1) {
-               char translated[4];
-               int count = 0;
-
-               while (count < 4) {
-                       char *table_ptr;
-                       int ch;
-
-                       /* Get next _valid_ character.
-                        * global vector bb_uuenc_tbl_base64[] contains this string:
-                        * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n"
-                        */
-                       do {
-                               ch = fgetc(src_stream);
-                               if (ch == EOF) {
-                                       bb_error_msg_and_die("short file");
-                               }
-                               table_ptr = strchr(bb_uuenc_tbl_base64, ch);
-                       } while (table_ptr == NULL);
-
-                       /* Convert encoded character to decimal */
-                       ch = table_ptr - bb_uuenc_tbl_base64;
-
-                       if (*table_ptr == '=') {
-                               if (term_count == 0) {
-                                       translated[count] = '\0';
-                                       break;
-                               }
-                               term_count++;
-                       } else if (*table_ptr == '\n') {
-                               /* Check for terminating line */
-                               if (term_count == 5) {
-                                       return;
-                               }
-                               term_count = 1;
-                               continue;
-                       } else {
-                               translated[count] = ch;
-                               count++;
-                               term_count = 0;
-                       }
-               }
-
-               /* Merge 6 bit chars to 8 bit */
-               if (count > 1) {
-                       fputc(translated[0] << 2 | translated[1] >> 4, dst_stream);
-               }
-               if (count > 2) {
-                       fputc(translated[1] << 4 | translated[2] >> 2, dst_stream);
-               }
-               if (count > 3) {
-                       fputc(translated[2] << 6 | translated[3], dst_stream);
-               }
-       }
-}
-
+#if ENABLE_UUDECODE
 int uudecode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int uudecode_main(int argc UNUSED_PARAM, char **argv)
 {
@@ -146,13 +99,13 @@ int uudecode_main(int argc UNUSED_PARAM, char **argv)
        getopt32(argv, "o:", &outname);
        argv += optind;
 
-       if (!*argv)
+       if (!argv[0])
                *--argv = (char*)"-";
-       src_stream = xfopen_stdin(*argv);
+       src_stream = xfopen_stdin(argv[0]);
 
        /* Search for the start of the encoding */
        while ((line = xmalloc_fgetline(src_stream)) != NULL) {
-               void (*decode_fn_ptr)(FILE *src, FILE *dst);
+               void FAST_FUNC (*decode_fn_ptr)(FILE *src, FILE *dst, int flags);
                char *line_ptr;
                FILE *dst_stream;
                int mode;
@@ -172,10 +125,11 @@ int uudecode_main(int argc UNUSED_PARAM, char **argv)
                mode = bb_strtou(line_ptr, NULL, 8);
                if (outname == NULL) {
                        outname = strchr(line_ptr, ' ');
-                       if ((outname == NULL) || (*outname == '\0')) {
+                       if (!outname)
                                break;
-                       }
                        outname++;
+                       if (!outname[0])
+                               break;
                }
                dst_stream = stdout;
                if (NOT_LONE_DASH(outname)) {
@@ -183,12 +137,73 @@ int uudecode_main(int argc UNUSED_PARAM, char **argv)
                        fchmod(fileno(dst_stream), mode & (S_IRWXU | S_IRWXG | S_IRWXO));
                }
                free(line);
-               decode_fn_ptr(src_stream, dst_stream);
+               decode_fn_ptr(src_stream, dst_stream, /*flags:*/ BASE64_FLAG_UU_STOP + BASE64_FLAG_NO_STOP_CHAR);
                /* fclose_if_not_stdin(src_stream); - redundant */
                return EXIT_SUCCESS;
        }
        bb_error_msg_and_die("no 'begin' line");
 }
+#endif
+
+//applet:IF_BASE64(APPLET(base64, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_BASE64) += uudecode.o
+
+//config:config BASE64
+//config:      bool "base64"
+//config:      default y
+//config:      help
+//config:        Base64 encode and decode
+
+//usage:#define base64_trivial_usage
+//usage:       "[-d] [FILE]"
+//usage:#define base64_full_usage "\n\n"
+//usage:       "Base64 encode or decode FILE to standard output"
+//usage:     "\n       -d      Decode data"
+////usage:     "\n     -w COL  Wrap lines at COL (default 76, 0 disables)"
+////usage:     "\n     -i      When decoding, ignore non-alphabet characters"
+
+#if ENABLE_BASE64
+int base64_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int base64_main(int argc UNUSED_PARAM, char **argv)
+{
+       FILE *src_stream;
+       unsigned opts;
+
+       opt_complementary = "?1"; /* 1 argument max */
+       opts = getopt32(argv, "d");
+       argv += optind;
+
+       if (!argv[0])
+               *--argv = (char*)"-";
+       src_stream = xfopen_stdin(argv[0]);
+       if (opts) {
+               read_base64(src_stream, stdout, /*flags:*/ (char)EOF);
+       } else {
+               enum {
+                       SRC_BUF_SIZE = 76/4*3,  /* This *MUST* be a multiple of 3 */
+                       DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3),
+               };
+               char src_buf[SRC_BUF_SIZE];
+               char dst_buf[DST_BUF_SIZE + 1];
+               int src_fd = fileno(src_stream);
+               while (1) {
+                       size_t size = full_read(src_fd, src_buf, SRC_BUF_SIZE);
+                       if (!size)
+                               break;
+                       if ((ssize_t)size < 0)
+                               bb_perror_msg_and_die(bb_msg_read_error);
+                       /* Encode the buffer we just read in */
+                       bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64);
+                       xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3));
+                       bb_putchar('\n');
+                       fflush(stdout);
+               }
+       }
+
+       fflush_stdout_and_exit(EXIT_SUCCESS);
+}
+#endif
 
 /* Test script.
 Put this into an empty dir with busybox binary, an run.
index bf66185..673ef36 100644 (file)
@@ -5,13 +5,26 @@
  *  based on the function base64_encode from http.c in wget v1.6
  *  Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define uuencode_trivial_usage
+//usage:       "[-m] [FILE] STORED_FILENAME"
+//usage:#define uuencode_full_usage "\n\n"
+//usage:       "Uuencode FILE (or stdin) to stdout\n"
+//usage:     "\n       -m      Use base64 encoding per RFC1521"
+//usage:
+//usage:#define uuencode_example_usage
+//usage:       "$ uuencode busybox busybox\n"
+//usage:       "begin 755 busybox\n"
+//usage:       "<encoded file snipped>\n"
+//usage:       "$ uudecode busybox busybox > busybox.uu\n"
+//usage:       "$\n"
+
 #include "libbb.h"
 
 enum {
-       SRC_BUF_SIZE = 45,  /* This *MUST* be a multiple of 3 */
+       SRC_BUF_SIZE = 15*3,  /* This *MUST* be a multiple of 3 */
        DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3),
 };
 
@@ -33,7 +46,7 @@ int uuencode_main(int argc UNUSED_PARAM, char **argv)
        }
        argv += optind;
        if (argv[1]) {
-               src_fd = xopen(*argv, O_RDONLY);
+               src_fd = xopen(argv[0], O_RDONLY);
                fstat(src_fd, &stat_buf);
                mode = stat_buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
                argv++;
index 7116842..a410e40 100644 (file)
@@ -4,10 +4,10 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
-/* BB_AUDIT SUSv3 _NOT_ compliant -- option -m is not currently supported. */
+/* BB_AUDIT SUSv3 compliant. */
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/wc.html */
 
 /* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
  *  3) no checking of ferror on EOF returns
  *  4) isprint() wasn't considered when word counting.
  *
- * TODO:
- *
- * When locale support is enabled, count multibyte chars in the '-m' case.
- *
  * NOTES:
  *
  * The previous busybox wc attempted an optimization using stat for the
@@ -40,8 +36,8 @@
  *
  * for which 'wc -c' should output '0'.
  */
-
 #include "libbb.h"
+#include "unicode.h"
 
 #if !ENABLE_LOCALE_SUPPORT
 # undef isprint
 # define COUNT_FMT "u"
 #endif
 
+/* We support -m even when UNICODE_SUPPORT is off,
+ * we just don't advertise it in help text,
+ * since it is the same as -c in this case.
+ */
+
+//usage:#define wc_trivial_usage
+//usage:       "[-c"IF_UNICODE_SUPPORT("m")"lwL] [FILE]..."
+//usage:
+//usage:#define wc_full_usage "\n\n"
+//usage:       "Count lines, words, and bytes for each FILE (or stdin)\n"
+//usage:     "\n       -c      Count bytes"
+//usage:       IF_UNICODE_SUPPORT(
+//usage:     "\n       -m      Count characters"
+//usage:       )
+//usage:     "\n       -l      Count newlines"
+//usage:     "\n       -w      Count words"
+//usage:     "\n       -L      Print longest line length"
+//usage:
+//usage:#define wc_example_usage
+//usage:       "$ wc /etc/passwd\n"
+//usage:       "     31      46    1365 /etc/passwd\n"
+
+/* Order is important if we want to be compatible with
+ * column order in "wc -cmlwL" output:
+ */
 enum {
-       WC_LINES        = 0,
-       WC_WORDS        = 1,
-       WC_CHARS        = 2,
-       WC_LENGTH       = 3
+       WC_LINES    = 0, /* -l */
+       WC_WORDS    = 1, /* -w */
+       WC_UNICHARS = 2, /* -m */
+       WC_BYTES    = 3, /* -c */
+       WC_LENGTH   = 4, /* -L */
+       NUM_WCS     = 5,
 };
 
 int wc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -72,16 +95,18 @@ int wc_main(int argc UNUSED_PARAM, char **argv)
        const char *start_fmt = " %9"COUNT_FMT + 1;
        const char *fname_fmt = " %s\n";
        COUNT_T *pcounts;
-       COUNT_T counts[4];
-       COUNT_T totals[4];
+       COUNT_T counts[NUM_WCS];
+       COUNT_T totals[NUM_WCS];
        int num_files;
        smallint status = EXIT_SUCCESS;
        unsigned print_type;
 
-       print_type = getopt32(argv, "lwcL");
+       init_unicode();
+
+       print_type = getopt32(argv, "lwmcL");
 
        if (print_type == 0) {
-               print_type = (1 << WC_LINES) | (1 << WC_WORDS) | (1 << WC_CHARS);
+               print_type = (1 << WC_LINES) | (1 << WC_WORDS) | (1 << WC_BYTES);
        }
 
        argv += optind;
@@ -99,7 +124,7 @@ int wc_main(int argc UNUSED_PARAM, char **argv)
        pcounts = counts;
 
        num_files = 0;
-       while ((arg = *argv++) != 0) {
+       while ((arg = *argv++) != NULL) {
                FILE *fp;
                const char *s;
                unsigned u;
@@ -117,21 +142,28 @@ int wc_main(int argc UNUSED_PARAM, char **argv)
                linepos = 0;
                in_word = 0;
 
-               do {
+               while (1) {
                        int c;
                        /* Our -w doesn't match GNU wc exactly... oh well */
 
-                       ++counts[WC_CHARS];
                        c = getc(fp);
                        if (c == EOF) {
                                if (ferror(fp)) {
                                        bb_simple_perror_msg(arg);
                                        status = EXIT_FAILURE;
                                }
-                               --counts[WC_CHARS];
-                               goto DO_EOF;            /* Treat an EOF as '\r'. */
+                               goto DO_EOF;  /* Treat an EOF as '\r'. */
                        }
-                       if (isprint_asciionly(c)) {
+
+                       /* Cater for -c and -m */
+                       ++counts[WC_BYTES];
+                       if (unicode_status != UNICODE_ON /* every byte is a new char */
+                        || (c & 0xc0) != 0x80 /* it isn't a 2nd+ byte of a Unicode char */
+                       ) {
+                               ++counts[WC_UNICHARS];
+                       }
+
+                       if (isprint_asciionly(c)) { /* FIXME: not unicode-aware */
                                ++linepos;
                                if (!isspace(c)) {
                                        in_word = 1;
@@ -146,7 +178,7 @@ int wc_main(int argc UNUSED_PARAM, char **argv)
                                 */
                                if (c == '\t') {
                                        linepos = (linepos | 7) + 1;
-                               } else {                        /* '\n', '\r', '\f', or '\v' */
+                               } else {  /* '\n', '\r', '\f', or '\v' */
  DO_EOF:
                                        if (linepos > counts[WC_LENGTH]) {
                                                counts[WC_LENGTH] = linepos;
@@ -167,18 +199,18 @@ int wc_main(int argc UNUSED_PARAM, char **argv)
                        if (c == EOF) {
                                break;
                        }
-               } while (1);
+               }
+
+               fclose_if_not_stdin(fp);
 
                if (totals[WC_LENGTH] < counts[WC_LENGTH]) {
                        totals[WC_LENGTH] = counts[WC_LENGTH];
                }
                totals[WC_LENGTH] -= counts[WC_LENGTH];
 
-               fclose_if_not_stdin(fp);
-
  OUTPUT:
                /* coreutils wc tries hard to print pretty columns
-                * (saves results for all files, find max col len etc...)
+                * (saves results for all files, finds max col len etc...)
                 * we won't try that hard, it will bloat us too much */
                s = start_fmt;
                u = 0;
@@ -188,7 +220,7 @@ int wc_main(int argc UNUSED_PARAM, char **argv)
                                s = " %9"COUNT_FMT; /* Ok... restore the leading space. */
                        }
                        totals[u] += pcounts[u];
-               } while (++u < 4);
+               } while (++u < NUM_WCS);
                printf(fname_fmt, arg);
        }
 
@@ -197,7 +229,7 @@ int wc_main(int argc UNUSED_PARAM, char **argv)
         * effect of trashing the totals array after outputting it, but that's
         * irrelavent since we no longer need it. */
        if (num_files > 1) {
-               num_files = 0;                          /* Make sure we don't get here again. */
+               num_files = 0;  /* Make sure we don't get here again. */
                arg = "total";
                pcounts = totals;
                --argv;
index 2b43310..f955ce6 100644 (file)
  *
  * Copyright (c) 2002 AYR Networks, Inc.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  *----------------------------------------------------------------------
  */
 /* BB_AUDIT SUSv3 _NOT_ compliant -- missing options -b, -d, -l, -m, -p, -q, -r, -s, -t, -T, -u; Missing argument 'file'.  */
 
+//config:config WHO
+//config:      bool "who"
+//config:      default y
+//config:      depends on FEATURE_UTMP
+//config:      help
+//config:        who is used to show who is logged on.
+
+//config:config USERS
+//config:      bool "users"
+//config:      default y
+//config:      depends on FEATURE_UTMP
+//config:      help
+//config:        Print users currently logged on.
+
+//applet:IF_USERS(APPLET_ODDNAME(users, who, BB_DIR_USR_BIN, BB_SUID_DROP, users))
+//applet:IF_WHO(  APPLET(  who, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_USERS) += who.o
+//kbuild:lib-$(CONFIG_WHO) += who.o
+
+//usage:#define users_trivial_usage
+//usage:       ""
+//usage:#define users_full_usage "\n\n"
+//usage:       "Print the users currently logged on"
+
+//usage:#define who_trivial_usage
+//usage:       "[-a]"
+//usage:#define who_full_usage "\n\n"
+//usage:       "Show who is logged on\n"
+//usage:     "\n       -a      Show all"
+//usage:     "\n       -H      Print column headers"
+
 #include "libbb.h"
-#include <utmp.h>
 
 static void idle_string(char *str6, time_t t)
 {
@@ -44,9 +75,11 @@ int who_main(int argc UNUSED_PARAM, char **argv)
 {
        struct utmp *ut;
        unsigned opt;
+       int do_users = (ENABLE_USERS && (!ENABLE_WHO || applet_name[0] == 'u'));
+       const char *fmt = "%s";
 
        opt_complementary = "=0";
-       opt = getopt32(argv, "aH");
+       opt = getopt32(argv, do_users ? "" : "aH");
        if (opt & 2) // -H
                printf("USER\t\tTTY\t\tIDLE\tTIME\t\t HOST\n");
 
@@ -55,36 +88,43 @@ int who_main(int argc UNUSED_PARAM, char **argv)
                if (ut->ut_user[0]
                 && ((opt & 1) || ut->ut_type == USER_PROCESS)
                ) {
-                       char str6[6];
-                       char name[sizeof("/dev/") + sizeof(ut->ut_line) + 1];
-                       struct stat st;
-                       time_t seconds;
+                       if (!do_users) {
+                               char str6[6];
+                               char name[sizeof("/dev/") + sizeof(ut->ut_line) + 1];
+                               struct stat st;
+                               time_t seconds;
 
-                       str6[0] = '?';
-                       str6[1] = '\0';
-                       strcpy(name, "/dev/");
-                       safe_strncpy(ut->ut_line[0] == '/' ? name : name + sizeof("/dev/")-1,
-                               ut->ut_line,
-                               sizeof(ut->ut_line)+1
-                       );
-                       if (stat(name, &st) == 0)
-                               idle_string(str6, st.st_atime);
-                       /* manpages say ut_tv.tv_sec *is* time_t,
-                        * but some systems have it wrong */
-                       seconds = ut->ut_tv.tv_sec;
-                       /* How wide time field can be?
-                        * "Nov 10 19:33:20": 15 chars
-                        * "2010-11-10 19:33": 16 chars
-                        */
-                       printf("%-15.*s %-15.*s %-7s %-16.16s %.*s\n",
-                                       (int)sizeof(ut->ut_user), ut->ut_user,
-                                       (int)sizeof(ut->ut_line), ut->ut_line,
-                                       str6,
-                                       ctime(&seconds) + 4,
-                                       (int)sizeof(ut->ut_host), ut->ut_host
-                       );
+                               str6[0] = '?';
+                               str6[1] = '\0';
+                               strcpy(name, "/dev/");
+                               safe_strncpy(ut->ut_line[0] == '/' ? name : name + sizeof("/dev/")-1,
+                                       ut->ut_line,
+                                       sizeof(ut->ut_line)+1
+                               );
+                               if (stat(name, &st) == 0)
+                                       idle_string(str6, st.st_atime);
+                               /* manpages say ut_tv.tv_sec *is* time_t,
+                                * but some systems have it wrong */
+                               seconds = ut->ut_tv.tv_sec;
+                               /* How wide time field can be?
+                                * "Nov 10 19:33:20": 15 chars
+                                * "2010-11-10 19:33": 16 chars
+                                */
+                               printf("%-15.*s %-15.*s %-7s %-16.16s %.*s\n",
+                                               (int)sizeof(ut->ut_user), ut->ut_user,
+                                               (int)sizeof(ut->ut_line), ut->ut_line,
+                                               str6,
+                                               ctime(&seconds) + 4,
+                                               (int)sizeof(ut->ut_host), ut->ut_host
+                               );
+                       } else {
+                               printf(fmt, ut->ut_user);
+                               fmt = " %s";
+                       }
                }
        }
+       if (do_users)
+               bb_putchar('\n');
        if (ENABLE_FEATURE_CLEAN_UP)
                endutent();
        return EXIT_SUCCESS;
index 22d722e..30b17ca 100644 (file)
@@ -4,11 +4,16 @@
  *
  * Copyright (C) 2000  Edward Betts <edward@debian.org>.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
 
+//usage:#define whoami_trivial_usage
+//usage:       ""
+//usage:#define whoami_full_usage "\n\n"
+//usage:       "Print the user name associated with the current effective user id"
+
 #include "libbb.h"
 
 /* This is a NOFORK applet. Be very careful! */
index ead674b..5d799f0 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
diff --git a/debian/bin/genorig.py b/debian/bin/genorig.py
deleted file mode 100755 (executable)
index f0857fa..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-#!/usr/bin/env python
-
-import os, os.path, re, shutil, sys
-
-class Changelog(list):
-    _rules = r"""
-^
-(?P<source>
-    \w[-+0-9a-z.]+
-)
-\ 
-\(
-(?P<version>
-    [^\(\)\ \t]+
-)
-\)
-\s+
-(?P<distribution>
-    [-+0-9a-zA-Z.]+
-)
-\;
-"""
-    _re = re.compile(_rules, re.X)
-
-    class Entry(object):
-        __slot__ = 'distribution', 'source', 'version'
-
-        def __init__(self, distribution, source, version):
-            self.distribution, self.source, self.version = distribution, source, version
-
-    def __init__(self, dir = '', version = None):
-        if version is None:
-            version = Version
-        f = file(os.path.join(dir, "debian/changelog"))
-        while True:
-            line = f.readline()
-            if not line:
-                break
-            match = self._re.match(line)
-            if not match:
-                continue
-            try:
-                v = version(match.group('version'))
-            except Exception:
-                if not len(self):
-                    raise
-                v = Version(match.group('version'))
-            self.append(self.Entry(match.group('distribution'), match.group('source'), v))
-
-class Version(object):
-    _version_rules = ur"""
-^
-(?:
-    (?P<epoch>
-        \d+
-    )
-    :
-)?
-(?P<upstream>
-    .+?
-)   
-(?:
-    -
-    (?P<revision>[^-]+)
-)?
-$
-"""
-    _version_re = re.compile(_version_rules, re.X)
-
-    def __init__(self, version):
-        match = self._version_re.match(version)
-        if match is None:
-            raise RuntimeError, "Invalid debian version"
-        self.epoch = None
-        if match.group("epoch") is not None:
-            self.epoch = int(match.group("epoch"))
-        self.upstream = match.group("upstream")
-        self.revision = match.group("revision")
-
-    def __str__(self):
-        return self.complete
-
-    @property
-    def complete(self):
-        if self.epoch is not None:
-            return "%d:%s" % (self.epoch, self.complete_noepoch)
-        return self.complete_noepoch
-
-    @property
-    def complete_noepoch(self):
-        if self.revision is not None:
-            return "%s-%s" % (self.upstream, self.revision)
-        return self.upstream
-
-class Main(object):
-    def __init__(self, input_tar, override_version):
-        self.log = sys.stdout.write
-
-        self.input_tar = input_tar
-
-        changelog = Changelog()[0]
-        source = changelog.source
-        version = changelog.version
-
-        if override_version:
-            version = Version('%s-undef' % override_version)
-
-        self.log('Using source name %s, version %s\n' % (source, version.upstream))
-
-        self.orig = '%s-%s' % (source, version.upstream)
-        self.orig_tar = '%s_%s.orig.tar.gz' % (source, version.upstream)
-
-    def __call__(self):
-        import tempfile
-        self.dir = tempfile.mkdtemp(prefix = 'genorig', dir = 'debian')
-        try:
-            self.upstream()
-            self.remove()
-            self.tar()
-        finally:
-            shutil.rmtree(self.dir)
-
-    def upstream(self):
-        self.log("Extracting tarball %s\n" % self.input_tar)
-        match = re.match(r'(^|.*/).*\.(?P<extension>(tar\.(bz2|gz)|tbz2|tgz))?$', self.input_tar)
-        if not match:
-            raise RuntimeError("Can't identify name of tarball")
-        cmdline = ['tar -xf', self.input_tar, '-C', self.dir]
-        if match.group('extension') in ('tar.bz2', 'tbz2'):
-            cmdline.append('-j')
-        elif match.group('extension') in ('tar.gz', 'tgz'):
-            cmdline.append('-z')
-        if os.spawnv(os.P_WAIT, '/bin/sh', ['sh', '-c', ' '.join(cmdline)]):
-            raise RuntimeError("Can't extract tarball")
-
-    def remove(self):
-        self.log("Remove files\n")
-        root = os.path.join(self.dir, self.orig)
-        os.unlink(os.path.join(root, "networking/ftp_ipv6_rfc2428.txt"))
-
-    def tar(self):
-        out = os.path.join("../orig", self.orig_tar)
-        try:
-            os.mkdir("../orig")
-        except OSError: pass
-        try:
-            os.stat(out)
-            raise RuntimeError("Destination already exists")
-        except OSError: pass
-        self.log("Generate tarball %s\n" % out)
-        cmdline = ['tar -czf', out, '-C', self.dir, self.orig]
-        try:
-            if os.spawnv(os.P_WAIT, '/bin/sh', ['sh', '-c', ' '.join(cmdline)]):
-                raise RuntimeError("Can't patch source")
-            os.chmod(out, 0644)
-        except:
-            try:
-                os.unlink(out)
-            except OSError:
-                pass
-            raise
-
-if __name__ == '__main__':
-    from optparse import OptionParser
-    parser = OptionParser(usage = "%prog [OPTION]... TAR [PATCH]")
-    parser.add_option("-v", "--version", dest = "version", help = "Override version", metavar = "VERSION")
-    options, args = parser.parse_args()
-    Main(args[0], options.version)()
diff --git a/debian/busybox-klogd.service b/debian/busybox-klogd.service
deleted file mode 100644 (file)
index 8b5d726..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-[Unit]
-Description=Busybox Logging Service: klogd
-After=busybox-syslogd.service syslog.target
-
-[Service]
-Type=simple
-ExecStart=/sbin/klogd -n -c4
-
-[Install]
-WantedBy=multi-user.target
diff --git a/debian/busybox-static.install b/debian/busybox-static.install
deleted file mode 100644 (file)
index 1434949..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-busybox bin
-docs/busybox.1 usr/share/man/man1
diff --git a/debian/busybox-symlinks-adduser.links b/debian/busybox-symlinks-adduser.links
deleted file mode 100644 (file)
index af4207a..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-bin/busybox usr/sbin/addgroup
-bin/busybox usr/sbin/adduser
-bin/busybox usr/sbin/delgroup
-bin/busybox usr/sbin/deluser
diff --git a/debian/busybox-symlinks-adjtimex.links b/debian/busybox-symlinks-adjtimex.links
deleted file mode 100644 (file)
index cdb3eeb..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/adjtimex
diff --git a/debian/busybox-symlinks-binutils.links b/debian/busybox-symlinks-binutils.links
deleted file mode 100644 (file)
index c687a4c..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-bin/busybox usr/bin/ar
-bin/busybox usr/bin/strings
diff --git a/debian/busybox-symlinks-bridge-utils.links b/debian/busybox-symlinks-bridge-utils.links
deleted file mode 100644 (file)
index 63dd55c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/brctl
diff --git a/debian/busybox-symlinks-bsdmainutils.links b/debian/busybox-symlinks-bsdmainutils.links
deleted file mode 100644 (file)
index be467d6..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-bin/busybox usr/bin/cal
-bin/busybox usr/bin/hd
-bin/busybox usr/bin/hexdump
diff --git a/debian/busybox-symlinks-busybox.links b/debian/busybox-symlinks-busybox.links
deleted file mode 100644 (file)
index 3debeb1..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-bin/busybox usr/bin/[[
-bin/busybox usr/bin/catv
-bin/busybox usr/sbin/crond
-bin/busybox usr/sbin/dhcprelay
-bin/busybox usr/sbin/dnsd
-bin/busybox bin/dumpkmap
-bin/busybox usr/bin/ether-wake
-bin/busybox usr/sbin/fakeidentd
-bin/busybox sbin/fbsplash
-bin/busybox bin/fsync
-bin/busybox usr/bin/ftpget
-bin/busybox usr/bin/ftpput
-bin/busybox usr/sbin/httpd
-bin/busybox sbin/ifenslave
-bin/busybox sbin/inotifyd
-bin/busybox bin/ipaddr
-bin/busybox bin/iplink
-bin/busybox bin/iproute
-bin/busybox bin/iprule
-bin/busybox usr/bin/length
-bin/busybox usr/bin/loadfont
-bin/busybox sbin/loadkmap
-bin/busybox sbin/logread
-bin/busybox sbin/makedevs
-bin/busybox sbin/mdev
-bin/busybox usr/bin/microcom
-bin/busybox usr/bin/nmeter
-bin/busybox usr/bin/pscan
-bin/busybox sbin/raidautorun
-bin/busybox usr/bin/readahead
-bin/busybox sbin/setconsole
-bin/busybox usr/sbin/tftpd
-bin/busybox usr/bin/ttysize
-bin/busybox bin/usleep
-bin/busybox usr/bin/volname
diff --git a/debian/busybox-symlinks-bzip2.links b/debian/busybox-symlinks-bzip2.links
deleted file mode 100644 (file)
index 590ea6c..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-bin/busybox bin/bunzip2
-bin/busybox bin/bzcat
-bin/busybox bin/bzip2
diff --git a/debian/busybox-symlinks-console-tools.links b/debian/busybox-symlinks-console-tools.links
deleted file mode 100644 (file)
index 9c39eed..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-bin/busybox usr/bin/chvt
-bin/busybox usr/bin/deallocvt
-bin/busybox bin/fgconsole
-bin/busybox usr/bin/kbd_mode
-bin/busybox usr/bin/openvt
-bin/busybox usr/bin/setkeycodes
-bin/busybox usr/bin/setlogcons
-bin/busybox usr/bin/showkey
diff --git a/debian/busybox-symlinks-cpio.links b/debian/busybox-symlinks-cpio.links
deleted file mode 100644 (file)
index b31d727..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox bin/cpio
diff --git a/debian/busybox-symlinks-cpio.postinst b/debian/busybox-symlinks-cpio.postinst
deleted file mode 100644 (file)
index 3c12d3d..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-set -e
-update-alternatives --install /bin/mt mt /bin/busybox 1
diff --git a/debian/busybox-symlinks-cpio.prerm b/debian/busybox-symlinks-cpio.prerm
deleted file mode 100644 (file)
index c6a3f50..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-set -e
-update-alternatives --remove mt /bin/busybox
diff --git a/debian/busybox-symlinks-cron.links b/debian/busybox-symlinks-cron.links
deleted file mode 100644 (file)
index 9b1fe35..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/crontab
diff --git a/debian/busybox-symlinks-daemontools.links b/debian/busybox-symlinks-daemontools.links
deleted file mode 100644 (file)
index 3330648..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-bin/busybox usr/bin/envdir
-bin/busybox usr/bin/envuidgid
-bin/busybox usr/bin/setuidgid
-bin/busybox usr/bin/softlimit
diff --git a/debian/busybox-symlinks-dc.links b/debian/busybox-symlinks-dc.links
deleted file mode 100644 (file)
index 86f8c30..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/dc
diff --git a/debian/busybox-symlinks-dnsutils.links b/debian/busybox-symlinks-dnsutils.links
deleted file mode 100644 (file)
index eb44fc7..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/nslookup
diff --git a/debian/busybox-symlinks-dosfstools.links b/debian/busybox-symlinks-dosfstools.links
deleted file mode 100644 (file)
index ff83723..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-bin/busybox sbin/mkdosfs
-bin/busybox sbin/mkfs.vfat
diff --git a/debian/busybox-symlinks-ed.links b/debian/busybox-symlinks-ed.links
deleted file mode 100644 (file)
index 90fbc07..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox bin/ed
diff --git a/debian/busybox-symlinks-eject.links b/debian/busybox-symlinks-eject.links
deleted file mode 100644 (file)
index 70e7ff1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/eject
diff --git a/debian/busybox-symlinks-fbset.links b/debian/busybox-symlinks-fbset.links
deleted file mode 100644 (file)
index d2526a1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox bin/fbset
diff --git a/debian/busybox-symlinks-fdflush.links b/debian/busybox-symlinks-fdflush.links
deleted file mode 100644 (file)
index 66c3d68..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox bin/fdflush
diff --git a/debian/busybox-symlinks-hdparm.links b/debian/busybox-symlinks-hdparm.links
deleted file mode 100644 (file)
index b7ddf97..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox sbin/hdparm
diff --git a/debian/busybox-symlinks-ifupdown.dirs b/debian/busybox-symlinks-ifupdown.dirs
deleted file mode 100644 (file)
index d7fd273..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-etc/network/if-post-down.d
-etc/network/if-pre-up.d
-etc/network/if-up.d
-etc/network/if-down.d
diff --git a/debian/busybox-symlinks-ifupdown.links b/debian/busybox-symlinks-ifupdown.links
deleted file mode 100644 (file)
index 26e4525..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-bin/busybox sbin/ifdown
-bin/busybox sbin/ifup
diff --git a/debian/busybox-symlinks-initscripts.links b/debian/busybox-symlinks-initscripts.links
deleted file mode 100644 (file)
index 8fca0bb..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox bin/mountpoint
diff --git a/debian/busybox-symlinks-ipcalc.links b/debian/busybox-symlinks-ipcalc.links
deleted file mode 100644 (file)
index 9cbb249..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/ipcalc
diff --git a/debian/busybox-symlinks-iproute.links b/debian/busybox-symlinks-iproute.links
deleted file mode 100644 (file)
index 5875622..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-bin/busybox bin/ip
-bin/busybox sbin/ip
diff --git a/debian/busybox-symlinks-ipsvd.links b/debian/busybox-symlinks-ipsvd.links
deleted file mode 100644 (file)
index 1376e43..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-bin/busybox usr/bin/tcpsvd
-bin/busybox usr/bin/udpsvd
diff --git a/debian/busybox-symlinks-iputils-arping.links b/debian/busybox-symlinks-iputils-arping.links
deleted file mode 100644 (file)
index da66deb..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/arping
diff --git a/debian/busybox-symlinks-iputils-ping.links b/debian/busybox-symlinks-iputils-ping.links
deleted file mode 100644 (file)
index 80d5ce2..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-bin/busybox bin/ping
-bin/busybox bin/ping6
diff --git a/debian/busybox-symlinks-klogd.links b/debian/busybox-symlinks-klogd.links
deleted file mode 100644 (file)
index f7749be..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox sbin/klogd
diff --git a/debian/busybox-symlinks-loadlin.links b/debian/busybox-symlinks-loadlin.links
deleted file mode 100644 (file)
index 4890004..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/freeramdisk
diff --git a/debian/busybox-symlinks-lrzsz.links b/debian/busybox-symlinks-lrzsz.links
deleted file mode 100644 (file)
index 1a33f12..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/rx
diff --git a/debian/busybox-symlinks-lzma.links b/debian/busybox-symlinks-lzma.links
deleted file mode 100644 (file)
index 42f8232..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-bin/busybox usr/bin/lzcat
-bin/busybox usr/bin/lzma
-bin/busybox usr/bin/unlzma
diff --git a/debian/busybox-symlinks-lzop.links b/debian/busybox-symlinks-lzop.links
deleted file mode 100644 (file)
index 9987db9..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-bin/busybox usr/bin/lzop
-bin/busybox usr/bin/lzopcat
-bin/busybox usr/bin/unlzop
diff --git a/debian/busybox-symlinks-module-init-tools.links b/debian/busybox-symlinks-module-init-tools.links
deleted file mode 100644 (file)
index 5b1da4a..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-bin/busybox sbin/depmod
-bin/busybox sbin/insmod
-bin/busybox sbin/lsmod
-bin/busybox sbin/modinfo
-bin/busybox sbin/modprobe
-bin/busybox sbin/rmmod
diff --git a/debian/busybox-symlinks-mtd-utils.links b/debian/busybox-symlinks-mtd-utils.links
deleted file mode 100644 (file)
index e981bc4..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-bin/busybox usr/sbin/flash_eraseall
-bin/busybox usr/sbin/flash_lock
-bin/busybox usr/sbin/flash_unlock
-bin/busybox usr/sbin/flashcp
-bin/busybox usr/sbin/ubiattach
-bin/busybox usr/sbin/ubidetach
diff --git a/debian/busybox-symlinks-net-tools.links b/debian/busybox-symlinks-net-tools.links
deleted file mode 100644 (file)
index 18df066..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-bin/busybox usr/sbin/arp
-bin/busybox sbin/ifconfig
-bin/busybox sbin/iptunnel
-bin/busybox sbin/nameif
-bin/busybox bin/netstat
-bin/busybox sbin/route
-bin/busybox sbin/slattach
diff --git a/debian/busybox-symlinks-openbsd-inetd.links b/debian/busybox-symlinks-openbsd-inetd.links
deleted file mode 100644 (file)
index d9b1e3f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/sbin/inetd
diff --git a/debian/busybox-symlinks-passwd.links b/debian/busybox-symlinks-passwd.links
deleted file mode 100644 (file)
index 168db94..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-bin/busybox usr/sbin/chpasswd
-bin/busybox usr/bin/passwd
diff --git a/debian/busybox-symlinks-patch.links b/debian/busybox-symlinks-patch.links
deleted file mode 100644 (file)
index 74ed545..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/patch
diff --git a/debian/busybox-symlinks-ppp.links b/debian/busybox-symlinks-ppp.links
deleted file mode 100644 (file)
index 5193ff0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/sbin/chat
diff --git a/debian/busybox-symlinks-procps.links b/debian/busybox-symlinks-procps.links
deleted file mode 100644 (file)
index 62dd8f8..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-bin/busybox usr/bin/free
-bin/busybox bin/kill
-bin/busybox usr/bin/pgrep
-bin/busybox usr/bin/pkill
-bin/busybox bin/ps
-bin/busybox sbin/sysctl
-bin/busybox usr/bin/top
-bin/busybox usr/bin/uptime
-bin/busybox usr/bin/watch
diff --git a/debian/busybox-symlinks-psmisc.links b/debian/busybox-symlinks-psmisc.links
deleted file mode 100644 (file)
index e9f1a83..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-bin/busybox bin/fuser
-bin/busybox usr/bin/killall
diff --git a/debian/busybox-symlinks-rdate.links b/debian/busybox-symlinks-rdate.links
deleted file mode 100644 (file)
index 87edc7f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/sbin/rdate
diff --git a/debian/busybox-symlinks-realpath.links b/debian/busybox-symlinks-realpath.links
deleted file mode 100644 (file)
index 17f3c90..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/realpath
diff --git a/debian/busybox-symlinks-rpm.links b/debian/busybox-symlinks-rpm.links
deleted file mode 100644 (file)
index b943439..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-bin/busybox usr/bin/rpm
-bin/busybox usr/bin/rpm2cpio
diff --git a/debian/busybox-symlinks-runit.links b/debian/busybox-symlinks-runit.links
deleted file mode 100644 (file)
index 14ba9b5..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-bin/busybox usr/bin/chpst
-bin/busybox usr/bin/runsv
-bin/busybox usr/bin/runsvdir
-bin/busybox usr/bin/sv
-bin/busybox usr/bin/svlogd
diff --git a/debian/busybox-symlinks-sharutils.links b/debian/busybox-symlinks-sharutils.links
deleted file mode 100644 (file)
index adf295a..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-bin/busybox usr/bin/uudecode
-bin/busybox usr/bin/uuencode
diff --git a/debian/busybox-symlinks-ssmtp.links b/debian/busybox-symlinks-ssmtp.links
deleted file mode 100644 (file)
index 4221fd1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/sbin/sendmail
diff --git a/debian/busybox-symlinks-sysklogd.links b/debian/busybox-symlinks-sysklogd.links
deleted file mode 100644 (file)
index 20fb96b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox sbin/syslogd
diff --git a/debian/busybox-symlinks-telnetd.links b/debian/busybox-symlinks-telnetd.links
deleted file mode 100644 (file)
index fc22170..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/sbin/telnetd
diff --git a/debian/busybox-symlinks-tftp.links b/debian/busybox-symlinks-tftp.links
deleted file mode 100644 (file)
index f9d5794..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/tftp
diff --git a/debian/busybox-symlinks-time.links b/debian/busybox-symlinks-time.links
deleted file mode 100644 (file)
index 9cfd606..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/time
diff --git a/debian/busybox-symlinks-tofrodos.links b/debian/busybox-symlinks-tofrodos.links
deleted file mode 100644 (file)
index 40760e8..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-bin/busybox usr/bin/dos2unix
-bin/busybox usr/bin/unix2dos
diff --git a/debian/busybox-symlinks-udhcpc.links b/debian/busybox-symlinks-udhcpc.links
deleted file mode 100644 (file)
index 45827b4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/udhcpc
diff --git a/debian/busybox-symlinks-udhcpd.links b/debian/busybox-symlinks-udhcpd.links
deleted file mode 100644 (file)
index 62f4d1e..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-bin/busybox usr/bin/dumpleases
-bin/busybox usr/sbin/udhcpd
diff --git a/debian/busybox-symlinks-unzip.links b/debian/busybox-symlinks-unzip.links
deleted file mode 100644 (file)
index 9e7bba2..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/unzip
diff --git a/debian/busybox-symlinks-vlan.links b/debian/busybox-symlinks-vlan.links
deleted file mode 100644 (file)
index bc2fce5..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox sbin/vconfig
diff --git a/debian/busybox-symlinks-vlock.links b/debian/busybox-symlinks-vlock.links
deleted file mode 100644 (file)
index a42391a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/vlock
diff --git a/debian/busybox-symlinks-watchdog.links b/debian/busybox-symlinks-watchdog.links
deleted file mode 100644 (file)
index c7cfe6e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/sbin/watchdog
diff --git a/debian/busybox-symlinks-wget.links b/debian/busybox-symlinks-wget.links
deleted file mode 100644 (file)
index 3d55978..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/wget
diff --git a/debian/busybox-symlinks-xterm.links b/debian/busybox-symlinks-xterm.links
deleted file mode 100644 (file)
index aa45607..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/resize
diff --git a/debian/busybox-symlinks-zcip.links b/debian/busybox-symlinks-zcip.links
deleted file mode 100644 (file)
index 5e8f40d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bin/busybox usr/bin/zcip
diff --git a/debian/busybox-syslogd.busybox-klogd.init b/debian/busybox-syslogd.busybox-klogd.init
deleted file mode 100644 (file)
index 9acda52..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-#!/bin/sh
-#
-# init.d script with LSB support.
-#
-# Copyright (c) 2007 Javier Fernandez-Sanguino <jfs@debian.org>
-# Copyright (c) 2008 Axel Beckert <abe@deuxchevaux.org>
-#
-# This is free software; you may redistribute it and/or modify
-# it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; either version 2,
-# or (at your option) any later version.
-#
-# This 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License with
-# the Debian operating system, in /usr/share/common-licenses/GPL;  if
-# not, write to the Free Software Foundation, Inc., 59 Temple Place,
-# Suite 330, Boston, MA 02111-1307 USA
-#
-### BEGIN INIT INFO
-# Provides:          busybox-klogd klogd
-# Required-Start:    $remote_fs
-# Required-Stop:     $remote_fs
-# Should-Start:      syslogd
-# Should-Stop:
-# Default-Start:     2 3 4 5
-# Default-Stop:      0 1 6
-# Short-Description: Starts klogd
-# Description:       Starts the busybox klogd
-### END INIT INFO
-
-PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
-
-NAME=klogd         # Introduce the short server's name here
-DAEMON=/sbin/$NAME # Introduce the server's location here
-DESC="busybox' $NAME implementation" # Introduce a short description here
-NEEDED_OPTS=''
-DAEMON_USER='root'
-
-test -x $DAEMON || exit 0
-
-. /lib/lsb/init-functions
-
-# Default options, these can be overriden by the information
-# at /etc/default/$NAME
-KLOG_OPTS=""            # Additional options given to the server
-
-DIETIME=10              # Time to wait for the server to die, in seconds
-                        # If this value is set too low you might not
-                        # let some servers to die gracefully and
-                        # 'restart' will not work
-
-STARTTIME=2             # Time to wait for the server to start, in seconds
-                        # If this value is set each time the server is
-                        # started (on start or restart) the script will
-                        # stall to try to determine if it is running
-                        # If it is not set and the server takes time
-                        # to setup a pid file the log message might 
-                        # be a false positive (says it did not start
-                        # when it actually did)
-                        
-# Include defaults if available
-if [ -f /etc/default/busybox-syslogd ] ; then
-       . /etc/default/busybox-syslogd
-fi
-
-set -e
-
-start_server() {
-       start-stop-daemon --start --verbose --name $NAME \
-               --exec $DAEMON -- $NEEDED_OPTS $KLOG_OPTS
-}
-
-stop_server() {
-       start-stop-daemon --stop --verbose --name $NAME
-}
-
-running() {
-    cut -d ' ' -f 1-2 /proc/[0-9]*/stat 2> /dev/null | grep -F "($NAME)"
-}
-
-case "$1" in
-  start)
-       log_daemon_msg "Starting $DESC " "$NAME"
-        # Check if it's running first
-        if running ;  then
-            log_progress_msg "apparently already running"
-            log_end_msg 0
-            exit 0
-        fi
-        if start_server ; then
-            # NOTE: Some servers might die some time after they start,
-            # this code will detect this issue if STARTTIME is set
-            # to a reasonable value
-            [ -n "$STARTTIME" ] && sleep $STARTTIME # Wait some time 
-            if  running ;  then
-                # It's ok, the server started and is running
-                log_end_msg 0
-            else
-                # It is not running after we did start
-                log_end_msg 1
-            fi
-        else
-            # Either we could not start it
-            log_end_msg 1
-        fi
-       ;;
-
-  stop)
-        log_daemon_msg "Stopping $DESC" "$NAME"
-        if running ; then
-            # Only stop the server if we see it running
-                       errcode=0
-            stop_server || errcode=$?
-            log_end_msg $errcode
-        else
-            # If it's not running don't do anything
-            log_progress_msg "apparently not running"
-            log_end_msg 0
-            exit 0
-        fi
-        ;;
-
-  restart|force-reload)
-        log_daemon_msg "Restarting $DESC" "$NAME"
-               errcode=0
-        stop_server || errcode=$?
-        # Wait some sensible amount, some server need this
-        [ -n "$DIETIME" ] && sleep $DIETIME
-        start_server || errcode=$?
-        [ -n "$STARTTIME" ] && sleep $STARTTIME
-        running || errcode=$?
-        log_end_msg $errcode
-       ;;
-
-  status)
-        log_daemon_msg "Checking status of $DESC" "$NAME"
-        if running ;  then
-            log_progress_msg "running"
-            log_end_msg 0
-        else
-            log_progress_msg "apparently not running"
-            log_end_msg 1
-            exit 1
-        fi
-        ;;
-
-  *)
-       N=/etc/init.d/$NAME
-       echo "Usage: $N {start|stop|restart|force-reload|status}" >&2
-       exit 1
-       ;;
-esac
-
-exit 0
diff --git a/debian/busybox-syslogd.default b/debian/busybox-syslogd.default
deleted file mode 100644 (file)
index bc9a28f..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# Defaults for busybox-syslogd initscript
-# This is a POSIX shell fragment sourced by /etc/init.d/busybox-syslogd
-
-# Additional options that are passed to the daemons.  Default is to log
-# to ring buffer (to be read with logread(1)) and drop duplicates.
-SYSLOG_OPTS="-C128"
-KLOG_OPTS=""
diff --git a/debian/busybox-syslogd.init b/debian/busybox-syslogd.init
deleted file mode 100644 (file)
index f489dd2..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-#!/bin/sh
-#
-# init.d script with LSB support.
-#
-# Copyright (c) 2007 Javier Fernandez-Sanguino <jfs@debian.org>
-# Copyright (c) 2008 Axel Beckert <abe@deuxchevaux.org>
-#
-# This is free software; you may redistribute it and/or modify
-# it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; either version 2,
-# or (at your option) any later version.
-#
-# This 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License with
-# the Debian operating system, in /usr/share/common-licenses/GPL;  if
-# not, write to the Free Software Foundation, Inc., 59 Temple Place,
-# Suite 330, Boston, MA 02111-1307 USA
-#
-### BEGIN INIT INFO
-# Provides:          busybox-syslogd syslogd
-# Required-Start:    $remote_fs
-# Required-Stop:     $remote_fs
-# Should-Start:      
-# Should-Stop:
-# Default-Start:     2 3 4 5
-# Default-Stop:      0 1 6
-# Short-Description: Starts syslogd
-# Description:       Starts the busybox syslogd
-### END INIT INFO
-
-PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
-
-NAME=syslogd         # Introduce the short server's name here
-DAEMON=/sbin/$NAME # Introduce the server's location here
-DESC="busybox' $NAME implementation" # Introduce a short description here
-NEEDED_OPTS=''
-DAEMON_USER='root'
-
-test -x $DAEMON || exit 0
-
-. /lib/lsb/init-functions
-
-# Default options, these can be overriden by the information
-# at /etc/default/$NAME
-SYSLOG_OPTS=""          # Additional options given to the server
-
-DIETIME=10              # Time to wait for the server to die, in seconds
-                        # If this value is set too low you might not
-                        # let some servers to die gracefully and
-                        # 'restart' will not work
-
-#STARTTIME=2             # Time to wait for the server to start, in seconds
-                        # If this value is set each time the server is
-                        # started (on start or restart) the script will
-                        # stall to try to determine if it is running
-                        # If it is not set and the server takes time
-                        # to setup a pid file the log message might 
-                        # be a false positive (says it did not start
-                        # when it actually did)
-                        
-# Include defaults if available
-if [ -f /etc/default/busybox-syslogd ] ; then
-       . /etc/default/busybox-syslogd
-fi
-
-set -e
-
-start_server() {
-       start-stop-daemon --start --verbose --name $NAME \
-               --exec $DAEMON -- $NEEDED_OPTS $SYSLOG_OPTS
-}
-
-stop_server() {
-       start-stop-daemon --stop --quiet --name $NAME
-}
-
-running() {
-    cut -d ' ' -f 1-2 /proc/[0-9]*/stat 2> /dev/null | grep -F "($NAME)"
-}
-
-case "$1" in
-  start)
-       log_daemon_msg "Starting $DESC " "$NAME"
-        # Check if it's running first
-        if running ;  then
-            log_progress_msg "apparently already running"
-            log_end_msg 0
-            exit 0
-        fi
-        if start_server ; then
-            # NOTE: Some servers might die some time after they start,
-            # this code will detect this issue if STARTTIME is set
-            # to a reasonable value
-            [ -n "$STARTTIME" ] && sleep $STARTTIME # Wait some time 
-            if  running ;  then
-                # It's ok, the server started and is running
-                log_end_msg 0
-            else
-                # It is not running after we did start
-                log_end_msg 1
-            fi
-        else
-            # Either we could not start it
-            log_end_msg 1
-        fi
-        ;;
-  stop)
-        log_daemon_msg "Stopping $DESC" "$NAME"
-        if running ; then
-            # Only stop the server if we see it running
-                       errcode=0
-            stop_server || errcode=$?
-            log_end_msg $errcode
-        else
-            # If it's not running don't do anything
-            log_progress_msg "apparently not running"
-            log_end_msg 0
-            exit 0
-        fi
-        ;;
-  restart|force-reload)
-        log_daemon_msg "Restarting $DESC" "$NAME"
-               errcode=0
-        stop_server || errcode=$?
-        # Wait some sensible amount, some server need this
-        [ -n "$DIETIME" ] && sleep $DIETIME
-        start_server || errcode=$?
-        [ -n "$STARTTIME" ] && sleep $STARTTIME
-        running || errcode=$?
-        log_end_msg $errcode
-        ;;
-  status)
-
-        log_daemon_msg "Checking status of $DESC" "$NAME"
-        if running ;  then
-            log_progress_msg "running"
-            log_end_msg 0
-        else
-            log_progress_msg "apparently not running"
-            log_end_msg 1
-            exit 1
-        fi
-        ;;
-  # daemon cannot reload
-  reload)
-        log_warning_msg "Reloading $NAME daemon: not implemented, as the daemon"
-        log_warning_msg "cannot re-read the config file (use restart)."
-        ;;
-
-  *)
-       N=/etc/init.d/$NAME
-       echo "Usage: $N {start|stop|restart|force-reload|status}" >&2
-       exit 1
-        ;;
-esac
-
-exit 0
diff --git a/debian/busybox-syslogd.service b/debian/busybox-syslogd.service
deleted file mode 100644 (file)
index 36227a2..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-[Unit]
-Description=Busybox Logging Service: syslogd
-
-[Service]
-Type=simple
-ExecStartPre=/bin/systemctl stop systemd-kmsg-syslogd.service
-ExecStart=/sbin/syslogd -n
-Sockets=syslog.socket
-
-[Install]
-WantedBy=multi-user.target
diff --git a/debian/busybox-systemd-klogd.install b/debian/busybox-systemd-klogd.install
deleted file mode 100644 (file)
index cbab4d7..0000000
+++ /dev/null
@@ -1 +0,0 @@
-debian/busybox-klogd.service lib/systemd/system/
diff --git a/debian/busybox-systemd-klogd.links b/debian/busybox-systemd-klogd.links
deleted file mode 100644 (file)
index 0fae3ea..0000000
+++ /dev/null
@@ -1 +0,0 @@
-lib/systemd/system/busybox-klogd.service lib/systemd/system/multi-user.target.wants/busybox-klogd.service
diff --git a/debian/busybox-systemd-sysklogd.install b/debian/busybox-systemd-sysklogd.install
deleted file mode 100644 (file)
index 0379dc9..0000000
+++ /dev/null
@@ -1 +0,0 @@
-debian/busybox-syslogd.service lib/systemd/system/
diff --git a/debian/busybox-systemd-sysklogd.links b/debian/busybox-systemd-sysklogd.links
deleted file mode 100644 (file)
index 1a1f1aa..0000000
+++ /dev/null
@@ -1 +0,0 @@
-lib/systemd/system/busybox-syslogd.service lib/systemd/system/multi-user.target.wants/busybox-syslogd.service
diff --git a/debian/busybox-udeb.install b/debian/busybox-udeb.install
deleted file mode 100644 (file)
index d094590..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-../../tree/busybox-udeb/* /
-_install/* /
diff --git a/debian/busybox.dirs b/debian/busybox.dirs
deleted file mode 100644 (file)
index d653148..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-bin
-sbin
-usr/share/man/man1
diff --git a/debian/busybox.install b/debian/busybox.install
deleted file mode 100644 (file)
index b2b3f72..0000000
+++ /dev/null
@@ -1 +0,0 @@
-busybox bin
diff --git a/debian/busybox.links b/debian/busybox.links
deleted file mode 100644 (file)
index 97a2764..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-bin/busybox bin/ash
-bin/busybox bin/cat
-bin/busybox bin/chgrp
-bin/busybox bin/chmod
-bin/busybox bin/chown
-bin/busybox bin/cp
-bin/busybox bin/date
-bin/busybox bin/dd
-bin/busybox bin/df
-bin/busybox bin/dmesg
-bin/busybox bin/dnsdomainname
-bin/busybox bin/echo
-bin/busybox bin/egrep
-bin/busybox bin/false
-bin/busybox bin/fgrep
-bin/busybox bin/grep
-bin/busybox bin/gunzip
-bin/busybox bin/gzip
-bin/busybox bin/hostname
-bin/busybox bin/ln
-bin/busybox bin/ls
-bin/busybox bin/mkdir
-bin/busybox bin/mknod
-bin/busybox bin/mktemp
-bin/busybox bin/more
-bin/busybox bin/mount
-bin/busybox bin/mv
-bin/busybox bin/pwd
-bin/busybox bin/readlink
-bin/busybox bin/rm
-bin/busybox bin/rmdir
-bin/busybox bin/sed
-bin/busybox bin/sh
-bin/busybox bin/sleep
-bin/busybox bin/stty
-bin/busybox bin/sync
-bin/busybox bin/tar
-bin/busybox bin/touch
-bin/busybox bin/true
-bin/busybox bin/umount
-bin/busybox bin/uname
-bin/busybox bin/uncompress
-bin/busybox bin/zcat
-bin/busybox sbin/blkid
-bin/busybox sbin/blockdev
-bin/busybox sbin/fdisk
-bin/busybox sbin/fsck.minix
-bin/busybox sbin/getty
-bin/busybox sbin/hwclock
-bin/busybox sbin/losetup
-bin/busybox sbin/mkfs.minix
-bin/busybox sbin/mkswap
-bin/busybox sbin/pivot_root
-bin/busybox sbin/swapoff
-bin/busybox sbin/swapon
-bin/busybox sbin/switch_root
-bin/busybox usr/bin/[
-bin/busybox usr/bin/basename
-bin/busybox usr/bin/chrt
-bin/busybox usr/bin/cksum
-bin/busybox usr/bin/cmp
-bin/busybox usr/bin/comm
-bin/busybox usr/bin/cut
-bin/busybox usr/bin/diff
-bin/busybox usr/bin/dirname
-bin/busybox usr/bin/du
-bin/busybox usr/bin/env
-bin/busybox usr/bin/expand
-bin/busybox usr/bin/expr
-bin/busybox usr/bin/fdformat
-bin/busybox usr/bin/find
-bin/busybox usr/bin/flock
-bin/busybox usr/bin/fold
-bin/busybox usr/bin/getopt
-bin/busybox usr/bin/head
-bin/busybox usr/bin/hostid
-bin/busybox usr/bin/id
-bin/busybox usr/bin/install
-bin/busybox usr/bin/ionice
-bin/busybox usr/bin/ipcrm
-bin/busybox usr/bin/ipcs
-bin/busybox usr/bin/less
-bin/busybox usr/bin/linux32
-bin/busybox usr/bin/linux64
-bin/busybox usr/bin/logger
-bin/busybox usr/bin/logname
-bin/busybox usr/bin/md5sum
-bin/busybox usr/bin/mkfifo
-bin/busybox usr/bin/nice
-bin/busybox usr/bin/nohup
-bin/busybox usr/bin/od
-bin/busybox usr/bin/printenv
-bin/busybox usr/bin/printf
-bin/busybox usr/bin/renice
-bin/busybox usr/bin/rev
-bin/busybox usr/bin/rtcwake
-bin/busybox usr/bin/script
-bin/busybox usr/bin/scriptreplay
-bin/busybox usr/bin/seq
-bin/busybox usr/bin/setarch
-bin/busybox usr/bin/setsid
-bin/busybox usr/bin/sha1sum
-bin/busybox usr/bin/sha256sum
-bin/busybox usr/bin/sha512sum
-bin/busybox usr/bin/sort
-bin/busybox usr/bin/split
-bin/busybox usr/bin/stat
-bin/busybox usr/bin/sum
-bin/busybox usr/bin/tac
-bin/busybox usr/bin/tail
-bin/busybox usr/bin/taskset
-bin/busybox usr/bin/tee
-bin/busybox usr/bin/test
-bin/busybox usr/bin/timeout
-bin/busybox usr/bin/tr
-bin/busybox usr/bin/tty
-bin/busybox usr/bin/unexpand
-bin/busybox usr/bin/uniq
-bin/busybox usr/bin/wall
-bin/busybox usr/bin/wc
-bin/busybox usr/bin/who
-bin/busybox usr/bin/whoami
-bin/busybox usr/bin/xargs
-bin/busybox usr/bin/yes
-bin/busybox usr/sbin/chroot
-bin/busybox usr/sbin/rdev
-bin/busybox usr/sbin/readprofile
diff --git a/debian/busybox.postinst b/debian/busybox.postinst
deleted file mode 100644 (file)
index d48f82a..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-set -e
-update-alternatives --install /usr/bin/pager pager /bin/more 10
-update-alternatives --install /usr/bin/man man /bin/busybox 1
-update-alternatives --install /usr/bin/traceroute traceroute /bin/busybox 1
-update-alternatives --install /usr/bin/pager pager /usr/bin/less 20
-update-alternatives --install /usr/bin/telnet telnet /bin/busybox 1
-update-alternatives --install /usr/bin/awk awk /bin/busybox 1
-update-alternatives --install /usr/bin/vi vi /bin/busybox 1
-dpkg-divert --package busybox --add /bin/sh
diff --git a/debian/busybox.prerm b/debian/busybox.prerm
deleted file mode 100644 (file)
index ec46dd0..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-set -e
-update-alternatives --remove pager /bin/more
-update-alternatives --remove man /bin/busybox
-update-alternatives --remove traceroute /bin/busybox
-update-alternatives --remove pager /usr/bin/less
-update-alternatives --remove telnet /bin/busybox
-update-alternatives --remove awk /bin/busybox
-update-alternatives --remove vi /bin/busybox
-dpkg-divert --package busybox --remove /bin/sh
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644 (file)
index ac9f800..0000000
+++ /dev/null
@@ -1,1350 +0,0 @@
-busybox (3:1.17.1-10slp2+s8) unstable; urgency=low
-
-  * Fix displaying of unicode characters.
-  * Git: slp/pkgs/b/busybox
-  * Tag: busybox_3%1.17.1-10slp2+s8
-
- -- Rafal Krypa <r.krypa@samsung.com>  Mon, 16 Apr 2012 13:10:58 +0200
-
-busybox (3:1.17.1-10slp2+s7) unstable; urgency=low
-
-  * slp: Disable chat(1)
-  * Git: slp/pkgs/b/busybox
-  * Tag: busybox_3%1.17.1-10slp2+s7
-
- -- Karol Lewandowski <k.lewandowsk@samsung.com>  Tue, 17 Jan 2012 11:41:58 +0100
-
-busybox (3:1.17.1-10slp2+s6-1) unstable; urgency=low
-
-  * Add tag required by build server
-  * Git: slp/pkgs/b/busybox
-  * Tag: busybox_3%1.17.1-10slp2+s6-1
-
- -- Karol Lewandowski <k.lewandowsk@samsung.com>  Thu, 12 Jan 2012 12:45:55 +0100
-
-busybox (3:1.17.1-10slp2+s6) unstable; urgency=low
-
-  * slp: Import SMACK patch
-  * slp: Don't allow SELINUX and SMACK to be enabled at the same time
-  * slp: Fix obvious mistake in smack patch
-
- -- Karol Lewandowski <k.lewandowsk@samsung.com>  Wed, 11 Jan 2012 12:31:43 +0100
-
-busybox (3:1.17.1-10slp2+s5) unstable; urgency=low
-
-  * klogd: Handle facility too when reading from /proc/kmsg
-  * Make sure klogd starts after busybox's syslogd
-  * Ugly hack: Increase console log level to make camera app work
-
- -- Karol Lewandowski <k.lewandowsk@samsung.com>  Mon, 07 Nov 2011 15:40:21 +0100
-
-busybox (3:1.17.1-10slp2+s4) unstable; urgency=low
-
-  * mount: Handle list of comma-separated fs types ("-t" option)
-
- -- Karol Lewandowski <k.lewandowsk@samsung.com>  Mon, 10 Oct 2011 11:31:10 +0200
-
-busybox (3:1.17.1-10slp2+s3) unstable; urgency=low
-
-  * Support receiving server socket from systemd in syslogd
-  * Add packages with systemd units for syslogd and klogd
-
- -- Karol Lewandowski <k.lewandowsk@samsung.com>  Thu, 01 Sep 2011 19:14:44 +0200
-
-busybox (3:1.17.1-10slp2+s2) unstable; urgency=low
-
-  [ Rafal Krypa ]
-  * Enable mktemp applet.
-  * Make debian/scripts/*.py files executable.
-  * debian/scripts/README: adapt to SLP
-
-  [ Mike McCormack ]
-  * Disable netcat.
-
-  [ Yeongil Jang ]
-  * Add CONFIG_LZOP_COMPR_HIGH option for building 2.6.36 kernel
-
- -- Rafal Krypa <r.krypa@samsung.com>  Mon, 01 Aug 2011 11:31:35 +0200
-
-busybox (3:1.17.1-10slp2+s1) unstable; urgency=low
-
-  * Bump epoch back to 3 to let things upgrade correctly.
-
- -- Mike McCormack <mj.mccormack@samsung.com>  Tue, 19 Apr 2011 10:51:42 +0900
-
-busybox (1:1.17.1-10slp2+s1) unstable; urgency=low
-
-  [ Minho Ban ]
-  * getty: Disable SW flow control (XON/XOFF).
-
-  [ Rafal Krypa ]
-  * Merge with Debian.
-  * Enable blockdev applet, backported by Debian.
-
- -- Rafal Krypa <r.krypa@samsung.com>  Fri, 15 Apr 2011 13:19:25 +0200
-
-busybox (1:1.17.1-10) unstable; urgency=low
-
-  [ Michael Tokarev ]
-  * tiny build system changes: from main source to build directories:
-    don't copy .dotfiles, use ln instead of cp
-
-  [ Joey Hess ]
-  * Enable sha256sum in udeb, needed by debootstrap to handle Release
-    files w/o md5sums.
-  * Did not disable md5sum in udeb because eg anna still uses them to verify
-    md5sum fields from d-i Packages files. (Those fields are still present..
-    for now.)
-
- -- Joey Hess <joeyh@debian.org>  Mon, 21 Feb 2011 20:32:15 -0400
-
-busybox (1:1.17.1-9) unstable; urgency=low
-
-  * Team upload
-
-  [ Matt Palmer ]
-  * Build busybox udeb with IPv6 support, since otherwise wget has a
-    severe sadness.
-
-  [ Christian Perrier ]
-  * Add arping to udeb to allow link detection in netcfg
-    Closes: #612249
-
- -- Christian Perrier <bubulle@debian.org>  Sun, 13 Feb 2011 08:23:54 +0100
-
-busybox (1:1.17.1-8) unstable; urgency=low
-
-  * Set udhcpc and udhcpd Section to net, to match overrides.
-  * Disable blockdev on non-Linux architectures.
-
- -- Colin Watson <cjwatson@debian.org>  Mon, 15 Nov 2010 14:36:31 +0000
-
-busybox (1:1.17.1-7) unstable; urgency=low
-
-  * Backport blockdev applet from upstream, so that we can use it in
-    os-prober (closes: #418163).
-
- -- Colin Watson <cjwatson@debian.org>  Tue, 09 Nov 2010 12:33:35 +0000
-
-busybox (3:1.17.1-6slp2+s4) unstable; urgency=low
-
-  * Increase size of log file
-
- -- Mike McCormack <mj.mccormack@samsung.com>  Wed, 16 Mar 2011 17:16:34 +0900
-
-busybox (3:1.17.1-6slp2+s3) unstable; urgency=low
-
-  * Add SLP patch: udhcpc-fast-request.patch.
-  * Enable the new feature.
-
- -- Rafal Krypa <r.krypa@samsung.com>  Fri, 10 Dec 2010 12:53:38 +0100
-
-busybox (3:1.17.1-6slp2+s2) unstable; urgency=low
-
-  * SLP: adjust the config to be more compatible with BB provided by the
-    kernel team.
-  * Remove dependency on busybox-symlinks-busybox from the busybox
-    package to resolve dependency loop.
-
- -- Rafal Krypa <r.krypa@samsung.com>  Sun, 28 Nov 2010 12:39:33 +0900
-
-busybox (3:1.17.1-6slp2+s1) unstable; urgency=low
-
-  * debian/patches/03tar.dpatch: remove - applied in upstream.
-  * debian/patches/top-display-rss.patch: adapt to new upstream version.
-  * debian/config/slp: move to new location debian/config/pkg/slp.
-  * Adapt config to new upstream version, enable: wall, flock, rev.
-
- -- Rafal Krypa <r.krypa@samsung.com>  Fri, 05 Nov 2010 20:35:13 +0900
-
-busybox (1:1.17.1-6) unstable; urgency=low
-
-  * Apply patch from Samuel Thibault <sthibault@debian.org> to enable
-    hardware flow control for serial console (closes: #528560).
-
- -- Otavio Salvador <otavio@debian.org>  Thu, 21 Oct 2010 16:17:03 -0200
-
-busybox (1:1.17.1-5) unstable; urgency=low
-
-  [ Colin Watson ]
-  * Never execute busybox applets when chrooting (closes: #599101).
-
-  [ Otavio Salvador ]
-  * Fix initscript error messages in syslogd and klogd (closes:
-    #591436).
-  * Fix restart and force-reload for udhcpd initscript (closes:
-    #597510).
-
- -- Otavio Salvador <otavio@debian.org>  Thu, 21 Oct 2010 11:53:52 -0200
-
-busybox (1:1.17.1-4) unstable; urgency=low
-
-  * Update u-mount-FreeBSD-support.patch: 
-    - Add a few more mount options.
-    - Fix mounting of nullfs filesystems.
-
- -- Aurelien Jarno <aurel32@debian.org>  Mon, 23 Aug 2010 10:19:20 +0200
-
-busybox (1:1.17.1-3) unstable; urgency=low
-
-  [ Jérémie Koenig ]
-  * Enable CONFIG_FEATURE_INITRD on Hurd (Closes: #593865).
-
-  [ Aurelien Jarno ]
-  * [udeb] Disable volume id support.
-
- -- Aurelien Jarno <aurel32@debian.org>  Sun, 22 Aug 2010 19:46:52 +0200
-
-busybox (1:1.17.1-2) unstable; urgency=low
-
-  * Upload to unstable.
-  * [udeb] Enable volume id support.
-  * [deb] Enable all file system in volume id support.
-
- -- Aurelien Jarno <aurel32@debian.org>  Fri, 06 Aug 2010 22:54:40 +0200
-
-busybox (1:1.17.1-1) experimental; urgency=low
-
-  [ Aurelien Jarno ]
-  * New upstream release.
-    - Add --list option (Closes: #405108).
-    - Fix nc segfaults (Closes: #503672).
-    - Enable new options:
-      [all] Add support for unicode (Closes: #395227, #570789)
-      [all] Enable btrfs volume identification
-  * Update configuration:
-    [udeb/deb] Enable dd conv=notrunc (Closes: #552495)
-  * Fix busybox-udeb description: s/busybox-cvs-udeb/busybox-udeb/
-  * Fix busybox description wrt --install option (Closes: #326243).
-  * Fix /etc/init.d/udhcpd restart (Closes: #581342)
-  * Fix /etc/init.d/busybox-{klogd,syslogd} to correctly depend on 
-    $remote_fs.
-  * Fix bashism in udhcpc default.script (Closes: #572008, #572013,
-    #572622).
-
-  [ Jérémie Koenig ]
-  * Refresh patches:
-    - debian/patches/applets-fallback.patch
-    - debian/patches/doc-man-name.patch
-    - debian/patches/shell-ash-export-HOME.patch
-  * Add per-OS configuration overrides.
-  * Add FreeBSD and Hurd compatibility patches from the latest git upstream
-    branch, and a few more to be merged soon (Closes: #323670).
-  * scripts/gen_build_files.sh: skip .pc files from quilt.
-
- -- Aurelien Jarno <aurel32@debian.org>  Tue, 03 Aug 2010 06:42:39 +0200
-
-busybox (3:1.15.3-1slp2+s5) unstable; urgency=low
-
-  * debian/scripts/debian-mappings.txt: fix mappings for mktemp,
-    sha256sum, sha512sum, dnsdomainname.
-  * debian/scripts/create-control.py: modify dependencies for busybox,
-    don't start output with blank line.
-  * debian/config/slp: disable lzop, enable applets for 'adduser'
-    package.
-  * Recreate control and *.links files.
-
- -- Rafal Krypa <r.krypa@samsung.com>  Thu, 14 Oct 2010 12:18:08 +0200
-
-busybox (3:1.15.3-1+s4) unstable; urgency=low
-
-  * Apply all quilt patches missed during import (this is 3.0 (quilt)
-    source format).
-  * Apply our quilt patches as well.
-  * Fix busybox-zero-ifr.ifr_hwaddr.sa_data.patch to apply without fuzz
-    (solves problem with dpkg-source).
-
- -- Rafal Krypa <r.krypa@samsung.com>  Wed, 16 Jun 2010 15:15:14 +0200
-
-busybox (3:1.15.3-1+s3) unstable; urgency=low
-
-  * Drop /bin/login and /bin/su for slp - it uses standard "shadow"
-    tools now
-  * Update maintaners and uploaders lists
-  * Add Essential tag to busybox package
-  * Don't use lsb_release in slp variant
-  * Kill bogus binary-arch_slp rule
-
- -- Karol Lewandowski <k.lewandowsk@samsung.com>  Wed, 19 May 2010 19:03:35 +0200
-
-busybox (3:1.15.3-1+s2) unstable; urgency=low
-
-  * Workaround for Scratchbox 1 apt problems - drop the Essential tag on
-    busybox package.
-  * Scratchbox 1 workaround - reduce debhelper compat level to 5.
-
- -- Rafal Krypa <r.krypa@samsung.com>  Tue, 30 Mar 2010 12:42:43 +0200
-
-busybox (3:1.15.3-1+s1) unstable; urgency=low
-
-  * Merge Debian version 1.15.3-1
-  * Drop patches with features that are now in upstream:
-    - fsync.patch
-    - pidof-fix.patch
-    - ls-s-dont-follow-links.patch
-    - upstart-support.patch
-  * Adapt patches to new upstream version:
-    - 03tar.dpatch
-    - top-display-rss.patch
-  * debian/rules: fix install source path for busybox binary
-  * debian/config/slp: using some new features
-  * Don't apply shell-hist.patch - it is incompatible with new upstream version
-    and we don't need it anyway.
-
- -- Rafal Krypa <r.krypa@samsung.com>  Wed, 24 Mar 2010 18:23:28 +0100
-
-busybox (3:1.10.2.legal-1osso26+0m5+s1) unstable; urgency=low
-
-  * Update configuration of Debian essential packages according to
-    current Sid version.
-  * Configure for SLP 2.0.
-  * Drop build-dependency on lsb-release.
-  * debian/control: added Uploaders field.
-
- -- Rafal Krypa <r.krypa@samsung.com>  Thu, 04 Feb 2010 10:41:03 +0900
-
-busybox (3:1.10.2.legal-1osso26+0m5) unstable; urgency=low
-
-  * This entry has been added by BIFH queue processor
-    Suffix +0m5 added to package revision
-
- -- Attiq Malik <ext-attiq.1.malik@nokia.com>  Wed, 01 Jul 2009 13:11:49 +0300
-
-busybox (3:1.10.2.legal-1osso26) unstable; urgency=low
-
-  * Enable more fance tar features. Fixes: NB#124832.
-
- -- Alexander Shishkin <ext-alexander.shishkin@nokia.com>  Mon, 29 Jun 2009 15:32:56 +0300
-
-busybox (3:1.10.2.legal-1osso25) unstable; urgency=low
-
-  * Call telinit to facilitate proper shutdown/reboot. Fixes: NB#120283.
-
- -- Alexander Shishkin <ext-alexander.shishkin@nokia.com>  Wed, 24 Jun 2009 14:41:13 +0300
-
-busybox (3:1.10.2.legal-1osso24) unstable; urgency=low
-
-  * Don't follow symlinks for -l or -s in ls. Fixes: NB#120999.
-
- -- Alexander Shishkin <ext-alexander.shishkin@nokia.com>  Mon, 15 Jun 2009 12:55:17 +0300
-
-busybox (3:1.10.2.legal-1osso23) unstable; urgency=low
-
-  * Enable more options for pidof. Fixes: NB#112823.
-
- -- Alexander Shishkin <ext-alexander.shishkin@nokia.com>  Thu, 23 Apr 2009 16:10:24 +0300
-
-busybox (3:1.10.2.legal-1osso22) unstable; urgency=low
-
-  * Remove 3 second sleep()s from sfdisk.
-
- -- Alexander Shishkin <ext-alexander.shishkin@nokia.com>  Thu, 23 Apr 2009 11:32:08 +0300
-
-busybox (3:1.10.2.legal-1osso21) unstable; urgency=low
-
-  * Drop dependencies to symlink packages. Fixes: NB#86011.
-
- -- Alexander Shishkin <ext-alexander.shishkin@nokia.com>  Wed, 01 Apr 2009 14:14:32 +0300
-
-busybox (3:1.10.2.legal-1osso20) unstable; urgency=low
-
-  * Merge sysvinit-maemo into busybox.
-  * Remove last from provided symlinks. Fixes: NB#108560.
-  * Patch to display RSS instead of VSZ in top. Fixes: NB#103591.
-  * Patch to only update history upon shell exit. Fixes: NB#107770.
-
- -- Alexander Shishkin <ext-alexander.shishkin@nokia.com>  Tue, 31 Mar 2009 14:28:47 +0300
-
-busybox (3:1.10.2.legal-1osso19) unstable; urgency=low
-
-  * Create alternatives directory if missing. Fixes: NB#107784.
-  * Enable CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY. Fixes: NB#107771.
-  * Disable wtmp for applets. Fixes: NB#101256.
-
- -- Alexander Shishkin <ext-alexander.shishkin@nokia.com>  Wed, 25 Mar 2009 10:28:05 +0200
-
-busybox (3:1.10.2.legal-1osso18) unstable; urgency=low
-
-  * Transition: make busybox depend on all packages with symlinks
-  to facilitate NB#86011.
-  * Symlinks moved to separate packages to match debian's counterparts.
-  * Enable xargs support for quotes. Fixes: NB#96575.
-
- -- Alexander Shishkin <ext-alexander.shishkin@nokia.com>  Wed, 18 Mar 2009 12:39:35 +0200
-
-busybox (3:1.10.2.legal-1osso17) unstable; urgency=low
-
-  * Add IPv6 support. Fixes: NB#98038.
-  * Add fix for pidof/killall5. Fixes: NB#101755.
-
- -- Alexander Shishkin <ext-alexander.shishkin@nokia.com>  Tue, 10 Mar 2009 15:14:05 +0200
-
-busybox (3:1.10.2.legal-1osso16) unstable; urgency=low
-
-  * Added CONFIG_CLEAR and CONFIG_RESET to config.maemo
-  * Added ncurses-bin to Provides/Replaces/Conflicts. Fixes: NB#92440
-
- -- Turo Janka <ext-turo.1.janka@nokia.com>  Thu, 12 Feb 2009 12:50:42 +0200
-
-busybox (3:1.10.2.legal-1osso15) unstable; urgency=low
-
-  * Removed CONFIG_FEATURE_GUNZIP_UNCOMPRESS from config.maemo
-  * Added CONFIG_UNCOMPRESS to config.maemo. Fixes: NB#98035
-
- -- Turo Janka <ext-turo.1.janka@nokia.com>  Wed, 11 Feb 2009 16:08:12 +0200
-
-busybox (3:1.10.2.legal-1osso14) unstable; urgency=low
-  
-  * Fixes time command segfault. Fixes: NB#98230
- -- Turo Janka <ext-turo.1.janka@nokia.com>  Tue, 21 Jan 2009 15:48:12 +0200
-
-busybox (3:1.10.2.legal-1osso13) unstable; urgency=low
-
-  * Enable more tools. Fixes: NB#98035
-
- -- Turo Janka <ext-turo.1.janka@nokia.com>  Tue, 20 Jan 2009 15:18:12 +0200
-
-busybox (3:1.10.2.legal-1osso12) unstable; urgency=low
-
-  * Enable MD5SUM and FEATURE_MD5_SHA1_SUM_CHECK. Fixes: NB#95950
-
- -- Turo Janka <ext-turo.1.janka@nokia.com>  Tue, 20 Jan 2009 11:16:20 +0200
-
-busybox (3:1.10.2.legal-1osso11) unstable; urgency=low
-
-  * Enable CONFIG_LFS. Fixes: NB#95830
-
- -- Turo Janka <ext-turo.1.janka@nokia.com>  Tue, 13 Jan 2009 15:26:23 +0200
-
-busybox (3:1.10.2.legal-1osso10) unstable; urgency=low
-
-  * Enable CONFIG_FEATURE_SORT_BIG. Fixes: NB#90925
-
- -- Yauheni Kaliuta <yauheni.kaliuta@nokia.com>  Mon, 27 Oct 2008 12:20:50 +0200
-
-busybox (3:1.10.2.legal-1osso9) unstable; urgency=low
-
-  * Disable runlevel, using a script from mini-rc
-
- -- Yauheni Kaliuta <yauheni.kaliuta@nokia.com>  Mon, 27 Oct 2008 11:54:48 +0200
-
-busybox (3:1.10.2.legal-1osso8) unstable; urgency=low
-
-  * Added busybox-zero-ifr.ifr_hwaddr.sa_data.patch
-    Fixes: NB#89189
-
- -- Yauheni Kaliuta <yauheni.kaliuta@nokia.com>  Fri, 17 Oct 2008 15:38:53 +0300
-
-busybox (3:1.10.2.legal-1osso7) unstable; urgency=low
-
-  * Reverting the utilities, it breaks R&D images
-  * Revert "enabled xargs -0 (265 bytes)"
-  * Revert "enabled addgroup (872 bytes), delgroup (652 bytes)"
-  * Revert "enabled less (6773 bytes)"
-  * Revert "enabled setsid (111 bytes)"
-  * Revert "enabled script (1443 bytes), strings (489 bytes)"
-  * Revert "enabled ip (23780 bytes)"
-
- -- Yauheni Kaliuta <yauheni.kaliuta@nokia.com>  Thu, 09 Oct 2008 15:26:35 +0300
-
-busybox (3:1.10.2.legal-1osso6) unstable; urgency=low
-
-  [ Pekka Pessi ]
-  * fsync.patch: fixed usage
-  * enabled xargs -0 (265 bytes)
-  * enabled addgroup (872 bytes), delgroup (652 bytes)
-  * enabled less (6773 bytes)
-  * enabled setsid (111 bytes)
-  * enabled script (1443 bytes), strings (489 bytes)
-  * enabled ip (23780 bytes)
-
- -- Yauheni Kaliuta <yauheni.kaliuta@nokia.com>  Wed, 08 Oct 2008 13:16:50 +0300
-
-busybox (3:1.10.2.legal-1osso5) unstable; urgency=low
-
-  * config.maemo: enable fsync
-  * Added fsync.patch
-
- -- Yauheni Kaliuta <yauheni.kaliuta@nokia.com>  Tue, 07 Oct 2008 11:15:54 +0300
-
-busybox (3:1.10.2.legal-1osso4) unstable; urgency=low
-
-  * Disable modutils, submitting real module-init-tools now
-
- -- Yauheni Kaliuta <yauheni.kaliuta@nokia.com>  Thu, 02 Oct 2008 16:37:48 +0300
-
-busybox (3:1.10.2.legal-1osso3) unstable; urgency=low
-
-  * Enable hwclock applet
-
- -- Yauheni Kaliuta <yauheni.kaliuta@nokia.com>  Wed, 24 Sep 2008 16:38:48 +0300
-
-busybox (3:1.10.2.legal-1osso2) unstable; urgency=low
-
-  * Install local/tempfile, forgot in upgrade.
-    Fixes: NB#88128
-
- -- Yauheni Kaliuta <yauheni.kaliuta@nokia.com>  Thu, 18 Sep 2008 14:23:08 +0300
-
-busybox (3:1.10.2.legal-1osso1) unstable; urgency=low
-
-  * Use .legal name (forgot)
-  * Updated config.maemo for 1.10.2
-
- -- Yauheni Kaliuta <yauheni.kaliuta@nokia.com>  Thu, 24 Jul 2008 12:35:10 +0300
-
-busybox (3:1.10.2-1osso1) unstable; urgency=low
-
-  * osso adoptation
-  * Added debug package
-  * tar -m dummy switch added
-  * build only deb package, no udeb or static
-  * CONFIG: fixing segfault
-  * Add tools needed by sysvinit/upstart
-  * Do not conflict with module-init-tools, divert should work
-
- -- Yauheni Kaliuta <yauheni.kaliuta@nokia.com>  Thu, 24 Jul 2008 12:22:11 +0300
-
-busybox (1:1.10.2-1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Bastian Blank <waldi@debian.org>  Fri, 06 Jun 2008 19:20:43 +0200
-
-busybox (1:1.9.2-3) unstable; urgency=low
-
-  * Readd package version into greeting.
-  * Ignore init action if device is not available. (closes: #473659)
-
- -- Bastian Blank <waldi@debian.org>  Sat, 12 Apr 2008 17:23:30 +0200
-
-busybox (1:1.9.2-2) unstable; urgency=low
-
-  * Set correct source section.
-  * Move busybox-static to extra.
-  * Update Standards-Version to 3.7.3, no changes.
-  * Add generic applet fallback. (closes: #472653)
-
- -- Bastian Blank <waldi@debian.org>  Wed, 26 Mar 2008 11:33:59 +0100
-
-busybox (1:1.9.2-1) unstable; urgency=low
-
-  * New upstream release.
-  * [deb] Reenable prefer applets.
-  * [deb, static] Make the shell use applets first. (closes: #472389)
-
- -- Bastian Blank <waldi@debian.org>  Mon, 24 Mar 2008 09:40:13 +0100
-
-busybox (1:1.9.1-3) unstable; urgency=low
-
-  * debian/config.udeb:
-    - Reenable fancy prompt.
-  * Readd copyright file.
-
- -- Bastian Blank <waldi@debian.org>  Sat, 22 Mar 2008 23:01:57 +0100
-
-busybox (1:1.9.1-2) experimental; urgency=low
-
-  * Readd sections gc to dynamic built binaries.
-  * debian/config.udeb:
-    - Fix remaining problems.
-    - Disable ifconfig.
-  * util-linux/mount.c:
-    - Support relatime. (closes: #460824)
-
- -- Bastian Blank <waldi@debian.org>  Thu, 13 Mar 2008 14:25:53 +0100
-
-busybox (1:1.9.1-1) experimental; urgency=low
-
-  * New upstream release.
-  * Drop unused busybox-floppy-udeb.
-
- -- Bastian Blank <waldi@debian.org>  Fri, 15 Feb 2008 16:25:31 +0000
-
-busybox (1:1.1.3-5) unstable; urgency=low
-
-  * debian/config*:
-    - Drop devfs support. (closes: #431569)
-
- -- Bastian Blank <waldi@debian.org>  Tue,  3 Jul 2007 15:45:43 +0000
-
-busybox (1:1.1.3-4) unstable; urgency=low
-
-  * coreutils/sort.c:
-    - Try again to fix sorting issues. Patch by Goswin Brederlow.
-      (closes: #373704)
-
- -- Bastian Blank <waldi@debian.org>  Sun, 21 Jan 2007 15:29:02 +0000
-
-busybox (1:1.1.3-3) unstable; urgency=low
-
-  * debian/control:
-    - Add build dependency to new dpkg-dev. (closes: #383710)
-  * debian/copyright:
-    - Mention copyright holders. (closes: #378654)
-
- -- Bastian Blank <waldi@debian.org>  Fri, 18 Aug 2006 22:32:04 +0000
-
-busybox (1:1.1.3-2) unstable; urgency=low
-
-  * coreutils/sort.c:
-    - Fix sorting of input which begins with the seperator. (closes: #373704)
-
- -- Bastian Blank <waldi@debian.org>  Thu, 29 Jun 2006 19:36:19 +0000
-
-busybox (1:1.1.3-1) unstable; urgency=low
-
-  * New upstream version.
-  * debian/config-udeb:
-    - Enable fancy head and tail. Readds support for head -1.
-  * coreutils/tr.c:
-    - Don't segfault on incorrect input.
-
- -- Bastian Blank <waldi@debian.org>  Thu, 01 Jun 2006 18:02:00 +0000
-
-busybox (1:1.1.2-2) unstable; urgency=low
-
-  * applets/install.sh:
-    - Fix installation if libbusybox is disabled. (closes: #368013)
-  * debian/config*:
-    - Support umount -a. (closes: #367714)
-
- -- Bastian Blank <waldi@debian.org>  Mon, 22 May 2006 17:58:17 +0000
-
-busybox (1:1.1.2-1) unstable; urgency=low
-
-  * New upstream version.
-  * debian/config*:
-    - Rework.
-    - Remove modutils support.
-  * modutils:
-    - Remove modutils patch.
-  * shell:
-    - Export PATH. (closes: #329406)
-
- -- Bastian Blank <waldi@debian.org>  Sun,  7 May 2006 19:31:17 +0000
-
-busybox (1:1.01-4) unstable; urgency=low
-
-  * debian/config*: (closes: #321304)
-    - Enable readlink -f support.
-    - Enable egrep alias.
-    - Enable ash command builtin.
-  * debianutils: Add readlink -f support.
-  * util-linux/umount.c: Fix loop for rootfs. (closes: #317062)
-
- -- Bastian Blank <waldi@debian.org>  Sat, 17 Dec 2005 14:35:56 +0100
-
-busybox (1:1.01-3) unstable; urgency=low
-
-  * debian/config-deb:
-    - Recheck options.
-
- -- Bastian Blank <waldi@debian.org>  Wed, 12 Oct 2005 14:40:08 +0200
-
-busybox (1:1.01-2) unstable; urgency=low
-
-  * debian/rules:
-    - Use CONFIG_DEBUG=y.
-  * modutils/obj:
-    - Fix bad casts that cause insmod (and probably depmod) to fail on
-      64-bit architectures. (closes: #321503)
-
- -- Bastian Blank <waldi@debian.org>  Thu, 08 Sep 2005 17:07:21 +0200
-
-busybox (1:1.01-1) unstable; urgency=low
-
-  * New upstream version.
-  * Fix more compilation errors. (closes: #325244)
-
- -- Bastian Blank <waldi@debian.org>  Wed, 24 Aug 2005 19:28:13 +0200
-
-busybox (1:1.00-5) unstable; urgency=low
-
-  * Fix build.
-  * Enable ip applet in deb.
-  * Fix remove syslog.
-
- -- Bastian Blank <waldi@debian.org>  Tue, 16 Aug 2005 19:20:13 +0200
-
-busybox (1:1.00-4) unstable; urgency=low
-
-  * Fix several problems with more strict gcc. (closes: #294474)
-
- -- Bastian Blank <waldi@debian.org>  Sun, 31 Jul 2005 13:10:29 +0200
-
-busybox (1:1.00-3) unstable; urgency=low
-
-  * shell/ash.c:
-    - Fix eval. (closes: #315444)
-
- -- Bastian Blank <waldi@debian.org>  Tue, 28 Jun 2005 14:45:54 +0200
-
-busybox (1:1.00-2) unstable; urgency=low
-
-  * debian/rules:
-    - Specify -s for debhelper tools. (closes: #314512)
-
- -- Bastian Blank <waldi@debian.org>  Mon, 27 Jun 2005 18:09:52 +0200
-
-busybox (1:1.00-1) unstable; urgency=low
-
-  * New upstream release. (closes: #276771)
-  * New maintainer. (closes: #298363)
-
- -- Bastian Blank <waldi@debian.org>  Fri, 10 Jun 2005 10:32:29 +0200
-
-busybox-cvs (20040623-2) UNRELEASED; urgency=low
-
-  * modutils:
-    - Fix error. (closes: #258546)
-    - Don't return failure if the module is already loaded. (closes: #257201)
-  * util-linux/umount.c:
-    - Don't ignore proc on umount -a. (closes: #257625)
-
- -- Bastian Blank <waldi@debian.org>  Wed, 28 Jul 2004 11:20:58 +0200
-
-busybox-cvs (20040623-1) unstable; urgency=low
-
-  * New CVS version.
-    - Support 64 bit arithmetic. (closes: #251302)
-
- -- Bastian Blank <waldi@debian.org>  Wed, 23 Jun 2004 21:53:52 +0200
-
-busybox-cvs (20040612-2) unstable; urgency=low
-
-  * modutils:
-    - Merge changes from modutils 2.4.26 (closes: #254214).
-
- -- Bastian Blank <waldi@debian.org>  Sun, 13 Jun 2004 21:11:08 +0200
-
-busybox-cvs (20040612-1) unstable; urgency=low
-
-  * New CVS version.
-  * modutils:
-    - Fix missdetection of module file extension.
-
- -- Bastian Blank <waldi@debian.org>  Fri, 21 May 2004 15:43:11 +0200
-
-busybox-cvs (20040507-3) unstable; urgency=low
-
-  * debian/config-udeb*:
-    - Disable udhcpc. (closes: #220652)
-    - Enable sleep.
-
- -- Bastian Blank <waldi@debian.org>  Fri, 14 May 2004 18:13:52 +0200
-
-busybox-cvs (20040507-2) unstable; urgency=low
-
-  * editors/sed.c:
-    - Never assign the return value of getopt to char. (closes: #248106)
-
- -- Bastian Blank <waldi@debian.org>  Sun, 09 May 2004 16:34:41 +0200
-
-busybox-cvs (20040507-1) unstable; urgency=low
-
-  * New CVS version
-    - Fix detection of hardlinks. (closes: #244589)
-    - Fix CAN-2003-0856.
-  * debian/config-udeb*:
-    - Enable netcat. (closes: #243508)
-    - Enable support for md5sum -c.
-  * modutils:
-    - Fix parameter passing for 2.4 modules. (closes: #245560)
-    - Add biarch support to 2.4 depmod and lsmod. (closes: #231606, #231618)
-    - Add 2.6 support to lsmod. (closes: #245580)
-
- -- Bastian Blank <waldi@debian.org>  Sat, 08 May 2004 14:58:04 +0200
-
-busybox-cvs (20040415-3) unstable; urgency=low
-
-  * modutils:
-    - Fix detection of 2.4 kernel. (closes: #244806)
-
- -- Bastian Blank <waldi@debian.org>  Tue, 20 Apr 2004 11:55:12 +0200
-
-busybox-cvs (20040415-2) unstable; urgency=low
-
-  * modutils:
-    - Make depmod quiet.
-    - Fix name of dependency files.
-
- -- Bastian Blank <waldi@debian.org>  Sun, 18 Apr 2004 20:00:51 +0200
-
-busybox-cvs (20040415-1) unstable; urgency=low
-
-  * New CVS version
-  * modutils:
-    - Merge 2.6 support into insmod and depmod.
-  * debian/config-*udeb-linux:
-    - Update.
-
- -- Bastian Blank <waldi@debian.org>  Sun, 18 Apr 2004 18:53:44 +0200
-
-busybox-cvs (20040408-1) unstable; urgency=low
-
-  * New CVS version
-    - Fix wget ftp handling. (closes: #242779)
-  * debian/config-*udeb:
-    - Disable standalone shell.
-
- -- Bastian Blank <waldi@debian.org>  Thu, 15 Apr 2004 22:40:35 +0200
-
-busybox-cvs (20040402-1) unstable; urgency=low
-
-  * New CVS version
-    - Fix llseek mess. (closes: #240918)
-  * debian/config-*:
-    - Update.
-
- -- Bastian Blank <waldi@debian.org>  Fri, 02 Apr 2004 23:49:25 +0200
-
-busybox-cvs (20040306-1) unstable; urgency=low
-
-  * New CVS version
-    - Fix directory header in ls. (closes: #231994)
-    - wget checks for empty *_proxy. (closes: #234130)
-  * debian/config-*:
-    - Update.
-
- -- Bastian Blank <waldi@debian.org>  Mon, 23 Feb 2004 13:22:38 +0100
-
-busybox-cvs (20040101-7) unstable; urgency=low
-
-  * archival/libunarchive/get_header_tar.c:
-    - Fix usage of tar typeflag field (upstream). (closes: #233627)
-  * debian/config-*:
-    - Disable oldgnu support in tar.
-
- -- Bastian Blank <waldi@debian.org>  Sat, 21 Feb 2004 12:34:40 +0100
-
-busybox-cvs (20040101-6) unstable; urgency=low
-
-  * debian/config-udeb*:
-    - Enable dd. (closes: #228248)
-  * debian/control:
-    - Set Standards-Version to 3.6.1, no changes.
-    - Remove Glenn McGrath <bug1@debian.org> from Uploaders.
-  * shell/cmdedit.c:
-    - Fix lockup (upstream). (closes: #228915)
-  * networking/wget.c:
-    - Fix proxy (upstream).
-
- -- Bastian Blank <waldi@debian.org>  Thu, 22 Jan 2004 14:25:24 +0100
-
-busybox-cvs (20040101-4) unstable; urgency=low
-
-  * Fix cp truncation bug (Closes: #227081)
-
- -- Glenn McGrath <bug1@debian.org>  Mon, 12 Jan 2004 20:51:01 +1100
-
-busybox-cvs (20040101-3) unstable; urgency=low
-
-  * debian/control:
-    - Change descriptions a little bit.
-    - Build-Depend against di-packages-build (>= 0.5).
-  * debian/rules:
-    - Install correct manpages.
-    - Mark build-* rules as notparallel.
-
- -- Bastian Blank <waldi@debian.org>  Fri, 09 Jan 2004 22:09:02 +0100
-
-busybox-cvs (20040101-2) unstable; urgency=low
-
-  * coreutils/ln.c:
-    - Fix check in symlink mode (upstream). (closes: #226722)
-
- -- Bastian Blank <waldi@debian.org>  Thu, 08 Jan 2004 12:50:27 +0100
-
-busybox-cvs (20040101-1) unstable; urgency=low
-
-  * New CVS version.
-    - Fix ln. (closes: #216435)
-    - Make insmod quiet. (closes: #215612)
-  * debiann/control:
-    - Build-Depend against di-packages-build (>= 0.4).
-  * debian/rules:
-    - Use di-packages-build.
-
- -- Bastian Blank <waldi@debian.org>  Mon, 05 Jan 2004 06:21:03 +0100
-
-busybox-cvs (20031212-3) unstable; urgency=low
-
-  * debian/config-udeb-linux:
-    - Enable freeramdisk. (closes: #225360)
-
- -- Bastian Blank <waldi@debian.org>  Tue, 30 Dec 2003 22:36:50 +0100
-
-busybox-cvs (20031212-2) unstable; urgency=low
-
-  * debian/config-udeb:
-    - Enable freeramdisk
-  * archival/libunarchive/data_extract_to_stdout.c:
-    - Don't extract to much (upstream).
-  * archival/libunarchive/data_extract_all.c:
-    - Don't set permissions on symlinks (upstream).
-  * editors/sed.c:
-    - Fix (upstream). (closes: #224676)
-
- -- Bastian Blank <waldi@debian.org>  Tue, 23 Dec 2003 16:59:13 +0100
-
-busybox-cvs (20031212-1) unstable; urgency=low
-
-  * new cvs version
-    - fixes IOR in fdisk. (closes: #223773)
-  * debian/config*
-    - update
-    - remove mtab support from mount in busybox-static. (closes: #222386)
-    - enable progress bar for wget. (closes: #223770)
-  * modutils/obj/obj_s390{,x}.c
-    - fix abi change, R_390_GOTOFF -> R_390_GOTOFF32. (closes: #216528)
-  * acknowledge nmu. (closes: #216950, #216756, #215169, #215613)
-
- -- Bastian Blank <waldi@debian.org>  Fri, 12 Dec 2003 21:19:41 +0100
-
-busybox-cvs (20030926-2.1) unstable; urgency=low
-
-  * NMU
-  * Remove /sbin/init from the udebs, while still leaving init support
-    compiled in. rootskel takes over providing init, but then calls bb init.
-    Remove linuxrc support from the udebs entirely. Closes: #216756
-  * config-floppy-udeb-linux: add minimal find, grep. Closes: #215169
-  * net-udeb-linux-i386: add loopback mount support. Closes: #215613
-
- -- Joey Hess <joeyh@debian.org>  Tue, 21 Oct 2003 12:47:52 -0400
-
-busybox-cvs (20030926-2) unstable; urgency=low
-
-  * debian/config-floppy-udeb-linux
-    - update for new floppy images (closes: #212986, #214102)
-
- -- Bastian Blank <waldi@debian.org>  Thu, 09 Oct 2003 12:25:49 +0200
-
-busybox-cvs (20030926-1) unstable; urgency=low
-
-  * new cvs version
-  * Makefile
-    - fix libpwdgrp link (upstream) (closes: #211675)
-  * Rules.mak.in
-    - fix optimization (closes: #212485)
-  * debian/config*
-    - update
-  * debian/config*udeb*
-    - move linux-i386 to linux (enable modutils on any linux arch)
-    - rename net to floppy
-    - enable wget status bar (closes: #211457)
-  * init/init.c
-    - workaround race conditions (closes: #212764)
-
- -- Bastian Blank <waldi@debian.org>  Fri, 26 Sep 2003 15:10:14 +0200
-
-busybox-cvs (0.60.99.cvs20030819-3) unstable; urgency=low
-
-  * shell/ash.c
-    - fix signal handling (upstream)
-
- -- Bastian Blank <waldi@debian.org>  Mon, 15 Sep 2003 18:12:09 +0200
-
-busybox-cvs (0.60.99.cvs20030819-2) unstable; urgency=low
-
-  * Fix configure permisions
-  * Set source Section to embedded
-
- -- Glenn McGrath <bug1@debian.org>  Mon, 25 Aug 2003 06:33:19 +0000
-
-busybox-cvs (0.60.99.cvs20030819-1) unstable; urgency=low
-
-  * new cvs version
-
- -- Bastian Blank <waldi@debian.org>  Tue, 19 Aug 2003 13:18:54 +0200
-
-busybox-cvs (0.60.99.cvs20030426-10) unstable; urgency=low
-
-  * archival/libunarchive/*
-    - add hardlink support (pending)
-  * debian/config-*udeb*
-    - add support for oldgnu tar format
-
- -- Bastian Blank <waldi@debian.org>  Tue, 10 Jun 2003 12:06:41 +0200
-
-busybox-cvs (0.60.99.cvs20030426-9) unstable; urgency=low
-
-  * modutils/depmod.c
-    - fix base_dir for modules.dep 
-
- -- Bastian Blank <waldi@debian.org>  Sat, 07 Jun 2003 14:52:54 +0200
-
-busybox-cvs (0.60.99.cvs20030426-8) unstable; urgency=low
-
-  * util-linux/fdisk.c
-    - fix syscalls. (pending)
-  * libpwd/*
-    - don't build setgroups.o
-
- -- Bastian Blank <waldi@debian.org>  Tue, 03 Jun 2003 11:26:28 +0200
-
-busybox-cvs (0.60.99.cvs20030426-7) unstable; urgency=low
-
-  * libbb/*syscallc.c
-    - fix syscalls. (upstream) (closes: #194631)
-  * modutils/*
-    - update complete objects code from upstream. (pending)
-  * debian/config-*udeb*
-    - update to busybox-applets.txt:1.14
-
- -- Bastian Blank <waldi@debian.org>  Thu, 29 May 2003 16:17:14 +0200
-
-busybox-cvs (0.60.99.cvs20030426-6) unstable; urgency=low
-
-  * modutils/Makefile.in
-    - don't build anything if deactivated
-
- -- Bastian Blank <waldi@debian.org>  Sat, 24 May 2003 18:57:14 +0200
-
-busybox-cvs (0.60.99.cvs20030426-5) unstable; urgency=low
-
-  * debian/config-*udeb*
-    - revert changes (closes: #192717, #192753)
-    - readd initrd support (workaround)
-    - make init quiet
-  * archival/tar.c
-    - fix usage of tar -O (upstream) (closes: #193788)
-
- -- Bastian Blank <waldi@debian.org>  Sat, 24 May 2003 13:25:36 +0200
-
-busybox-cvs (0.60.99.cvs20030426-4) unstable; urgency=low
-
-  * debian/config-*udeb*
-    - update to busybox-applets.txt:1.12
-  * debian/rules
-    - use system instead of arch to determine which config file to use
-
- -- Bastian Blank <waldi@debian.org>  Fri, 09 May 2003 11:21:13 +0200
-
-busybox-cvs (0.60.99.cvs20030426-3) unstable; urgency=low
-
-  * debian/config-udeb*
-    - update to busybox-applets.txt:1.11
-
- -- Bastian Blank <waldi@debian.org>  Wed, 07 May 2003 10:37:40 +0200
-
-busybox-cvs (0.60.99.cvs20030426-2) unstable; urgency=low
-
-  * modutils/*
-    - modprobe failes gracefully if the module is already loaded. (pending)
-    - implement depmod. (pending)
-  * debian/config*
-    - update
-
- -- Bastian Blank <waldi@debian.org>  Wed, 30 Apr 2003 14:12:36 +0200
-
-busybox-cvs (0.60.99.cvs20030426-1) unstable; urgency=low
-
-  * new cvs version
-
- -- Bastian Blank <waldi@debian.org>  Sat, 26 Apr 2003 18:33:17 +0200
-
-busybox-cvs (0.60.99.cvs20030420-4) unstable; urgency=low
-
-  * network/libiproute/iproute.c
-    - fix usage of ip route flush (upstream)
-
- -- Bastian Blank <waldi@debian.org>  Fri, 25 Apr 2003 16:48:43 +0200
-
-busybox-cvs (0.60.99.cvs20030420-3) unstable; urgency=low
-
-  * debian/config-*udeb*
-    - update to busybox-applets.txt:1.10
-
- -- Bastian Blank <waldi@debian.org>  Fri, 25 Apr 2003 14:03:23 +0200
-
-busybox-cvs (0.60.99.cvs20030420-2) unstable; urgency=low
-
-  * archival/tar.c, archival/libunarchive/data_extract_all.c
-    - unlink files first (upstream patch)
-
- -- Bastian Blank <waldi@debian.org>  Mon, 21 Apr 2003 12:42:06 +0200
-
-busybox-cvs (0.60.99.cvs20030420-1) unstable; urgency=low
-
-  * new cvs version
-  * debian/config*-udeb*
-    - add ifconfig/route for easier transition
-  * debian/config-net-udeb-i386
-    - add logger
-  * debian/config-udeb*
-    - add uniq
-
- -- Bastian Blank <waldi@debian.org>  Sun, 20 Apr 2003 21:02:58 +0200
-
-busybox-cvs (0.60.99.cvs20030405-1) unstable; urgency=low
-
-  * new cvs version
-  * debian/control
-    - add busybox-cvs-net-udeb
-  * debian/rules
-    - arch depend debs
-    - new net-udeb
-  * debian/config*
-    - cleanup applet list
-
- -- Bastian Blank <waldi@debian.org>  Sat, 05 Apr 2003 11:44:50 +0200
-
-busybox-cvs (0.60.99.cvs20030221-1) unstable; urgency=low
-
-  * new cvs version
-  * enable new applets in udeb
-
- -- Bastian Blank <waldi@debian.org>  Fri, 21 Feb 2003 23:15:16 +0100
-
-busybox-cvs (0.60.99.cvs20030114-1) unstable; urgency=low
-
-  * new cvs version
-
- -- Bastian Blank <waldi@debian.org>  Tue, 14 Jan 2003 17:06:43 +0000
-
-busybox-cvs (0.60.99.cvs20030105-1) unstable; urgency=low
-
-  * Fix ip command build failure on ia64 (Closes: #172580
-  * Dont build with BSD partition table support in fdisk, fails on m68k 
-
- -- Glenn McGrath <bug1@debian.org>  Sun,  5 Jan 2003 12:48:05 +1100
-
-busybox-cvs (0.60.99.cvs20030104-2) unstable; urgency=low
-
-  * floppy-retriever needs the cut command in the udeb 
-
- -- Glenn McGrath <bug1@debian.org>  Sat,  4 Jan 2003 17:13:05 +1100
-
-busybox-cvs (0.60.99.cvs20030104-1) unstable; urgency=low
-
-  * new cvs version
-  * Include new applets in the static package
-  * Include ash in the udeb
-
- -- Glenn McGrath <bug1@debian.org>  Sat,  4 Jan 2003 13:39:04 +1100
-
-busybox-cvs (0.60.99.cvs20021214-1) unstable; urgency=low
-
-  * new cvs version
-    - udhcp merge.
-  * fix location of ip link.
-
- -- Bastian Blank <waldi@debian.org>  Sat, 14 Dec 2002 13:52:15 +0100
-
-busybox-cvs (0.60.99.cvs20021210-1) unstable; urgency=low
-
-  * new cvs version
-    - various upstream fixes found in the last version.
-  * busybox-cvs-udeb
-    - include readlink and realpath.
-
- -- Bastian Blank <waldi@debian.org>  Tue, 10 Dec 2002 21:23:40 +0100
-
-busybox-cvs (0.60.99.cvs20021209-2) unstable; urgency=low
-
-  * cleanup scripts/config/ within make clean (closes: #172413)
-  * busybox-cvs-udeb
-    - include readlink
-  * include manpages within busybox-cvs and busybox-cvs-static
-
- -- Bastian Blank <waldi@debian.org>  Mon, 09 Dec 2002 22:09:52 +0100
-
-busybox-cvs (0.60.99.cvs20021209-1) unstable; urgency=low
-
-  * New cvs version.
-    - fix udhcpc for use with ip.
-    - klogd supports -c.
-  * busybox-cvs-udeb
-    - set priority to extra. (closes: #172302)
-    - don't longer provide busybox-udeb.
-
- -- Bastian Blank <waldi@debian.org>  Mon, 09 Dec 2002 16:22:03 +0100
-
-busybox-cvs (0.60.99.cvs20021203-1) unstable; urgency=low
-
-  * New packages based on busybox cvs.
-  * changes for the udeb
-    - enable ip applet with address, link and route part.
-    - enable udhcpc.
-    - disable ifconfig and route applet.
-    - disable ls color.
-
- -- Bastian Blank <waldi@debian.org>  Tue, 03 Dec 2002 18:51:00 +0100
-
-busybox (1:0.60.0-1) unstable; urgency=low
-
-  * New version released.  See changelog for details.
-
- -- Erik Andersen <andersee@debian.org>  Thu,  2 Aug 2001 12:12:37 -0600
-
-busybox (1:0.52-1.1) unstable; urgency=high
-
-  * Non-maintainer upload
-  * Fixed wget -P handling (closes: #106223).
-
- -- Matt Kraai <kraai@debian.org>  Wed, 25 Jul 2001 11:01:38 -0600
-
-busybox (1:0.52-1) unstable; urgency=high
-
-  * New version released.  See changelog for details.
-
- -- Erik Andersen <andersee@debian.org>  Sat,  7 Jul 2001 01:23:45 -0600
-
-busybox (1:0.51-10) unstable; urgency=high
-
-  * Fix a compile problem with gcc 3.0 on hppa (closes: #102045)
-
- -- Erik Andersen <andersee@debian.org>  Sat, 23 Jun 2001 23:55:57 -0600
-
-busybox (1:0.51-9) unstable; urgency=high
-
-  * tar was creating leading directories with 0777 permissions as
-    as reult of faulty umask handling.  This fixes it, repairing
-    a grave security problem in the woody the boot floppies.
-    (closes: #101169)
-
- -- Erik Andersen <andersee@debian.org>  Wed, 20 Jun 2001 16:17:38 -0600
-
-busybox (1:0.51-8) unstable; urgency=high
-
-  * Fix cp from /proc, where size=0 (closes: #100369)
-  * Add some padding to struct sysinfo for m68k.
-  * Apparently some bugs failed to be closed when master choked
-    (closes: #99627, #99637, #98571)
-  * Disable the busybox shell for the .deb, since it is not needed
-    for the boot floppies.
-
- -- Erik Andersen <andersee@debian.org>  Mon, 11 Jun 2001 13:26:07 -0600
-
-busybox (1:0.51-7) unstable; urgency=high
-
-  * Fix tar permission setting for existing directories (closes: #99627)
-  * Do not remove the .cvsignore files on 'make release' (closes: #99637)
-
- -- Erik Andersen <andersee@debian.org>  Mon,  4 Jun 2001 10:55:19 -0600
-
-busybox (1:0.51-6) testing unstable; urgency=high
-
-  * Update the version in testing so DHCP in the woody boot-floppies will work.
-  * Enable expr for the boot-floppies (closes: #98433)
-  * It builds on arm just fine now (closes: #97510)
-
- -- Erik Andersen <andersee@debian.org>  Wed, 23 May 2001 14:50:13 -0600
-
-busybox (1:0.51-5) unstable; urgency=low
-
-  * Backport a sed fix from 0.52pre
-  * Backport chroot fix from 0.52pre
-
- -- Erik Andersen <andersee@debian.org>  Wed, 16 May 2001 23:50:33 -0600
-
-busybox (1:0.51-4) unstable; urgency=low
-
-  * Backport from 0.52pre an endianness bugfix for md5sum
-  * Backport some updates to grep and sed
-  * Fix 'wget -O -' so it sets the quiet flag
-
- -- Erik Andersen <andersee@debian.org>  Mon, 14 May 2001 14:17:36 -0600
-
-busybox (1:0.51-3) unstable; urgency=low
-
-  * This is the "I am an idiot" release.
-  * Make cp and mv work again (closes: #97290) 
-  * Fix the version number.
-
- -- Erik Andersen <andersee@debian.org>  Sat, 12 May 2001 17:35:58 -0600
-
-busybox (0.51-2) unstable; urgency=low
-
-  * Backport several release critical fixes into the 0.51 codebase
-    so the boot-floppies will work again.
-  * Fix a link ordering problem. (closes: #93362)
-  * Fixed gunzip (closes: #94331)
-  * Fixed cp permission setting (closes: #94580)
-
- -- Erik Andersen <andersee@debian.org>  Sat, 12 May 2001 11:22:35 -0600
-
-busybox (0.51-1) unstable; urgency=low
-
-  * Fixes several critical bugs (see the busybox changelog
-    for complete details)
-  * Force USE_SYSTEM_PWD_GRP=false, so busybox bypasses
-    the glibc NSS libraries. (closes: #93362)
-  * Fixed a bug in sed's address range handling (closes: #91758) 
-  * Removed irrelevant cruft from the bottem of debian/changelog
-
- -- Erik Andersen <andersee@debian.org>  Tue, 10 Apr 2001 14:07:29 -0600
-
-busybox (0.50-2) unstable; urgency=low
-  
-  * Enabled freeramdisk and pivot_root in the udeb (closes: #91336)
-  * Disabled lash (the busybox shell) in the udeb (closes: #91337)
-  * fixed a bug in syslog, a problem with rebooting when booted as
-    an initrd, and a few other minor problems.
-
- -- Erik Andersen <andersee@debian.org>  Sun, 25 Mar 2001 20:59:44 -0700
-
-
-busybox (0.50-2) unstable; urgency=low
-  
-  * Enabled freeramdisk and pivot_root in the udeb (closes: #91336)
-  * Disabled lash (the busybox shell) in the udeb (closes: #91337)
-  * fixed a bug in syslog, a problem with rebooting when booted as
-    an initrd, and a few other minor problems.
-
- -- Erik Andersen <andersee@debian.org>  Sun, 25 Mar 2001 20:59:44 -0700
-
-busybox (0.50-1) unstable; urgency=low
-
-  * Tons on improvements all around -- See changelog for details.
-  * Fix malformed Build-Depends (closes: #86930)
-  * grep has worked for some time now (closes: #81084)
-  * init compiles with DEBUG_INIT enabled (closes: #85794)
-  * md5sum no longer displays filename when reading stdin (closes: #81283)
-  * lsmod, rmmod, and insmod are no longer enabled (closes: #85642)
-  * busybox and buxybox-static now conflict/replace each other (closes: #80421)
-
- -- Erik Andersen <andersee@debian.org>  Thu, 15 Mar 2001 14:45:00 -0700
-
-busybox (0.49-1) unstable; urgency=low
-
-  * Lots more source updates and bug fixes.  See changelog for details.
-
- -- Erik Andersen <andersee@debian.org>  Sat, 27 Jan 2001 01:45:53 -0700
-
-busybox (0.48-1) unstable; urgency=low
-
-  * Lots more source updates and bug fixes.  See changelog for details.
-  * Now includes .udeb support for the debian-installer.  The .udeb 
-    probably needs some more work, but this should be a good start.
-
- -- Erik Andersen <andersee@debian.org>  Wed, 13 Dec 2000 08:36:07 -0700
-
-busybox (0.47-1) unstable; urgency=low
-
-  * New version released.  See changelog for details.
-
- -- Erik Andersen <andersee@debian.org>  Mon, 25 Sep 2000 23:00:56 -0600
-
-busybox (0.46-1) unstable; urgency=low
-
-  * New version released.  See changelog for details.
-
- -- Erik Andersen <andersee@debian.org>  Tue, 11 Jul 2000 12:15:44 -0600
-
-busybox (0.45-1) unstable; urgency=low
-
-  * First attempt at packaging BusyBox as a .deb.  This has been in 
-    in the Debian boot-floppies CVS tree forever.  Hopefully, having it as a
-    standalone app will make life easier for me, the debian-installer team, and
-    everyone else as well...
-  * I have created a busybox-static that can be used as a rescue shell when you 
-    hose your system.  Just invoke "busybox sh" to fir up the shell.  This has
-    every app provided by busybox staically linked in.  There have been several
-    times in the past that I would have loved to have this sitting on my system
-    (i.e. when libc gets screwed up.)
-
- -- Erik Andersen <andersee@debian.org>  Tue, 27 Jun 2000 12:26:41 -0600
-
diff --git a/debian/compat b/debian/compat
deleted file mode 100644 (file)
index 7ed6ff8..0000000
+++ /dev/null
@@ -1 +0,0 @@
-5
diff --git a/debian/config/config.maemo b/debian/config/config.maemo
deleted file mode 100644 (file)
index 0535cbd..0000000
+++ /dev/null
@@ -1,825 +0,0 @@
-#
-# Automatically generated make config: don't edit
-# Busybox version: 1.10.2
-# Thu Jul 24 12:28:58 2008
-#
-CONFIG_HAVE_DOT_CONFIG=y
-
-#
-# Busybox Settings
-#
-
-#
-# General Configuration
-#
-# CONFIG_NITPICK is not set
-# CONFIG_DESKTOP is not set
-# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
-# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
-# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
-CONFIG_SHOW_USAGE=y
-# CONFIG_FEATURE_VERBOSE_USAGE is not set
-CONFIG_FEATURE_COMPRESS_USAGE=y
-# CONFIG_FEATURE_INSTALLER is not set
-# CONFIG_LOCALE_SUPPORT is not set
-CONFIG_GETOPT_LONG=y
-CONFIG_FEATURE_DEVPTS=y
-# CONFIG_FEATURE_CLEAN_UP is not set
-# CONFIG_FEATURE_PIDFILE is not set
-CONFIG_FEATURE_SUID=y
-# CONFIG_FEATURE_SUID_CONFIG is not set
-# CONFIG_FEATURE_SUID_CONFIG_QUIET is not set
-# CONFIG_SELINUX is not set
-# CONFIG_FEATURE_APPLETS_FALLBACK is not set
-CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
-CONFIG_FEATURE_SYSLOG=y
-CONFIG_FEATURE_HAVE_RPC=y
-
-#
-# Build Options
-#
-# CONFIG_STATIC is not set
-# CONFIG_NOMMU is not set
-# CONFIG_BUILD_LIBBUSYBOX is not set
-# CONFIG_FEATURE_INDIVIDUAL is not set
-# CONFIG_FEATURE_SHARED_BUSYBOX is not set
-CONFIG_LFS=y
-
-#
-# Debugging Options
-#
-CONFIG_DEBUG=y
-# CONFIG_WERROR is not set
-CONFIG_NO_DEBUG_LIB=y
-# CONFIG_DMALLOC is not set
-# CONFIG_EFENCE is not set
-CONFIG_INCLUDE_SUSv2=y
-
-#
-# Installation Options
-#
-# CONFIG_INSTALL_NO_USR is not set
-CONFIG_INSTALL_APPLET_SYMLINKS=y
-# CONFIG_INSTALL_APPLET_HARDLINKS is not set
-# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
-# CONFIG_INSTALL_APPLET_DONT is not set
-# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
-# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
-# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
-CONFIG_PREFIX="./_install"
-
-#
-# Busybox Library Tuning
-#
-CONFIG_PASSWORD_MINLEN=6
-CONFIG_MD5_SIZE_VS_SPEED=0
-CONFIG_FEATURE_FAST_TOP=y
-# CONFIG_FEATURE_ETC_NETWORKS is not set
-CONFIG_FEATURE_EDITING=y
-CONFIG_FEATURE_EDITING_MAX_LEN=1024
-# CONFIG_FEATURE_EDITING_VI is not set
-CONFIG_FEATURE_EDITING_HISTORY=15
-CONFIG_FEATURE_EDITING_SAVEHISTORY=y
-CONFIG_FEATURE_TAB_COMPLETION=y
-# CONFIG_FEATURE_USERNAME_COMPLETION is not set
-CONFIG_FEATURE_EDITING_FANCY_PROMPT=y
-CONFIG_FEATURE_VERBOSE_CP_MESSAGE=y
-CONFIG_FEATURE_COPYBUF_KB=4
-CONFIG_MONOTONIC_SYSCALL=y
-CONFIG_IOCTL_HEX2STR_ERROR=y
-
-#
-# Applets
-#
-
-#
-# Archival Utilities
-#
-# CONFIG_AR is not set
-# CONFIG_FEATURE_AR_LONG_FILENAMES is not set
-# CONFIG_BUNZIP2 is not set
-# CONFIG_BZIP2 is not set
-# CONFIG_CPIO is not set
-# CONFIG_DPKG is not set
-# CONFIG_DPKG_DEB is not set
-# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
-CONFIG_GUNZIP=y
-# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set
-CONFIG_GZIP=y
-# CONFIG_RPM2CPIO is not set
-# CONFIG_RPM is not set
-# CONFIG_FEATURE_RPM_BZ2 is not set
-CONFIG_TAR=y
-CONFIG_FEATURE_TAR_CREATE=y
-CONFIG_FEATURE_TAR_GZIP=y
-# CONFIG_FEATURE_TAR_BZIP2 is not set
-# CONFIG_FEATURE_TAR_LZMA is not set
-# CONFIG_FEATURE_TAR_COMPRESS is not set
-# CONFIG_FEATURE_TAR_AUTODETECT is not set
-CONFIG_FEATURE_TAR_FROM=y
-CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y
-CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y
-CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
-CONFIG_FEATURE_TAR_LONG_OPTIONS=y
-# CONFIG_FEATURE_TAR_UNAME_GNAME is not set
-CONFIG_FEATURE_TAR_TOUCH=y
-CONFIG_UNCOMPRESS=y
-# CONFIG_UNLZMA is not set
-# CONFIG_FEATURE_LZMA_FAST is not set
-# CONFIG_UNZIP is not set
-
-#
-# Common options for cpio and tar
-#
-# CONFIG_FEATURE_UNARCHIVE_TAPE is not set
-# CONFIG_FEATURE_DEB_TAR_GZ is not set
-# CONFIG_FEATURE_DEB_TAR_BZ2 is not set
-# CONFIG_FEATURE_DEB_TAR_LZMA is not set
-
-#
-# Coreutils
-#
-CONFIG_BASENAME=y
-# CONFIG_CAL is not set
-CONFIG_CAT=y
-# CONFIG_CATV is not set
-CONFIG_CHGRP=y
-CONFIG_CHMOD=y
-CONFIG_CHOWN=y
-CONFIG_CHROOT=y
-CONFIG_CKSUM=y
-CONFIG_COMM=y
-CONFIG_CP=y
-CONFIG_CUT=y
-CONFIG_DATE=y
-CONFIG_FEATURE_DATE_ISOFMT=y
-CONFIG_DD=y
-CONFIG_FEATURE_DD_SIGNAL_HANDLING=y
-# CONFIG_FEATURE_DD_IBS_OBS is not set
-CONFIG_DF=y
-# CONFIG_FEATURE_DF_INODE is not set
-CONFIG_DIRNAME=y
-# CONFIG_DOS2UNIX is not set
-# CONFIG_UNIX2DOS is not set
-CONFIG_DU=y
-CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y
-CONFIG_ECHO=y
-CONFIG_FEATURE_FANCY_ECHO=y
-CONFIG_ENV=y
-# CONFIG_FEATURE_ENV_LONG_OPTIONS is not set
-CONFIG_EXPAND=y
-# CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set
-CONFIG_EXPR=y
-# CONFIG_EXPR_MATH_SUPPORT_64 is not set
-CONFIG_FALSE=y
-CONFIG_FOLD=y
-CONFIG_FSYNC=y
-CONFIG_HEAD=y
-# CONFIG_FEATURE_FANCY_HEAD is not set
-CONFIG_HOSTID=y
-CONFIG_ID=y
-CONFIG_INSTALL=y
-# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set
-# CONFIG_LENGTH is not set
-CONFIG_LN=y
-CONFIG_LOGNAME=y
-CONFIG_LS=y
-# CONFIG_FEATURE_LS_FILETYPES is not set
-CONFIG_FEATURE_LS_FOLLOWLINKS=y
-CONFIG_FEATURE_LS_RECURSIVE=y
-CONFIG_FEATURE_LS_SORTFILES=y
-CONFIG_FEATURE_LS_TIMESTAMPS=y
-CONFIG_FEATURE_LS_USERNAME=y
-# CONFIG_FEATURE_LS_COLOR is not set
-# CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set
-CONFIG_MD5SUM=y
-CONFIG_MKDIR=y
-CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y
-CONFIG_MKFIFO=y
-CONFIG_MKNOD=y
-CONFIG_MV=y
-CONFIG_FEATURE_MV_LONG_OPTIONS=y
-CONFIG_NICE=y
-CONFIG_NOHUP=y
-CONFIG_OD=y
-CONFIG_PRINTENV=y
-CONFIG_PRINTF=y
-CONFIG_PWD=y
-CONFIG_READLINK=y
-CONFIG_FEATURE_READLINK_FOLLOW=y
-CONFIG_REALPATH=y
-CONFIG_RM=y
-CONFIG_RMDIR=y
-# CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set
-CONFIG_SEQ=y
-CONFIG_SHA1SUM=y
-CONFIG_SLEEP=y
-# CONFIG_FEATURE_FANCY_SLEEP is not set
-CONFIG_SORT=y
-CONFIG_FEATURE_SORT_BIG=y
-CONFIG_SPLIT=y
-# CONFIG_FEATURE_SPLIT_FANCY is not set
-CONFIG_STAT=y
-# CONFIG_FEATURE_STAT_FORMAT is not set
-CONFIG_STTY=y
-CONFIG_SUM=y
-CONFIG_SYNC=y
-CONFIG_TAC=y
-CONFIG_TAIL=y
-CONFIG_FEATURE_FANCY_TAIL=y
-CONFIG_TEE=y
-# CONFIG_FEATURE_TEE_USE_BLOCK_IO is not set
-CONFIG_TEST=y
-CONFIG_FEATURE_TEST_64=y
-CONFIG_TOUCH=y
-CONFIG_TR=y
-# CONFIG_FEATURE_TR_CLASSES is not set
-# CONFIG_FEATURE_TR_EQUIV is not set
-CONFIG_TRUE=y
-CONFIG_TTY=y
-CONFIG_UNAME=y
-CONFIG_UNEXPAND=y
-# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set
-CONFIG_UNIQ=y
-# CONFIG_USLEEP is not set
-# CONFIG_UUDECODE is not set
-# CONFIG_UUENCODE is not set
-CONFIG_WC=y
-# CONFIG_FEATURE_WC_LARGE is not set
-CONFIG_WHO=y
-CONFIG_WHOAMI=y
-CONFIG_YES=y
-
-#
-# Common options for cp and mv
-#
-# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set
-
-#
-# Common options for ls, more and telnet
-#
-CONFIG_FEATURE_AUTOWIDTH=y
-
-#
-# Common options for df, du, ls
-#
-CONFIG_FEATURE_HUMAN_READABLE=y
-CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
-
-#
-# Console Utilities
-#
-CONFIG_CHVT=y
-CONFIG_CLEAR=y
-CONFIG_DEALLOCVT=y
-# CONFIG_DUMPKMAP is not set
-CONFIG_KBD_MODE=y
-# CONFIG_LOADFONT is not set
-# CONFIG_LOADKMAP is not set
-CONFIG_OPENVT=y
-CONFIG_RESET=y
-# CONFIG_RESIZE is not set
-# CONFIG_FEATURE_RESIZE_PRINT is not set
-# CONFIG_SETCONSOLE is not set
-# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
-CONFIG_SETKEYCODES=y
-CONFIG_SETLOGCONS=y
-
-#
-# Debian Utilities
-#
-CONFIG_MKTEMP=y
-# CONFIG_PIPE_PROGRESS is not set
-CONFIG_RUN_PARTS=y
-CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y
-CONFIG_FEATURE_RUN_PARTS_FANCY=y
-# CONFIG_START_STOP_DAEMON is not set
-# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set
-# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set
-CONFIG_WHICH=y
-
-#
-# Editors
-#
-CONFIG_AWK=y
-# CONFIG_FEATURE_AWK_MATH is not set
-CONFIG_CMP=y
-# CONFIG_DIFF is not set
-# CONFIG_FEATURE_DIFF_BINARY is not set
-# CONFIG_FEATURE_DIFF_DIR is not set
-# CONFIG_FEATURE_DIFF_MINIMAL is not set
-# CONFIG_ED is not set
-# CONFIG_PATCH is not set
-CONFIG_SED=y
-CONFIG_VI=y
-CONFIG_FEATURE_VI_MAX_LEN=4096
-CONFIG_FEATURE_VI_8BIT=y
-CONFIG_FEATURE_VI_COLON=y
-CONFIG_FEATURE_VI_YANKMARK=y
-CONFIG_FEATURE_VI_SEARCH=y
-CONFIG_FEATURE_VI_USE_SIGNALS=y
-CONFIG_FEATURE_VI_DOT_CMD=y
-# CONFIG_FEATURE_VI_READONLY is not set
-# CONFIG_FEATURE_VI_SETOPTS is not set
-# CONFIG_FEATURE_VI_SET is not set
-CONFIG_FEATURE_VI_WIN_RESIZE=y
-CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y
-CONFIG_FEATURE_ALLOW_EXEC=y
-
-#
-# Finding Utilities
-#
-CONFIG_FIND=y
-CONFIG_FEATURE_FIND_PRINT0=y
-CONFIG_FEATURE_FIND_MTIME=y
-# CONFIG_FEATURE_FIND_MMIN is not set
-CONFIG_FEATURE_FIND_PERM=y
-CONFIG_FEATURE_FIND_TYPE=y
-CONFIG_FEATURE_FIND_XDEV=y
-CONFIG_FEATURE_FIND_MAXDEPTH=y
-CONFIG_FEATURE_FIND_NEWER=y
-CONFIG_FEATURE_FIND_INUM=y
-# CONFIG_FEATURE_FIND_EXEC is not set
-CONFIG_FEATURE_FIND_USER=y
-CONFIG_FEATURE_FIND_GROUP=y
-CONFIG_FEATURE_FIND_NOT=y
-CONFIG_FEATURE_FIND_DEPTH=y
-CONFIG_FEATURE_FIND_PAREN=y
-CONFIG_FEATURE_FIND_SIZE=y
-CONFIG_FEATURE_FIND_PRUNE=y
-# CONFIG_FEATURE_FIND_DELETE is not set
-CONFIG_FEATURE_FIND_PATH=y
-CONFIG_FEATURE_FIND_REGEX=y
-# CONFIG_FEATURE_FIND_CONTEXT is not set
-CONFIG_GREP=y
-CONFIG_FEATURE_GREP_EGREP_ALIAS=y
-CONFIG_FEATURE_GREP_FGREP_ALIAS=y
-CONFIG_FEATURE_GREP_CONTEXT=y
-CONFIG_XARGS=y
-# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set
-CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
-# CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT is not set
-# CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM is not set
-
-#
-# Init Utilities
-#
-# CONFIG_INIT is not set
-CONFIG_CALL_TELINIT=y
-CONFIG_TELINIT_PATH="/sbin/telinit"
-# CONFIG_DEBUG_INIT is not set
-# CONFIG_FEATURE_USE_INITTAB is not set
-# CONFIG_FEATURE_KILL_REMOVED is not set
-CONFIG_FEATURE_KILL_DELAY=0
-# CONFIG_FEATURE_INIT_SCTTY is not set
-# CONFIG_FEATURE_INIT_SYSLOG is not set
-# CONFIG_FEATURE_EXTRA_QUIET is not set
-# CONFIG_FEATURE_INIT_COREDUMPS is not set
-# CONFIG_FEATURE_INITRD is not set
-CONFIG_HALT=y
-CONFIG_MESG=y
-
-#
-# Login/Password Management Utilities
-#
-# CONFIG_FEATURE_SHADOWPASSWDS is not set
-# CONFIG_USE_BB_SHADOW is not set
-# CONFIG_USE_BB_PWD_GRP is not set
-# CONFIG_ADDGROUP is not set
-# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
-# CONFIG_DELGROUP is not set
-# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
-# CONFIG_FEATURE_CHECK_NAMES is not set
-# CONFIG_ADDUSER is not set
-# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set
-# CONFIG_DELUSER is not set
-CONFIG_GETTY=y
-CONFIG_FEATURE_UTMP=y
-# CONFIG_FEATURE_WTMP is not set
-CONFIG_LOGIN=y
-# CONFIG_PAM is not set
-# CONFIG_LOGIN_SCRIPTS is not set
-CONFIG_FEATURE_NOLOGIN=y
-CONFIG_FEATURE_SECURETTY=y
-# CONFIG_PASSWD is not set
-# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set
-# CONFIG_CRYPTPW is not set
-# CONFIG_CHPASSWD is not set
-CONFIG_SU=y
-CONFIG_FEATURE_SU_SYSLOG=y
-# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set
-# CONFIG_SULOGIN is not set
-# CONFIG_VLOCK is not set
-
-#
-# Linux Ext2 FS Progs
-#
-# CONFIG_CHATTR is not set
-# CONFIG_FSCK is not set
-# CONFIG_LSATTR is not set
-
-#
-# Linux Module Utilities
-#
-# CONFIG_INSMOD is not set
-# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
-# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
-# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
-# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
-# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set
-# CONFIG_RMMOD is not set
-# CONFIG_LSMOD is not set
-# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set
-# CONFIG_MODPROBE is not set
-# CONFIG_FEATURE_MODPROBE_MULTIPLE_OPTIONS is not set
-# CONFIG_FEATURE_MODPROBE_FANCY_ALIAS is not set
-# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
-# CONFIG_FEATURE_2_4_MODULES is not set
-# CONFIG_FEATURE_2_6_MODULES is not set
-# CONFIG_FEATURE_QUERY_MODULE_INTERFACE is not set
-
-#
-# Linux System Utilities
-#
-CONFIG_DMESG=y
-CONFIG_FEATURE_DMESG_PRETTY=y
-# CONFIG_FBSET is not set
-# CONFIG_FEATURE_FBSET_FANCY is not set
-# CONFIG_FEATURE_FBSET_READMODE is not set
-# CONFIG_FDFLUSH is not set
-# CONFIG_FDFORMAT is not set
-# CONFIG_FDISK is not set
-# CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set
-# CONFIG_FEATURE_FDISK_WRITABLE is not set
-# CONFIG_FEATURE_AIX_LABEL is not set
-# CONFIG_FEATURE_SGI_LABEL is not set
-# CONFIG_FEATURE_SUN_LABEL is not set
-# CONFIG_FEATURE_OSF_LABEL is not set
-# CONFIG_FEATURE_FDISK_ADVANCED is not set
-# CONFIG_FINDFS is not set
-# CONFIG_FREERAMDISK is not set
-# CONFIG_FSCK_MINIX is not set
-# CONFIG_MKFS_MINIX is not set
-# CONFIG_FEATURE_MINIX2 is not set
-CONFIG_GETOPT=y
-# CONFIG_HEXDUMP is not set
-# CONFIG_FEATURE_HEXDUMP_REVERSE is not set
-# CONFIG_HD is not set
-CONFIG_HWCLOCK=y
-# CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set
-# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set
-CONFIG_IPCRM=y
-CONFIG_IPCS=y
-CONFIG_LOSETUP=y
-# CONFIG_MDEV is not set
-# CONFIG_FEATURE_MDEV_CONF is not set
-# CONFIG_FEATURE_MDEV_RENAME is not set
-# CONFIG_FEATURE_MDEV_EXEC is not set
-# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set
-CONFIG_MKSWAP=y
-# CONFIG_FEATURE_MKSWAP_V0 is not set
-CONFIG_MORE=y
-CONFIG_FEATURE_USE_TERMIOS=y
-# CONFIG_VOLUMEID is not set
-# CONFIG_FEATURE_VOLUMEID_EXT is not set
-# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
-# CONFIG_FEATURE_VOLUMEID_FAT is not set
-# CONFIG_FEATURE_VOLUMEID_HFS is not set
-# CONFIG_FEATURE_VOLUMEID_JFS is not set
-# CONFIG_FEATURE_VOLUMEID_XFS is not set
-# CONFIG_FEATURE_VOLUMEID_NTFS is not set
-# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set
-# CONFIG_FEATURE_VOLUMEID_UDF is not set
-# CONFIG_FEATURE_VOLUMEID_LUKS is not set
-# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set
-# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set
-# CONFIG_FEATURE_VOLUMEID_ROMFS is not set
-# CONFIG_FEATURE_VOLUMEID_SYSV is not set
-# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set
-# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set
-CONFIG_MOUNT=y
-CONFIG_FEATURE_MOUNT_FAKE=y
-# CONFIG_FEATURE_MOUNT_VERBOSE is not set
-CONFIG_FEATURE_MOUNT_HELPERS=y
-# CONFIG_FEATURE_MOUNT_LABEL is not set
-CONFIG_FEATURE_MOUNT_NFS=y
-# CONFIG_FEATURE_MOUNT_CIFS is not set
-CONFIG_FEATURE_MOUNT_FLAGS=y
-CONFIG_FEATURE_MOUNT_FSTAB=y
-CONFIG_PIVOT_ROOT=y
-# CONFIG_RDATE is not set
-# CONFIG_READPROFILE is not set
-# CONFIG_RTCWAKE is not set
-# CONFIG_SETARCH is not set
-CONFIG_SWAPONOFF=y
-# CONFIG_SWITCH_ROOT is not set
-CONFIG_UMOUNT=y
-# CONFIG_FEATURE_UMOUNT_ALL is not set
-
-#
-# Common options for mount/umount
-#
-CONFIG_FEATURE_MOUNT_LOOP=y
-CONFIG_FEATURE_MTAB_SUPPORT=y
-
-#
-# Miscellaneous Utilities
-#
-# CONFIG_ADJTIMEX is not set
-# CONFIG_BBCONFIG is not set
-# CONFIG_CHAT is not set
-# CONFIG_FEATURE_CHAT_NOFAIL is not set
-# CONFIG_FEATURE_CHAT_TTY_HIFI is not set
-# CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set
-# CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set
-# CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set
-# CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set
-# CONFIG_FEATURE_CHAT_CLR_ABORT is not set
-# CONFIG_CHRT is not set
-# CONFIG_CROND is not set
-# CONFIG_DEBUG_CROND_OPTION is not set
-# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
-# CONFIG_CRONTAB is not set
-# CONFIG_DC is not set
-# CONFIG_DEVFSD is not set
-# CONFIG_DEVFSD_MODLOAD is not set
-# CONFIG_DEVFSD_FG_NP is not set
-# CONFIG_DEVFSD_VERBOSE is not set
-# CONFIG_FEATURE_DEVFS is not set
-# CONFIG_EJECT is not set
-# CONFIG_FEATURE_EJECT_SCSI is not set
-# CONFIG_LAST is not set
-# CONFIG_LESS is not set
-CONFIG_FEATURE_LESS_MAXLINES=
-# CONFIG_FEATURE_LESS_BRACKETS is not set
-# CONFIG_FEATURE_LESS_FLAGS is not set
-# CONFIG_FEATURE_LESS_FLAGCS is not set
-# CONFIG_FEATURE_LESS_MARKS is not set
-# CONFIG_FEATURE_LESS_REGEXP is not set
-# CONFIG_HDPARM is not set
-# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set
-# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set
-# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set
-# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set
-# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set
-# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set
-# CONFIG_MAKEDEVS is not set
-# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
-# CONFIG_FEATURE_MAKEDEVS_TABLE is not set
-# CONFIG_MICROCOM is not set
-# CONFIG_MOUNTPOINT is not set
-# CONFIG_MT is not set
-# CONFIG_RAIDAUTORUN is not set
-# CONFIG_READAHEAD is not set
-# CONFIG_RUNLEVEL is not set
-# CONFIG_RX is not set
-# CONFIG_SCRIPT is not set
-# CONFIG_STRINGS is not set
-CONFIG_SETSID=y
-# CONFIG_TASKSET is not set
-# CONFIG_FEATURE_TASKSET_FANCY is not set
-CONFIG_TIME=y
-# CONFIG_TTYSIZE is not set
-# CONFIG_WATCHDOG is not set
-
-#
-# Networking Utilities
-#
-CONFIG_FEATURE_IPV6=y
-# CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set
-# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
-# CONFIG_ARP is not set
-# CONFIG_ARPING is not set
-# CONFIG_BRCTL is not set
-# CONFIG_FEATURE_BRCTL_FANCY is not set
-# CONFIG_DNSD is not set
-# CONFIG_ETHER_WAKE is not set
-# CONFIG_FAKEIDENTD is not set
-# CONFIG_FTPGET is not set
-# CONFIG_FTPPUT is not set
-# CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set
-CONFIG_HOSTNAME=y
-# CONFIG_HTTPD is not set
-# CONFIG_FEATURE_HTTPD_RANGES is not set
-# CONFIG_FEATURE_HTTPD_USE_SENDFILE is not set
-# CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP is not set
-# CONFIG_FEATURE_HTTPD_SETUID is not set
-# CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set
-# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
-# CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES is not set
-# CONFIG_FEATURE_HTTPD_CGI is not set
-# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
-# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
-# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set
-# CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set
-# CONFIG_FEATURE_HTTPD_PROXY is not set
-CONFIG_IFCONFIG=y
-CONFIG_FEATURE_IFCONFIG_STATUS=y
-# CONFIG_FEATURE_IFCONFIG_SLIP is not set
-# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
-CONFIG_FEATURE_IFCONFIG_HW=y
-# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
-# CONFIG_IFENSLAVE is not set
-CONFIG_IFUPDOWN=y
-CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate"
-# CONFIG_FEATURE_IFUPDOWN_IP is not set
-# CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN is not set
-CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN=y
-CONFIG_FEATURE_IFUPDOWN_IPV4=y
-# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set
-# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set
-# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set
-# CONFIG_INETD is not set
-# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set
-# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set
-# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set
-# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set
-# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set
-# CONFIG_FEATURE_INETD_RPC is not set
-# CONFIG_IP is not set
-# CONFIG_FEATURE_IP_ADDRESS is not set
-# CONFIG_FEATURE_IP_LINK is not set
-# CONFIG_FEATURE_IP_ROUTE is not set
-# CONFIG_FEATURE_IP_TUNNEL is not set
-# CONFIG_FEATURE_IP_RULE is not set
-# CONFIG_FEATURE_IP_SHORT_FORMS is not set
-# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
-# CONFIG_IPADDR is not set
-# CONFIG_IPLINK is not set
-# CONFIG_IPROUTE is not set
-# CONFIG_IPTUNNEL is not set
-# CONFIG_IPRULE is not set
-# CONFIG_IPCALC is not set
-# CONFIG_FEATURE_IPCALC_FANCY is not set
-# CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set
-CONFIG_NAMEIF=y
-# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
-# CONFIG_NC is not set
-# CONFIG_NC_SERVER is not set
-# CONFIG_NC_EXTRA is not set
-CONFIG_NETSTAT=y
-# CONFIG_FEATURE_NETSTAT_WIDE is not set
-CONFIG_NSLOOKUP=y
-CONFIG_PING=y
-CONFIG_PING6=y
-CONFIG_FEATURE_FANCY_PING=y
-# CONFIG_PSCAN is not set
-CONFIG_ROUTE=y
-# CONFIG_SENDMAIL is not set
-# CONFIG_FETCHMAIL is not set
-CONFIG_SLATTACH=y
-# CONFIG_TELNET is not set
-# CONFIG_FEATURE_TELNET_TTYPE is not set
-# CONFIG_FEATURE_TELNET_AUTOLOGIN is not set
-# CONFIG_TELNETD is not set
-# CONFIG_FEATURE_TELNETD_STANDALONE is not set
-# CONFIG_TFTP is not set
-# CONFIG_TFTPD is not set
-# CONFIG_FEATURE_TFTP_GET is not set
-# CONFIG_FEATURE_TFTP_PUT is not set
-# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set
-# CONFIG_DEBUG_TFTP is not set
-# CONFIG_TRACEROUTE is not set
-# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
-# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
-# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
-# CONFIG_APP_UDHCPD is not set
-# CONFIG_APP_DHCPRELAY is not set
-# CONFIG_APP_DUMPLEASES is not set
-# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
-CONFIG_DHCPD_LEASES_FILE=""
-# CONFIG_APP_UDHCPC is not set
-# CONFIG_FEATURE_UDHCPC_ARPING is not set
-# CONFIG_FEATURE_UDHCP_PORT is not set
-# CONFIG_FEATURE_UDHCP_DEBUG is not set
-# CONFIG_FEATURE_RFC3397 is not set
-CONFIG_DHCPC_DEFAULT_SCRIPT=""
-CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=
-# CONFIG_VCONFIG is not set
-# CONFIG_WGET is not set
-# CONFIG_FEATURE_WGET_STATUSBAR is not set
-# CONFIG_FEATURE_WGET_AUTHENTICATION is not set
-# CONFIG_FEATURE_WGET_LONG_OPTIONS is not set
-# CONFIG_ZCIP is not set
-# CONFIG_TCPSVD is not set
-# CONFIG_UDPSVD is not set
-
-#
-# Process Utilities
-#
-CONFIG_FREE=y
-CONFIG_FUSER=y
-CONFIG_KILL=y
-CONFIG_KILLALL=y
-CONFIG_KILLALL5=y
-# CONFIG_NMETER is not set
-CONFIG_PGREP=y
-CONFIG_PIDOF=y
-CONFIG_FEATURE_PIDOF_SINGLE=y
-CONFIG_FEATURE_PIDOF_OMIT=y
-CONFIG_PKILL=y
-CONFIG_PS=y
-# CONFIG_FEATURE_PS_WIDE is not set
-# CONFIG_FEATURE_PS_TIME is not set
-# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
-CONFIG_RENICE=y
-CONFIG_BB_SYSCTL=y
-CONFIG_TOP=y
-CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y
-CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y
-CONFIG_FEATURE_TOP_DECIMALS=y
-CONFIG_FEATURE_TOPMEM=y
-CONFIG_UPTIME=y
-CONFIG_WATCH=y
-
-#
-# Shells
-#
-CONFIG_FEATURE_SH_IS_ASH=y
-# CONFIG_FEATURE_SH_IS_HUSH is not set
-# CONFIG_FEATURE_SH_IS_MSH is not set
-# CONFIG_FEATURE_SH_IS_NONE is not set
-CONFIG_ASH=y
-
-#
-# Ash Shell Options
-#
-CONFIG_ASH_JOB_CONTROL=y
-# CONFIG_ASH_READ_NCHARS is not set
-CONFIG_ASH_READ_TIMEOUT=y
-CONFIG_ASH_ALIAS=y
-CONFIG_ASH_MATH_SUPPORT=y
-# CONFIG_ASH_MATH_SUPPORT_64 is not set
-# CONFIG_ASH_GETOPTS is not set
-CONFIG_ASH_BUILTIN_ECHO=y
-CONFIG_ASH_BUILTIN_TEST=y
-CONFIG_ASH_CMDCMD=y
-# CONFIG_ASH_MAIL is not set
-CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
-# CONFIG_ASH_RANDOM_SUPPORT is not set
-# CONFIG_ASH_EXPAND_PRMT is not set
-# CONFIG_HUSH is not set
-# CONFIG_HUSH_HELP is not set
-# CONFIG_HUSH_INTERACTIVE is not set
-# CONFIG_HUSH_JOB is not set
-# CONFIG_HUSH_TICK is not set
-# CONFIG_HUSH_IF is not set
-# CONFIG_HUSH_LOOPS is not set
-# CONFIG_LASH is not set
-# CONFIG_MSH is not set
-
-#
-# Bourne Shell Options
-#
-# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
-# CONFIG_FEATURE_SH_STANDALONE is not set
-# CONFIG_CTTYHACK is not set
-
-#
-# System Logging Utilities
-#
-# CONFIG_SYSLOGD is not set
-# CONFIG_FEATURE_ROTATE_LOGFILE is not set
-# CONFIG_FEATURE_REMOTE_LOG is not set
-# CONFIG_FEATURE_SYSLOGD_DUP is not set
-# CONFIG_FEATURE_IPC_SYSLOG is not set
-CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=
-# CONFIG_LOGREAD is not set
-# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
-# CONFIG_KLOGD is not set
-CONFIG_LOGGER=y
-
-#
-# Runit Utilities
-#
-# CONFIG_RUNSV is not set
-# CONFIG_RUNSVDIR is not set
-# CONFIG_SV is not set
-# CONFIG_SVLOGD is not set
-# CONFIG_CHPST is not set
-# CONFIG_SETUIDGID is not set
-# CONFIG_ENVUIDGID is not set
-# CONFIG_ENVDIR is not set
-# CONFIG_SOFTLIMIT is not set
-# CONFIG_CHCON is not set
-# CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set
-# CONFIG_GETENFORCE is not set
-# CONFIG_GETSEBOOL is not set
-# CONFIG_LOAD_POLICY is not set
-# CONFIG_MATCHPATHCON is not set
-# CONFIG_RESTORECON is not set
-# CONFIG_RUNCON is not set
-# CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set
-# CONFIG_SELINUXENABLED is not set
-# CONFIG_SETENFORCE is not set
-# CONFIG_SETFILES is not set
-# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set
-# CONFIG_SETSEBOOL is not set
-# CONFIG_SESTATUS is not set
-
-#
-# Print Utilities
-#
-# CONFIG_LPD is not set
-# CONFIG_LPR is not set
-# CONFIG_LPQ is not set
diff --git a/debian/config/os/hurd b/debian/config/os/hurd
deleted file mode 100644 (file)
index 827a002..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-CONFIG_PLATFORM_LINUX=n
-CONFIG_INIT_TERMINAL_TYPE="mach"
-CONFIG_FEATURE_INITRD=y
-CONFIG_BLOCKDEV=n
-CONFIG_MOUNT=n
-CONFIG_UMOUNT=n
-CONFIG_SWAPONOFF=n
diff --git a/debian/config/os/kfreebsd b/debian/config/os/kfreebsd
deleted file mode 100644 (file)
index 9e40944..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-CONFIG_PLATFORM_LINUX=n
-CONFIG_INIT_TERMINAL_TYPE="cons25"
-CONFIG_BLOCKDEV=n
diff --git a/debian/config/os/linux b/debian/config/os/linux
deleted file mode 100644 (file)
index 6af524c..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-CONFIG_PLATFORM_LINUX=y
-
diff --git a/debian/control b/debian/control
deleted file mode 100644 (file)
index 9890abf..0000000
+++ /dev/null
@@ -1,597 +0,0 @@
-Source: busybox
-Priority: optional
-Section: utils
-Maintainer: Rafal Krypa <r.krypa@samsung.com>
-Uploaders: Karol Lewandowski <k.lewandowsk@samsung.com>
-X-Maemo-Maintainer: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
-X-Original-Maintainer: Debian Install System Team <debian-boot@lists.debian.org>
-X-Original-Uploaders: Bastian Blank <waldi@debian.org>
-Build-Depends: debhelper (>> 5), python, quilt, libsystemd-daemon-dev
-Standards-Version: 3.7.3
-
-Package: busybox
-Priority: required
-Essential: yes
-Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}
-Provides: ash, awk, bsdutils, coreutils, diffutils, editor, findutils, grep, gzip, hostname, less, man-browser, mount, sed, tar, telnet-client, traceroute, util-linux
-Replaces: ash, bsdutils, coreutils, diffutils, findutils, grep, gzip, hostname, mount, sed, tar, util-linux
-Conflicts: ash, bsdutils, coreutils, diffutils, findutils, grep, gzip, hostname, mount, sed, tar, util-linux
-Description: Tiny utilities for small and embedded systems
- BusyBox combines tiny versions of many common UNIX utilities into a single
- small executable. It provides minimalist replacements for the most common
- utilities you would usually find on your desktop system (i.e., ls, cp, mv,
- mount, tar, etc.). The utilities in BusyBox generally have fewer options than
- their full-featured GNU cousins; however, the options that are included
- provide the expected functionality and behave very much like their GNU
- counterparts.
- .
- This package installs:
- - the BusyBox binary
- - symlinks for tools included into it that correspond to binaries
-   in essential Debian packages
- - alternatives from packages that don't have anything else
- .
- Symlinks to other tools included into BusyBox which correspond to binaries
- in non-essential Debian packages are provided in separate symlink packages.
- Those package are split and named according to the corresponding Debian
- packages.
-
-Package: busybox-dbg
-Architecture: any
-Depends: busybox (= ${binary:Version})
-Section: devel
-Description: Debug symbols for BusyBox
- Debug symbol file for BusyBox. BusyBox provides tiny utilities for small
- and embedded systems.
-
-Package: busybox-symlinks-busybox
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Description: BusyBox specific symlinks
- BusyBox symlinks for utilities without counterparts in Debian.
- These are in separate package because they aren't essentials
- (e.g. needed for system package upgrades).
-
-Package: busybox-symlinks-adduser
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: adduser
-Replaces: adduser
-Conflicts: adduser
-Description: BusyBox symlinks to provide 'adduser'
- BusyBox symlinks for utilities corresponding to 'adduser' package.
-
-Package: busybox-symlinks-adjtimex
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: adjtimex
-Replaces: adjtimex
-Conflicts: adjtimex
-Description: BusyBox symlinks to provide 'adjtimex'
- BusyBox symlinks for utilities corresponding to 'adjtimex' package.
-
-Package: busybox-symlinks-binutils
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: binutils
-Replaces: binutils
-Conflicts: binutils
-Description: BusyBox symlinks to provide 'binutils'
- BusyBox symlinks for utilities corresponding to 'binutils' package.
-
-Package: busybox-symlinks-bridge-utils
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: bridge-utils
-Replaces: bridge-utils
-Conflicts: bridge-utils
-Description: BusyBox symlinks to provide 'bridge-utils'
- BusyBox symlinks for utilities corresponding to 'bridge-utils' package.
-
-Package: busybox-symlinks-bsdmainutils
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: bsdmainutils
-Replaces: bsdmainutils
-Conflicts: bsdmainutils
-Description: BusyBox symlinks to provide 'bsdmainutils'
- BusyBox symlinks for utilities corresponding to 'bsdmainutils' package.
-
-Package: busybox-symlinks-bzip2
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: bzip2
-Replaces: bzip2
-Conflicts: bzip2
-Description: BusyBox symlinks to provide 'bzip2'
- BusyBox symlinks for utilities corresponding to 'bzip2' package.
-
-Package: busybox-symlinks-console-tools
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: console-tools
-Replaces: console-tools
-Conflicts: console-tools
-Description: BusyBox symlinks to provide 'console-tools'
- BusyBox symlinks for utilities corresponding to 'console-tools' package.
-
-Package: busybox-symlinks-cpio
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: cpio
-Replaces: cpio
-Conflicts: cpio
-Description: BusyBox symlinks to provide 'cpio'
- BusyBox symlinks for utilities corresponding to 'cpio' package.
-
-Package: busybox-symlinks-cron
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: cron
-Replaces: cron
-Conflicts: cron
-Description: BusyBox symlinks to provide 'cron'
- BusyBox symlinks for utilities corresponding to 'cron' package.
-
-Package: busybox-symlinks-daemontools
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: daemontools
-Replaces: daemontools
-Conflicts: daemontools
-Description: BusyBox symlinks to provide 'daemontools'
- BusyBox symlinks for utilities corresponding to 'daemontools' package.
-
-Package: busybox-symlinks-dc
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: dc
-Replaces: dc
-Conflicts: dc
-Description: BusyBox symlinks to provide 'dc'
- BusyBox symlinks for utilities corresponding to 'dc' package.
-
-Package: busybox-symlinks-dnsutils
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: dnsutils
-Replaces: dnsutils
-Conflicts: dnsutils
-Description: BusyBox symlinks to provide 'dnsutils'
- BusyBox symlinks for utilities corresponding to 'dnsutils' package.
-
-Package: busybox-symlinks-dosfstools
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: dosfstools
-Replaces: dosfstools
-Conflicts: dosfstools
-Description: BusyBox symlinks to provide 'dosfstools'
- BusyBox symlinks for utilities corresponding to 'dosfstools' package.
-
-Package: busybox-symlinks-ed
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: ed
-Replaces: ed
-Conflicts: ed
-Description: BusyBox symlinks to provide 'ed'
- BusyBox symlinks for utilities corresponding to 'ed' package.
-
-Package: busybox-symlinks-eject
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: eject
-Replaces: eject
-Conflicts: eject
-Description: BusyBox symlinks to provide 'eject'
- BusyBox symlinks for utilities corresponding to 'eject' package.
-
-Package: busybox-symlinks-fbset
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: fbset
-Replaces: fbset
-Conflicts: fbset
-Description: BusyBox symlinks to provide 'fbset'
- BusyBox symlinks for utilities corresponding to 'fbset' package.
-
-Package: busybox-symlinks-fdflush
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: fdflush
-Replaces: fdflush
-Conflicts: fdflush
-Description: BusyBox symlinks to provide 'fdflush'
- BusyBox symlinks for utilities corresponding to 'fdflush' package.
-
-Package: busybox-symlinks-hdparm
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: hdparm
-Replaces: hdparm
-Conflicts: hdparm
-Description: BusyBox symlinks to provide 'hdparm'
- BusyBox symlinks for utilities corresponding to 'hdparm' package.
-
-Package: busybox-symlinks-ifupdown
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: ifupdown
-Replaces: ifupdown
-Conflicts: ifupdown
-Description: BusyBox symlinks to provide 'ifupdown'
- BusyBox symlinks for utilities corresponding to 'ifupdown' package.
-
-Package: busybox-symlinks-initscripts
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: initscripts
-Replaces: initscripts
-Conflicts: initscripts
-Description: BusyBox symlinks to provide 'initscripts'
- BusyBox symlinks for utilities corresponding to 'initscripts' package.
-
-Package: busybox-symlinks-ipcalc
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: ipcalc
-Replaces: ipcalc
-Conflicts: ipcalc
-Description: BusyBox symlinks to provide 'ipcalc'
- BusyBox symlinks for utilities corresponding to 'ipcalc' package.
-
-Package: busybox-symlinks-iproute
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: iproute
-Replaces: iproute
-Conflicts: iproute
-Description: BusyBox symlinks to provide 'iproute'
- BusyBox symlinks for utilities corresponding to 'iproute' package.
-
-Package: busybox-symlinks-ipsvd
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: ipsvd
-Replaces: ipsvd
-Conflicts: ipsvd
-Description: BusyBox symlinks to provide 'ipsvd'
- BusyBox symlinks for utilities corresponding to 'ipsvd' package.
-
-Package: busybox-symlinks-iputils-arping
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: iputils-arping
-Replaces: iputils-arping
-Conflicts: iputils-arping
-Description: BusyBox symlinks to provide 'iputils-arping'
- BusyBox symlinks for utilities corresponding to 'iputils-arping' package.
-
-Package: busybox-symlinks-iputils-ping
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: iputils-ping
-Replaces: iputils-ping
-Conflicts: iputils-ping
-Description: BusyBox symlinks to provide 'iputils-ping'
- BusyBox symlinks for utilities corresponding to 'iputils-ping' package.
-
-Package: busybox-symlinks-klogd
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: klogd
-Replaces: klogd
-Conflicts: klogd
-Description: BusyBox symlinks to provide 'klogd'
- BusyBox symlinks for utilities corresponding to 'klogd' package.
-
-Package: busybox-symlinks-loadlin
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: loadlin
-Replaces: loadlin
-Conflicts: loadlin
-Description: BusyBox symlinks to provide 'loadlin'
- BusyBox symlinks for utilities corresponding to 'loadlin' package.
-
-Package: busybox-symlinks-lrzsz
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: lrzsz
-Replaces: lrzsz
-Conflicts: lrzsz
-Description: BusyBox symlinks to provide 'lrzsz'
- BusyBox symlinks for utilities corresponding to 'lrzsz' package.
-
-Package: busybox-symlinks-lzma
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: lzma
-Replaces: lzma
-Conflicts: lzma
-Description: BusyBox symlinks to provide 'lzma'
- BusyBox symlinks for utilities corresponding to 'lzma' package.
-
-Package: busybox-symlinks-lzop
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: lzop
-Replaces: lzop
-Conflicts: lzop
-Description: BusyBox symlinks to provide 'lzop'
- BusyBox symlinks for utilities corresponding to 'lzop' package.
-
-Package: busybox-symlinks-module-init-tools
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: module-init-tools
-Replaces: module-init-tools
-Conflicts: module-init-tools
-Description: BusyBox symlinks to provide 'module-init-tools'
- BusyBox symlinks for utilities corresponding to 'module-init-tools' package.
-
-Package: busybox-symlinks-mtd-utils
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: mtd-utils
-Replaces: mtd-utils
-Conflicts: mtd-utils
-Description: BusyBox symlinks to provide 'mtd-utils'
- BusyBox symlinks for utilities corresponding to 'mtd-utils' package.
-
-Package: busybox-symlinks-net-tools
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: net-tools
-Replaces: net-tools
-Conflicts: net-tools
-Description: BusyBox symlinks to provide 'net-tools'
- BusyBox symlinks for utilities corresponding to 'net-tools' package.
-
-Package: busybox-symlinks-openbsd-inetd
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: openbsd-inetd
-Replaces: openbsd-inetd
-Conflicts: openbsd-inetd
-Description: BusyBox symlinks to provide 'openbsd-inetd'
- BusyBox symlinks for utilities corresponding to 'openbsd-inetd' package.
-
-Package: busybox-symlinks-passwd
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: passwd
-Replaces: passwd
-Conflicts: passwd
-Description: BusyBox symlinks to provide 'passwd'
- BusyBox symlinks for utilities corresponding to 'passwd' package.
-
-Package: busybox-symlinks-patch
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: patch
-Replaces: patch
-Conflicts: patch
-Description: BusyBox symlinks to provide 'patch'
- BusyBox symlinks for utilities corresponding to 'patch' package.
-
-Package: busybox-symlinks-ppp
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: ppp
-Replaces: ppp
-Conflicts: ppp
-Description: BusyBox symlinks to provide 'ppp'
- BusyBox symlinks for utilities corresponding to 'ppp' package.
-
-Package: busybox-symlinks-procps
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: procps
-Replaces: procps
-Conflicts: procps
-Description: BusyBox symlinks to provide 'procps'
- BusyBox symlinks for utilities corresponding to 'procps' package.
-
-Package: busybox-symlinks-psmisc
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: psmisc
-Replaces: psmisc
-Conflicts: psmisc
-Description: BusyBox symlinks to provide 'psmisc'
- BusyBox symlinks for utilities corresponding to 'psmisc' package.
-
-Package: busybox-symlinks-rdate
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: rdate
-Replaces: rdate
-Conflicts: rdate
-Description: BusyBox symlinks to provide 'rdate'
- BusyBox symlinks for utilities corresponding to 'rdate' package.
-
-Package: busybox-symlinks-realpath
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: realpath
-Replaces: realpath
-Conflicts: realpath
-Description: BusyBox symlinks to provide 'realpath'
- BusyBox symlinks for utilities corresponding to 'realpath' package.
-
-Package: busybox-symlinks-rpm
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: rpm
-Replaces: rpm
-Conflicts: rpm
-Description: BusyBox symlinks to provide 'rpm'
- BusyBox symlinks for utilities corresponding to 'rpm' package.
-
-Package: busybox-symlinks-runit
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: runit
-Replaces: runit
-Conflicts: runit
-Description: BusyBox symlinks to provide 'runit'
- BusyBox symlinks for utilities corresponding to 'runit' package.
-
-Package: busybox-symlinks-sharutils
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: sharutils
-Replaces: sharutils
-Conflicts: sharutils
-Description: BusyBox symlinks to provide 'sharutils'
- BusyBox symlinks for utilities corresponding to 'sharutils' package.
-
-Package: busybox-symlinks-ssmtp
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: ssmtp
-Replaces: ssmtp
-Conflicts: ssmtp
-Description: BusyBox symlinks to provide 'ssmtp'
- BusyBox symlinks for utilities corresponding to 'ssmtp' package.
-
-Package: busybox-symlinks-sysklogd
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: sysklogd
-Replaces: sysklogd
-Conflicts: sysklogd
-Description: BusyBox symlinks to provide 'sysklogd'
- BusyBox symlinks for utilities corresponding to 'sysklogd' package.
-
-Package: busybox-symlinks-telnetd
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: telnetd
-Replaces: telnetd
-Conflicts: telnetd
-Description: BusyBox symlinks to provide 'telnetd'
- BusyBox symlinks for utilities corresponding to 'telnetd' package.
-
-Package: busybox-symlinks-tftp
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: tftp
-Replaces: tftp
-Conflicts: tftp
-Description: BusyBox symlinks to provide 'tftp'
- BusyBox symlinks for utilities corresponding to 'tftp' package.
-
-Package: busybox-symlinks-time
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: time
-Replaces: time
-Conflicts: time
-Description: BusyBox symlinks to provide 'time'
- BusyBox symlinks for utilities corresponding to 'time' package.
-
-Package: busybox-symlinks-tofrodos
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: tofrodos
-Replaces: tofrodos
-Conflicts: tofrodos
-Description: BusyBox symlinks to provide 'tofrodos'
- BusyBox symlinks for utilities corresponding to 'tofrodos' package.
-
-Package: busybox-symlinks-udhcpc
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: udhcpc
-Replaces: udhcpc
-Conflicts: udhcpc
-Description: BusyBox symlinks to provide 'udhcpc'
- BusyBox symlinks for utilities corresponding to 'udhcpc' package.
-
-Package: busybox-symlinks-udhcpd
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: udhcpd
-Replaces: udhcpd
-Conflicts: udhcpd
-Description: BusyBox symlinks to provide 'udhcpd'
- BusyBox symlinks for utilities corresponding to 'udhcpd' package.
-
-Package: busybox-symlinks-unzip
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: unzip
-Replaces: unzip
-Conflicts: unzip
-Description: BusyBox symlinks to provide 'unzip'
- BusyBox symlinks for utilities corresponding to 'unzip' package.
-
-Package: busybox-symlinks-vlan
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: vlan
-Replaces: vlan
-Conflicts: vlan
-Description: BusyBox symlinks to provide 'vlan'
- BusyBox symlinks for utilities corresponding to 'vlan' package.
-
-Package: busybox-symlinks-vlock
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: vlock
-Replaces: vlock
-Conflicts: vlock
-Description: BusyBox symlinks to provide 'vlock'
- BusyBox symlinks for utilities corresponding to 'vlock' package.
-
-Package: busybox-symlinks-watchdog
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: watchdog
-Replaces: watchdog
-Conflicts: watchdog
-Description: BusyBox symlinks to provide 'watchdog'
- BusyBox symlinks for utilities corresponding to 'watchdog' package.
-
-Package: busybox-symlinks-wget
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: wget
-Replaces: wget
-Conflicts: wget
-Description: BusyBox symlinks to provide 'wget'
- BusyBox symlinks for utilities corresponding to 'wget' package.
-
-Package: busybox-symlinks-xterm
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: xterm
-Replaces: xterm
-Conflicts: xterm
-Description: BusyBox symlinks to provide 'xterm'
- BusyBox symlinks for utilities corresponding to 'xterm' package.
-
-Package: busybox-symlinks-zcip
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: zcip
-Replaces: zcip
-Conflicts: zcip
-Description: BusyBox symlinks to provide 'zcip'
- BusyBox symlinks for utilities corresponding to 'zcip' package.
-
-Package: busybox-systemd-klogd
-Architecture: all
-Depends: busybox-symlinks-klogd (= ${binary:Version})
-Description: systemd unit file for BusyBox's klogd
- This package provides systemd unit file for BusyBox's klogd
-
-Package: busybox-systemd-sysklogd
-Architecture: all
-Depends: busybox-symlinks-sysklogd (= ${binary:Version})
-Description: systemd unit file for BusyBox's syslogd
- This package provides systemd unit file for BusyBox's syslogd
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644 (file)
index 7d98fb8..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-This package was debianized by Erik Andersen <andersee@debian.org> on
-Sun, 18 Jun 2000 23:31:02 -0600
-
-It was downloaded from ftp://ftp.busybox.net/busybox
-
-BusyBox is an aggregate of multiple packages. These packages are copyrighted
-by their respective authors.
-
-Copyright: 1999-2005 Erik Andersen
-
-License:
-
-   This package is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; version 2 dated June, 1991.
-
-   This package 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 General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this package; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
-   MA 02110-1301, USA.
-
-On Debian GNU/Linux systems, the complete text of the GNU General
-Public License can be found in `/usr/share/common-licenses/GPL-2'.
diff --git a/debian/local/bash b/debian/local/bash
deleted file mode 100644 (file)
index 1006ab8..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/ash
-/bin/ash "$@"
diff --git a/debian/local/faketrue b/debian/local/faketrue
deleted file mode 100644 (file)
index 1a24852..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#!/bin/sh
diff --git a/debian/local/tempfile b/debian/local/tempfile
deleted file mode 100644 (file)
index d94f654..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/bin/sh
-
-#set -x
-
-mode=0600
-ver=0.0.1
-
-usage() {
-    cat <<EOF
-Usage: tempfile [OPTION]
-
-Create a temporary file in a safe manner.
-
--d, --directory=DIR  place temporary file in DIR
--p, --prefix=STRING  set temporary file's prefix to STRING
--s, --suffix=STRING  set temporary file's suffix to STRING
--m, --mode=MODE      open with MODE instead of 0600
--n, --name=FILE      use FILE instead of tempnam(3)
-    --help           display this help and exit
-    --version        output version information and exit
-
-EOF
-
-}
-
-showversion() {
-    echo "$0 wrapper $ver"
-}
-    
-
-while [ "$1" != "" ]; do
-    case $1 in
-       --help)
-           usage
-           exit 0;;
-       --version)
-           showversion
-           exit 0;;
-       -p|-s|-n)
-           shift 2;;
-       --prefix=*|--suffix=*|--name=*)
-           shift;;
-       -d|--directory)
-           dir="$2"
-           shift 2;;
-       --directory=*)
-           dir=${1##--directory=} 
-           shift;;
-       -m|--mode)
-           mode="$2"
-           shift 2;;
-       --mode=*)
-           mode=${1##--mode=} 
-           shift;;
-       --*)
-           echo "Unknown option $1"
-           shift;;
-       *)
-           echo "Unknown parameter $1"
-           shift;;
-    esac
-done
-
-[ x$dir = x ] || dir="-p $dir"
-
-set -e
-file=`mktemp /tmp/tmp.XXXXXXX`
-chmod $mode $file
-echo $file
diff --git a/debian/patches/05thumb.dpatch b/debian/patches/05thumb.dpatch
deleted file mode 100644 (file)
index 04ffbf9..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#! /bin/sh /usr/share/dpatch/dpatch-run
-## 99-unnamed.dpatch by Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
-##
-## All lines beginning with `## DP:' are a description of the patch.
-## DP: No description.
-
-@DPATCH@
-diff --git a/Makefile.flags b/Makefile.flags
-index 988d6e7..e55e3a2 100644
---- a/Makefile.flags
-+++ b/Makefile.flags
-@@ -41,7 +41,7 @@ CFLAGS += $(call cc-option,-Os -fno-builtin-strlen -finline-limit=0 -fomit-frame
- # of branch probabilities (hopefully makes bloatcheck more stable):
- CFLAGS += $(call cc-option,-fno-guess-branch-probability,)
- CFLAGS += $(call cc-option,-funsigned-char -static-libgcc,)
--CFLAGS += $(call cc-option,-falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1,)
-+CFLAGS += $(call cc-option,-falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1 $(THUMB),)
- # FIXME: These warnings are at least partially to be concerned about and should
- # be fixed..
diff --git a/debian/patches/06ls.dpatch b/debian/patches/06ls.dpatch
deleted file mode 100644 (file)
index b953d77..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#! /bin/sh /usr/share/dpatch/dpatch-run
-## 99-unnamed.dpatch by Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
-##
-## All lines beginning with `## DP:' are a description of the patch.
-## DP: No description.
-
-@DPATCH@
-diff --git a/coreutils/ls.c b/coreutils/ls.c
-index 067e463..39aa63e 100644
---- a/coreutils/ls.c
-+++ b/coreutils/ls.c
-@@ -431,6 +431,7 @@ static void showfiles(struct dnode **dn, int nfiles)
-                       }
-               }
-               putchar('\n');
-+              fflush(NULL);
-               column = 0;
-       }
- }
diff --git a/debian/patches/applets-fallback.patch b/debian/patches/applets-fallback.patch
deleted file mode 100644 (file)
index 880cbe5..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
---- a/shell/ash.c
-+++ b/shell/ash.c
-@@ -7241,25 +7241,10 @@
- static void
--tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
-+tryexec(IF_FEATURE_SH_STANDALONE(int applet_no UNUSED_PARAM,) char *cmd, char **argv, char **envp)
- {
-       int repeated = 0;
--#if ENABLE_FEATURE_SH_STANDALONE
--      if (applet_no >= 0) {
--              if (APPLET_IS_NOEXEC(applet_no)) {
--                      clearenv();
--                      while (*envp)
--                              putenv(*envp++);
--                      run_applet_no_and_exit(applet_no, argv);
--              }
--              /* re-exec ourselves with the new arguments */
--              execve(bb_busybox_exec_path, argv, envp);
--              /* If they called chroot or otherwise made the binary no longer
--               * executable, fall through */
--      }
--#endif
--
-  repeat:
- #ifdef SYSV
-       do {
-@@ -7309,14 +7294,14 @@
-       clearredir(/*drop:*/ 1);
-       envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
--      if (strchr(argv[0], '/') != NULL
--#if ENABLE_FEATURE_SH_STANDALONE
--       || (applet_no = find_applet_by_name(argv[0])) >= 0
--#endif
--      ) {
-+      if (strchr(argv[0], '/') != NULL) {
-               tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
-               e = errno;
-       } else {
-+#if ENABLE_FEATURE_SH_STANDALONE
-+              bb_execv_applet(argv[0], argv, envp);
-+#endif
-+
-               e = ENOENT;
-               while ((cmdname = path_advance(&path, argv[0])) != NULL) {
-                       if (--idx < 0 && pathopt == NULL) {
---- a/libbb/execable.c
-+++ b/libbb/execable.c
-@@ -9,6 +9,9 @@
- #include "libbb.h"
-+#include <alloca.h>
-+#include <stdarg.h>
-+
- /* check if path points to an executable file;
-  * return 1 if found;
-  * return 0 otherwise;
-@@ -68,12 +71,60 @@
- }
- #if ENABLE_FEATURE_PREFER_APPLETS
-+int FAST_FUNC bb_execv_applet(const char *name, char *const argv[], char *const envp[])
-+{
-+      const char **path = bb_busybox_exec_paths;
-+
-+      errno = ENOENT;
-+
-+      if (find_applet_by_name(name) < 0)
-+              return -1;
-+
-+      for (; *path; ++path)
-+              execve(*path, argv, envp);
-+
-+      return -1;
-+}
-+
- /* just like the real execvp, but try to launch an applet named 'file' first
-  */
- int FAST_FUNC bb_execvp(const char *file, char *const argv[])
- {
--      return execvp(find_applet_by_name(file) >= 0 ? bb_busybox_exec_path : file,
--                                      argv);
-+      int ret = bb_execv_applet(file, argv, environ);
-+      if (errno != ENOENT)
-+              return ret;
-+
-+      return execvp(file, argv);
-+}
-+
-+int FAST_FUNC bb_execlp(const char *file, const char *arg, ...)
-+{
-+#define INITIAL_ARGV_MAX 16
-+      size_t argv_max = INITIAL_ARGV_MAX;
-+      const char **argv = malloc(argv_max * sizeof (const char *));
-+      va_list args;
-+      unsigned int i = 0;
-+      int ret;
-+
-+      va_start (args, arg);
-+      while (argv[i++] != NULL) {
-+              if (i == argv_max) {
-+                      const char **nptr;
-+                      argv_max *= 2;
-+                      nptr = realloc (argv, argv_max * sizeof (const char *));
-+                      if (nptr == NULL)
-+                              return -1;
-+                      argv = nptr;
-+              }
-+
-+              argv[i] = va_arg (args, const char *);
-+      }
-+      va_end (args);
-+
-+      ret = bb_execvp(file, (char *const *)argv);
-+      free(argv);
-+
-+      return ret;
- }
- #endif
---- a/libbb/messages.c
-+++ b/libbb/messages.c
-@@ -45,6 +45,15 @@
- const char bb_path_motd_file[] ALIGN1 = "/etc/motd";
- const char bb_dev_null[] ALIGN1 = "/dev/null";
- const char bb_busybox_exec_path[] ALIGN1 = CONFIG_BUSYBOX_EXEC_PATH;
-+const char *bb_busybox_exec_paths[] ALIGN1 = {
-+#ifdef __linux__
-+      "/proc/self/exe",
-+#endif
-+#ifdef CONFIG_BUSYBOX_EXEC_PATH
-+      CONFIG_BUSYBOX_EXEC_PATH,
-+#endif
-+      NULL
-+};
- const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL;
- /* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin,
-  * but I want to save a few bytes here. Check libbb.h before changing! */
---- a/include/libbb.h
-+++ b/include/libbb.h
-@@ -830,11 +830,11 @@
-  * but it may exec busybox and call applet instead of searching PATH.
-  */
- #if ENABLE_FEATURE_PREFER_APPLETS
-+int bb_execv_applet(const char *name, char *const argv[], char *const envp[]) FAST_FUNC;
- int bb_execvp(const char *file, char *const argv[]) FAST_FUNC;
--#define BB_EXECVP(prog,cmd) bb_execvp(prog,cmd)
--#define BB_EXECLP(prog,cmd,...) \
--      execlp((find_applet_by_name(prog) >= 0) ? CONFIG_BUSYBOX_EXEC_PATH : prog, \
--              cmd, __VA_ARGS__)
-+int bb_execlp(const char *file, const char *arg, ...) FAST_FUNC;
-+#define BB_EXECVP(prog,cmd)     bb_execvp(prog,cmd)
-+#define BB_EXECLP(prog,cmd,...) bb_execlp(prog,cmd, __VA_ARGS__)
- #else
- #define BB_EXECVP(prog,cmd)     execvp(prog,cmd)
- #define BB_EXECLP(prog,cmd,...) execlp(prog,cmd, __VA_ARGS__)
-@@ -1575,6 +1575,7 @@
- extern const char bb_path_wtmp_file[];
- extern const char bb_dev_null[];
- extern const char bb_busybox_exec_path[];
-+extern const char *bb_busybox_exec_paths[];
- /* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin,
-  * but I want to save a few bytes here */
- extern const char bb_PATH_root_path[]; /* "PATH=/sbin:/usr/sbin:/bin:/usr/bin" */
---- a/Config.in
-+++ b/Config.in
-@@ -386,13 +386,10 @@
- config BUSYBOX_EXEC_PATH
-       string "Path to BusyBox executable"
--      default "/proc/self/exe"
-+      default "/bin/busybox"
-       help
-         When Busybox applets need to run other busybox 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.
-+        sometimes needs to exec() itself.
- # These are auto-selected by other options
---- a/coreutils/chroot.c
-+++ b/coreutils/chroot.c
-@@ -30,5 +30,7 @@
-               argv[1] = (char *) "-i";
-       }
--      BB_EXECVP_or_die(argv);
-+      execvp(argv[0], argv);
-+      xfunc_error_retval = (errno == ENOENT) ? 127 : 126;
-+      bb_perror_msg_and_die("can't execute '%s'", argv[0]);
- }
diff --git a/debian/patches/blockdev.patch b/debian/patches/blockdev.patch
deleted file mode 100644 (file)
index 46f57b7..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-Description: Backport blockdev applet from upstream
- This allows os-prober to avoid replaying journals when mounting block
- devices read-only.
-Origin: http://git.busybox.net/busybox/tree/util-linux/blockdev.c
-Bug-Debian: http://bugs.debian.org/418163
-Author: Sergey Naumov <sknaumov@gmail.com>
-Author: Denys Vlasenko <dvlasenk@redhat.com>
-Forwarded: not-needed
-Last-Update: 2010-11-09
-
-Index: b/util-linux/blockdev.c
-===================================================================
---- /dev/null
-+++ b/util-linux/blockdev.c
-@@ -0,0 +1,195 @@
-+/*
-+ * blockdev implementation for busybox
-+ *
-+ * Copyright (C) 2010 Sergey Naumov <sknaumov@gmail.com>
-+ *
-+ * Licensed under GPLv2, see file LICENSE in this source tree.
-+ */
-+
-+//applet:IF_BLOCKDEV(APPLET(blockdev, _BB_DIR_SBIN, _BB_SUID_DROP))
-+
-+//kbuild:lib-$(CONFIG_BLOCKDEV) += blockdev.o
-+
-+//config:config BLOCKDEV
-+//config:     bool "blockdev"
-+//config:     default y
-+//config:     help
-+//config:       Performs some ioctls with block devices.
-+
-+//usage:#define blockdev_trivial_usage
-+//usage:      "OPTION BLOCKDEV"
-+//usage:#define blockdev_full_usage "\n\n"
-+//usage:       "Options:"
-+//usage:     "\n      --setro         Set ro"
-+//usage:     "\n      --setrw         Set rw"
-+//usage:     "\n      --getro         Get ro"
-+//usage:     "\n      --getss         Get sector size"
-+//usage:     "\n      --getbsz        Get block size"
-+//usage:     "\n      --setbsz BYTES  Set block size"
-+//usage:     "\n      --getsize       Get device size in 512-byte sectors"
-+//usage:     "\n      --getsize64     Get device size in bytes"
-+//usage:     "\n      --flushbufs     Flush buffers"
-+//usage:     "\n      --rereadpt      Reread partition table"
-+
-+
-+#include "libbb.h"
-+#include <linux/fs.h>
-+
-+enum {
-+      ARG_NONE   = 0,
-+      ARG_INT    = 1,
-+      ARG_ULONG  = 2,
-+      /* Yes, BLKGETSIZE64 takes pointer to uint64_t, not ullong! */
-+      ARG_U64    = 3,
-+      ARG_MASK   = 3,
-+
-+      FL_USRARG   = 4, /* argument is provided by user */
-+      FL_NORESULT = 8,
-+};
-+
-+struct bdc {
-+      uint32_t   ioc;                       /* ioctl code */
-+      const char name[sizeof("flushbufs")]; /* "--setfoo" wothout "--" */
-+      uint8_t    flags;
-+      int8_t     argval;                    /* default argument value */
-+};
-+
-+static const struct bdc bdcommands[] = {
-+      {
-+              .ioc = BLKROSET,
-+              .name = "setro",
-+              .flags = ARG_INT + FL_NORESULT,
-+              .argval = 1,
-+      },{
-+              .ioc = BLKROSET,
-+              .name = "setrw",
-+              .flags = ARG_INT + FL_NORESULT,
-+              .argval = 0,
-+      },{
-+              .ioc = BLKROGET,
-+              .name = "getro",
-+              .flags = ARG_INT,
-+              .argval = -1,
-+      },{
-+              .ioc = BLKSSZGET,
-+              .name = "getss",
-+              .flags = ARG_INT,
-+              .argval = -1,
-+      },{
-+              .ioc = BLKBSZGET,
-+              .name = "getbsz",
-+              .flags = ARG_INT,
-+              .argval = -1,
-+      },{
-+              .ioc = BLKBSZSET,
-+              .name = "setbsz",
-+              .flags = ARG_INT + FL_NORESULT + FL_USRARG,
-+              .argval = 0,
-+      },{
-+              .ioc = BLKGETSIZE,
-+              .name = "getsize",
-+              .flags = ARG_ULONG,
-+              .argval = -1,
-+      },{
-+              .ioc = BLKGETSIZE64,
-+              .name = "getsize64",
-+              .flags = ARG_U64,
-+              .argval = -1,
-+      },{
-+              .ioc = BLKFLSBUF,
-+              .name = "flushbufs",
-+              .flags = ARG_NONE + FL_NORESULT,
-+              .argval = 0,
-+      },{
-+              .ioc = BLKRRPART,
-+              .name = "rereadpt",
-+              .flags = ARG_NONE + FL_NORESULT,
-+              .argval = 0,
-+      }
-+};
-+
-+static const struct bdc *find_cmd(const char *s)
-+{
-+      const struct bdc *bdcmd = bdcommands;
-+      if (s[0] == '-' && s[1] == '-') {
-+              s += 2;
-+              do {
-+                      if (strcmp(s, bdcmd->name) == 0)
-+                              return bdcmd;
-+                      bdcmd++;
-+              } while (bdcmd != bdcommands + ARRAY_SIZE(bdcommands));
-+      }
-+      bb_show_usage();
-+}
-+
-+int blockdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-+int blockdev_main(int argc, char **argv)
-+{
-+      const struct bdc *bdcmd;
-+      int fd;
-+      uint64_t u64;
-+      union {
-+              int i;
-+              unsigned long lu;
-+              uint64_t u64;
-+      } ioctl_val_on_stack;
-+
-+      if ((unsigned)(argc - 3) > 1) /* must have 2 or 3 args */
-+              bb_show_usage();
-+
-+      bdcmd = find_cmd(*++argv);
-+
-+      u64 = (int)bdcmd->argval;
-+      if (bdcmd->flags & FL_USRARG)
-+              u64 = xatoi_u(*++argv);
-+
-+      if (!*++argv || argv[1])
-+              bb_show_usage();
-+      fd = xopen(*argv, O_RDONLY);
-+
-+      ioctl_val_on_stack.u64 = u64;
-+#if BB_BIG_ENDIAN
-+      /* Store data properly wrt data size.
-+       * (1) It's no-op for little-endian.
-+       * (2) it's no-op for 0 and -1. Only --setro uses arg != 0 and != -1,
-+       * and it is ARG_INT. --setbsz USER_VAL is also ARG_INT.
-+       * Thus, we don't need to handle ARG_ULONG.
-+       */
-+      switch (bdcmd->flags & ARG_MASK) {
-+      case ARG_INT:
-+              ioctl_val_on_stack.i = (int)u64;
-+              break;
-+# if 0 /* unused */
-+      case ARG_ULONG:
-+              ioctl_val_on_stack.lu = (unsigned long)u64;
-+              break;
-+# endif
-+      }
-+#endif
-+
-+      if (ioctl(fd, bdcmd->ioc, &ioctl_val_on_stack.u64) == -1)
-+              bb_simple_perror_msg_and_die(*argv);
-+
-+      /* Fetch it into register(s) */
-+      u64 = ioctl_val_on_stack.u64;
-+
-+      /* Zero- or one-extend the value if needed, then print */
-+      switch (bdcmd->flags & (ARG_MASK+FL_NORESULT)) {
-+      case ARG_INT:
-+              /* Smaller code when we use long long
-+               * (gcc tail-merges printf call)
-+               */
-+              printf("%lld\n", (long long)(int)u64);
-+              break;
-+      case ARG_ULONG:
-+              u64 = (unsigned long)u64;
-+              /* FALLTHROUGH */
-+      case ARG_U64:
-+              printf("%llu\n", (unsigned long long)u64);
-+              break;
-+      }
-+
-+      if (ENABLE_FEATURE_CLEAN_UP)
-+              close(fd);
-+      return EXIT_SUCCESS;
-+}
diff --git a/debian/patches/bootchartd-mounting-tmpfs-is-Linux-specific.patch b/debian/patches/bootchartd-mounting-tmpfs-is-Linux-specific.patch
deleted file mode 100644 (file)
index aafbed2..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-From e7a0632b7b38f635853a08c276dad2fbd395ba92 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 29 Jul 2010 04:29:53 +0200
-Subject: [PATCH 11/12] bootchartd: mounting tmpfs is Linux-specific
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- init/bootchartd.c |   20 +++++++++++++-------
- 1 files changed, 13 insertions(+), 7 deletions(-)
-
-diff --git a/init/bootchartd.c b/init/bootchartd.c
-index a1c0164..465a349 100644
---- a/init/bootchartd.c
-+++ b/init/bootchartd.c
-@@ -6,7 +6,6 @@
- //config:config BOOTCHARTD
- //config:     bool "bootchartd"
- //config:     default y
--//config:     depends on PLATFORM_LINUX
- //config:     help
- //config:       bootchartd is commonly used to profile the boot process
- //config:       for the purpose of speeding it up. In this case, it is started
-@@ -46,12 +45,15 @@
- #include "libbb.h"
- /* After libbb.h, since it needs sys/types.h on some systems */
- #include <sys/utsname.h>
--#include <sys/mount.h>
--#ifndef MS_SILENT
--# define MS_SILENT      (1 << 15)
--#endif
--#ifndef MNT_DETACH
--# define MNT_DETACH 0x00000002
-+
-+#ifdef __linux__
-+# include <sys/mount.h>
-+# ifndef MS_SILENT
-+#  define MS_SILENT      (1 << 15)
-+# endif
-+# ifndef MNT_DETACH
-+#  define MNT_DETACH 0x00000002
-+# endif
- #endif
- #define BC_VERSION_STR "0.8"
-@@ -175,6 +177,7 @@ static char *make_tempdir(void)
-       char template[] = "/tmp/bootchart.XXXXXX";
-       char *tempdir = xstrdup(mkdtemp(template));
-       if (!tempdir) {
-+#ifdef __linux__
-               /* /tmp is not writable (happens when we are used as init).
-                * Try to mount a tmpfs, them cd and lazily unmount it.
-                * Since we unmount it at once, we can mount it anywhere.
-@@ -192,6 +195,9 @@ static char *make_tempdir(void)
-               if (umount2(try_dir, MNT_DETACH) != 0) {
-                       bb_perror_msg_and_die("can't %smount tmpfs", "un");
-               }
-+#else
-+              bb_perror_msg_and_die("can't create temporary directory");
-+#endif
-       } else {
-               xchdir(tempdir);
-       }
--- 
-1.7.1
-
diff --git a/debian/patches/busybox-zero-ifr.ifr_hwaddr.sa_data.patch b/debian/patches/busybox-zero-ifr.ifr_hwaddr.sa_data.patch
deleted file mode 100644 (file)
index 9fc6f7d..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-diff --git a/networking/interface.c b/networking/interface.c
-index ef187be..6cb1afa 100644
---- a/networking/interface.c
-+++ b/networking/interface.c
-@@ -623,6 +623,7 @@ static int if_fetch(struct interface *ife)
-       strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
-       memset(ife->hwaddr, 0, 32);
-+      memset(ifr.ifr_hwaddr.sa_data, 0, 8);
-       if (ioctl(skfd, SIOCGIFHWADDR, &ifr) >= 0)
-               memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8);
diff --git a/debian/patches/cttyhack-serial-console-detection-is-Linux-specific.patch b/debian/patches/cttyhack-serial-console-detection-is-Linux-specific.patch
deleted file mode 100644 (file)
index 7228615..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-From 430ba79c39eeed4725c36e9c2ad61c438c8a5d3e Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Fri, 30 Jul 2010 06:21:21 +0200
-Subject: [PATCH 13/19] cttyhack: serial console detection is Linux-specific
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- shell/cttyhack.c |   15 ++++++++++++---
- 1 files changed, 12 insertions(+), 3 deletions(-)
-
-Index: busybox-1.17.1/shell/cttyhack.c
-===================================================================
---- busybox-1.17.1.orig/shell/cttyhack.c       2010-08-01 05:38:31.000000000 +0200
-+++ busybox-1.17.1/shell/cttyhack.c    2010-08-01 05:39:26.000000000 +0200
-@@ -6,6 +6,10 @@
-  */
- #include "libbb.h"
-+#if !defined(__linux__) && !defined(TIOCGSERIAL)
-+# warning cttyhack will not be able to detect a controlling tty on this system
-+#endif
-+
- /* From <linux/vt.h> */
- struct vt_stat {
-       unsigned short v_active;        /* active vt */
-@@ -59,13 +63,19 @@
-               close(fd);
-       } else {
-               /* We don't have ctty (or don't have "/dev/tty" node...) */
--              if (ioctl(0, TIOCGSERIAL, &u.sr) == 0) {
-+              if (0) {}
-+#ifdef TIOCGSERIAL
-+              else if (ioctl(0, TIOCGSERIAL, &u.sr) == 0) {
-                       /* this is a serial console */
-                       sprintf(console + 8, "S%d", u.sr.line);
--              } else if (ioctl(0, VT_GETSTATE, &u.vt) == 0) {
-+              }
-+#endif
-+#ifdef __linux__
-+              else if (ioctl(0, VT_GETSTATE, &u.vt) == 0) {
-                       /* this is linux virtual tty */
-                       sprintf(console + 8, "S%d" + 1, u.vt.v_active);
-               }
-+#endif
-               if (console[8]) {
-                       fd = xopen(console, O_RDWR);
-                       //bb_error_msg("switching to '%s'", console);
-Index: busybox-1.17.1/shell/Config.src
-===================================================================
---- busybox-1.17.1.orig/shell/Config.src       2010-08-01 05:39:44.000000000 +0200
-+++ busybox-1.17.1/shell/Config.src    2010-08-01 05:39:49.000000000 +0200
-@@ -370,7 +370,6 @@
- config CTTYHACK
-       bool "cttyhack"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         One common problem reported on the mailing list is "can't access tty;
-         job control turned off" error message which typically appears when
diff --git a/debian/patches/debian-changes-1:1.17.1-10 b/debian/patches/debian-changes-1:1.17.1-10
deleted file mode 100644 (file)
index 6bb43e4..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-Description: Upstream changes introduced in version 1:1.17.1-10
- This patch has been created by dpkg-source during the package build.
- Here's the last changelog entry, hopefully it gives details on why
- those changes were made:
- .
- busybox (1:1.17.1-10) unstable; urgency=low
- .
-   [ Michael Tokarev ]
-   * tiny build system changes: from main source to build directories:
-     don't copy .dotfiles, use ln instead of cp
- .
-   [ Joey Hess ]
-   * Enable sha256sum in udeb, needed by debootstrap to handle Release
-     files w/o md5sums.
-   * Did not disable md5sum in udeb because eg anna still uses them to verify
-     md5sum fields from d-i Packages files. (Those fields are still present..
-     for now.)
- .
- The person named in the Author field signed this changelog entry.
-Author: Joey Hess <joeyh@debian.org>
-
----
-The information above should follow the Patch Tagging Guidelines, please
-checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
-are templates for supplementary fields that you might want to add:
-
-Origin: <vendor|upstream|other>, <url of original patch>
-Bug: <url in upstream bugtracker>
-Bug-Debian: http://bugs.debian.org/<bugnumber>
-Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
-Forwarded: <no|not-needed|url proving that it has been forwarded>
-Reviewed-By: <name and email of someone who approved the patch>
-Last-Update: <YYYY-MM-DD>
-
---- /dev/null
-+++ busybox-1.17.1/busybox-1.17.1/.indent.pro
-@@ -0,0 +1,33 @@
-+--blank-lines-after-declarations
-+--blank-lines-after-procedures
-+--break-before-boolean-operator
-+--no-blank-lines-after-commas
-+--braces-on-if-line
-+--braces-on-struct-decl-line
-+--comment-indentation25
-+--declaration-comment-column25
-+--no-comment-delimiters-on-blank-lines
-+--cuddle-else
-+--continuation-indentation4
-+--case-indentation0
-+--else-endif-column33
-+--space-after-cast
-+--line-comments-indentation0
-+--declaration-indentation1
-+--dont-format-first-column-comments
-+--dont-format-comments
-+--honour-newlines
-+--indent-level4
-+/* changed from 0 to 4 */
-+--parameter-indentation4
-+--line-length78 /* changed from 75 */
-+--continue-at-parentheses
-+--no-space-after-function-call-names
-+--dont-break-procedure-type
-+--dont-star-comments
-+--leave-optional-blank-lines
-+--dont-space-special-semicolon
-+--tab-size4
-+/* additions by Mark */
-+--case-brace-indentation0
-+--leave-preprocessor-space
diff --git a/debian/patches/doc-man-name.patch b/debian/patches/doc-man-name.patch
deleted file mode 100644 (file)
index d793c35..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
---- a/Makefile.custom
-+++ b/Makefile.custom
-@@ -107,7 +107,7 @@
- # Documentation Targets
- .PHONY: doc
--doc: docs/busybox.pod docs/BusyBox.txt docs/BusyBox.1 docs/BusyBox.html
-+doc: docs/busybox.pod docs/BusyBox.txt docs/busybox.1 docs/BusyBox.html
- # FIXME: Doesn't belong here
-        cmd_doc =
-@@ -134,10 +134,10 @@
-       $(Q)-mkdir -p docs
-       $(Q)-pod2text $< > $@
--docs/BusyBox.1: docs/busybox.pod
-+docs/busybox.1: docs/busybox.pod
-       $(disp_doc)
-       $(Q)-mkdir -p docs
--      $(Q)-pod2man --center=BusyBox --release="version $(KERNELVERSION)" $< > $@
-+      $(Q)-pod2man --center=busybox --release="version $(KERNELVERSION)" $< > $@
- docs/BusyBox.html: docs/busybox.net/BusyBox.html
-       $(disp_doc)
diff --git a/debian/patches/init-console-CRTSCTS.patch b/debian/patches/init-console-CRTSCTS.patch
deleted file mode 100644 (file)
index ddc06fb..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
---- a/init/init.c.original     2010-10-17 20:12:02.000000000 +0200
-+++ b/init/init.c      2010-10-17 20:12:17.000000000 +0200
-@@ -232,7 +232,11 @@
- #endif
-       /* Make it be sane */
--      tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
-+      tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD
-+#ifdef CRTSCTS
-+              | CRTSCTS
-+#endif
-+              ;
-       tty.c_cflag |= CREAD | HUPCL | CLOCAL;
-       /* input modes */
diff --git a/debian/patches/init-console.patch b/debian/patches/init-console.patch
deleted file mode 100644 (file)
index 6845950..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/init/init.c
-+++ b/init/init.c
-@@ -441,6 +441,8 @@
-       for (a = init_action_list; a; a = a->next) {
-               if (!(a->action_type & action_type))
-                       continue;
-+              if (a->terminal[0] && access(a->terminal, R_OK | W_OK))
-+                      continue;
-               if (a->action_type & (SYSINIT | WAIT | ONCE | CTRLALTDEL | SHUTDOWN)) {
-                       pid_t pid = run(a);
diff --git a/debian/patches/init-halt-portability-improvements.patch b/debian/patches/init-halt-portability-improvements.patch
deleted file mode 100644 (file)
index 94618c3..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-From 714674e4da3d92c5dd14e00ab30794a895b91eb4 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:38:44 +0200
-Subject: [PATCH 4/9] init,halt: portability improvements
-
-* make init and halt use the same RB_* constants for reboot()
-* conditionalize the Linux-specific code
-
-Inspired by init.init.diff from the Debian kFreeBSD patches at:
-http://svn.debian.org/viewsvn/d-i/people/slackydeb/kfreebsd/busybox/1.14/debian
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- init/Config.src |    2 --
- init/halt.c     |   14 +-------------
- init/init.c     |   16 ++++++++--------
- init/reboot.h   |   31 +++++++++++++++++++++++++++++++
- 4 files changed, 40 insertions(+), 23 deletions(-)
- create mode 100644 init/reboot.h
-
-Index: busybox-1.17.1/init/Config.src
-===================================================================
---- busybox-1.17.1.orig/init/Config.src        2010-08-01 05:32:43.000000000 +0200
-+++ busybox-1.17.1/init/Config.src     2010-08-01 05:36:47.000000000 +0200
-@@ -10,7 +10,6 @@
- config INIT
-       bool "init"
-       default y
--      depends on PLATFORM_LINUX
-       select FEATURE_SYSLOG
-       help
-         init is the first program run when the system boots.
-@@ -93,7 +92,6 @@
- config HALT
-       bool "poweroff, halt, and reboot"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         Stop all processes and either halt, reboot, or power off the system.
-Index: busybox-1.17.1/init/halt.c
-===================================================================
---- busybox-1.17.1.orig/init/halt.c    2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/init/halt.c 2010-08-01 05:36:47.000000000 +0200
-@@ -8,7 +8,7 @@
-  */
- #include "libbb.h"
--#include <sys/reboot.h>
-+#include "reboot.h"
- #if ENABLE_FEATURE_WTMP
- #include <sys/utsname.h>
-@@ -36,18 +36,6 @@
- #define write_wtmp() ((void)0)
- #endif
--#ifndef RB_HALT_SYSTEM
--#define RB_HALT_SYSTEM RB_HALT
--#endif
--
--#ifndef RB_POWERDOWN
--/* Stop system and switch power off if possible.  */
--# define RB_POWERDOWN   0x4321fedc
--#endif
--#ifndef RB_POWER_OFF
--# define RB_POWER_OFF RB_POWERDOWN
--#endif
--
- int halt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
- int halt_main(int argc UNUSED_PARAM, char **argv)
-Index: busybox-1.17.1/init/init.c
-===================================================================
---- busybox-1.17.1.orig/init/init.c    2010-08-01 05:36:45.000000000 +0200
-+++ busybox-1.17.1/init/init.c 2010-08-01 05:36:47.000000000 +0200
-@@ -12,7 +12,6 @@
- #include "libbb.h"
- #include <syslog.h>
- #include <paths.h>
--#include <sys/reboot.h>
- #include <sys/resource.h>
- #ifdef __linux__
- #include <linux/vt.h>
-@@ -20,6 +19,7 @@
- #if ENABLE_FEATURE_UTMP
- # include <utmp.h> /* DEAD_PROCESS */
- #endif
-+#include "reboot.h" /* reboot() constants */
- /* Used only for sanitizing purposes in set_sane_term() below. On systems where
-  * the baud rate is stored in a separate field, we can safely disable them. */
-@@ -97,13 +97,6 @@
- enum {
-       L_LOG = 0x1,
-       L_CONSOLE = 0x2,
--#ifndef RB_HALT_SYSTEM
--      RB_HALT_SYSTEM = 0xcdef0123, /* FIXME: this overflows enum */
--      RB_ENABLE_CAD = 0x89abcdef,
--      RB_DISABLE_CAD = 0,
--      RB_POWER_OFF = 0x4321fedc,
--      RB_AUTOBOOT = 0x01234567,
--#endif
- };
- /* Print a message to the specified device.
-@@ -726,10 +719,12 @@
-               run_shutdown_and_kill_processes();
-+#ifdef RB_ENABLE_CAD
-               /* Allow Ctrl-Alt-Del to reboot the system.
-                * This is how kernel sets it up for init, we follow suit.
-                */
-               reboot(RB_ENABLE_CAD); /* misnomer */
-+#endif
-               if (open_stdio_to_tty(a->terminal)) {
-                       dbg_message(L_CONSOLE, "Trying to re-exec %s", a->command);
-@@ -872,9 +867,11 @@
-               ) {
-                       bb_show_usage();
-               }
-+#ifdef RB_DISABLE_CAD
-               /* Turn off rebooting via CTL-ALT-DEL - we get a
-                * SIGINT on CAD so we can shut things down gracefully... */
-               reboot(RB_DISABLE_CAD); /* misnomer */
-+#endif
-       }
-       /* Figure out where the default console should be */
-@@ -897,6 +894,8 @@
-       message(L_CONSOLE | L_LOG, "init started: %s", bb_banner);
- #endif
-+/* struct sysinfo is linux-specific */
-+#ifdef __linux__
-       /* Make sure there is enough memory to do something useful. */
-       if (ENABLE_SWAPONOFF) {
-               struct sysinfo info;
-@@ -912,6 +911,7 @@
-                       run_actions(SYSINIT);   /* wait and removing */
-               }
-       }
-+#endif
-       /* Check if we are supposed to be in single user mode */
-       if (argv[1]
-Index: busybox-1.17.1/init/reboot.h
-===================================================================
---- /dev/null  1970-01-01 00:00:00.000000000 +0000
-+++ busybox-1.17.1/init/reboot.h       2010-08-01 05:36:47.000000000 +0200
-@@ -0,0 +1,31 @@
-+/*
-+ * Definitions related to the reboot() system call,
-+ * shared between init.c and halt.c.
-+ */
-+
-+#include <sys/reboot.h>
-+
-+#ifndef RB_HALT_SYSTEM
-+# if defined(__linux__)
-+#  define RB_HALT_SYSTEM  0xcdef0123
-+#  define RB_ENABLE_CAD   0x89abcdef
-+#  define RB_DISABLE_CAD  0
-+#  define RB_POWER_OFF    0x4321fedc
-+#  define RB_AUTOBOOT     0x01234567
-+# elif defined(RB_HALT)
-+#  define RB_HALT_SYSTEM  RB_HALT
-+# endif
-+#endif
-+
-+/* Stop system and switch power off if possible.  */
-+#ifndef RB_POWER_OFF
-+# if defined(RB_POWERDOWN)
-+#  define RB_POWER_OFF  RB_POWERDOWN
-+# elif defined(__linux__)
-+#  define RB_POWER_OFF  0x4321fedc
-+# else
-+#  warning "poweroff unsupported, using halt as fallback"
-+#  define RB_POWER_OFF  RB_HALT_SYSTEM
-+# endif
-+#endif
-+
diff --git a/debian/patches/init-loginutils-termios-portability-fixes.patch b/debian/patches/init-loginutils-termios-portability-fixes.patch
deleted file mode 100644 (file)
index c612263..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-From f812eace1863feeac64dc8af27f4ab0f98119618 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:37:32 +0200
-Subject: [PATCH 3/9] init,loginutils: termios portability fixes
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- init/init.c           |   17 ++++++++++++++++-
- loginutils/Config.src |    2 --
- loginutils/getty.c    |   27 ++++++++++++++-------------
- loginutils/login.c    |    2 +-
- 4 files changed, 31 insertions(+), 17 deletions(-)
-
-diff --git a/init/init.c b/init/init.c
-index 2eb8f1a..1388c75 100644
---- a/init/init.c
-+++ b/init/init.c
-@@ -14,11 +14,19 @@
- #include <paths.h>
- #include <sys/reboot.h>
- #include <sys/resource.h>
-+#ifdef __linux__
- #include <linux/vt.h>
-+#endif
- #if ENABLE_FEATURE_UTMP
- # include <utmp.h> /* DEAD_PROCESS */
- #endif
-+/* Used only for sanitizing purposes in set_sane_term() below. On systems where
-+ * the baud rate is stored in a separate field, we can safely disable them. */
-+#ifndef CBAUD
-+# define CBAUD 0
-+# define CBAUDEX 0
-+#endif
- /* Was a CONFIG_xxx option. A lot of people were building
-  * not fully functional init by switching it on! */
-@@ -166,7 +174,9 @@ static void message(int where, const char *fmt, ...)
- static void console_init(void)
- {
-+#ifdef VT_OPENQRY
-       int vtno;
-+#endif
-       char *s;
-       s = getenv("CONSOLE");
-@@ -190,6 +200,7 @@ static void console_init(void)
-       }
-       s = getenv("TERM");
-+#ifdef VT_OPENQRY
-       if (ioctl(STDIN_FILENO, VT_OPENQRY, &vtno) != 0) {
-               /* Not a linux terminal, probably serial console.
-                * Force the TERM setting to vt102
-@@ -198,7 +209,9 @@ static void console_init(void)
-                       putenv((char*)"TERM=vt102");
-               if (!ENABLE_FEATURE_INIT_SYSLOG)
-                       log_console = NULL;
--      } else if (!s)
-+      } else
-+#endif
-+      if (!s)
-               putenv((char*)"TERM=linux");
- }
-@@ -220,8 +233,10 @@ static void set_sane_term(void)
-       tty.c_cc[VSTOP] = 19;   /* C-s */
-       tty.c_cc[VSUSP] = 26;   /* C-z */
-+#ifdef __linux__
-       /* use line discipline 0 */
-       tty.c_line = 0;
-+#endif
-       /* Make it be sane */
-       tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
-diff --git a/loginutils/Config.src b/loginutils/Config.src
-index 425d041..6ec2893 100644
---- a/loginutils/Config.src
-+++ b/loginutils/Config.src
-@@ -179,7 +179,6 @@ config DELUSER
- config GETTY
-       bool "getty"
-       default y
--      depends on PLATFORM_LINUX
-       select FEATURE_SYSLOG
-       help
-         getty lets you log in on a tty, it is normally invoked by init.
-@@ -187,7 +186,6 @@ config GETTY
- config LOGIN
-       bool "login"
-       default y
--      depends on PLATFORM_LINUX
-       select FEATURE_SUID
-       select FEATURE_SYSLOG
-       help
-diff --git a/loginutils/getty.c b/loginutils/getty.c
-index a5e8e90..7f04d33 100644
---- a/loginutils/getty.c
-+++ b/loginutils/getty.c
-@@ -282,10 +282,8 @@ static void termios_init(struct termios *tp, int speed, struct options *op)
-        * reads will be done in raw mode anyway. Errors will be dealt with
-        * later on.
-        */
--#ifdef __linux__
-       /* flush input and output queues, important for modems! */
--      ioctl(0, TCFLSH, TCIOFLUSH); /* tcflush(0, TCIOFLUSH)? - same */
--#endif
-+      tcflush(0, TCIOFLUSH);
-       ispeed = ospeed = speed;
-       if (speed == B0) {
-               /* Speed was specified as "0" on command line.
-@@ -299,10 +297,13 @@ static void termios_init(struct termios *tp, int speed, struct options *op)
-       cfsetispeed(tp, ispeed);
-       cfsetospeed(tp, ospeed);
--      tp->c_iflag = tp->c_lflag = tp->c_line = 0;
-+      tp->c_iflag = tp->c_lflag = 0;
-       tp->c_oflag = OPOST | ONLCR;
-       tp->c_cc[VMIN] = 1;
-       tp->c_cc[VTIME] = 0;
-+#ifdef __linux__
-+      tp->c_line = 0;
-+#endif
-       /* Optionally enable hardware flow control */
- #ifdef CRTSCTS
-@@ -360,10 +361,8 @@ static void auto_baud(char *buf, unsigned size_buf, struct termios *tp)
-               for (bp = buf; bp < buf + nread; bp++) {
-                       if (isdigit(*bp)) {
-                               speed = bcode(bp);
--                              if (speed > 0) {
--                                      tp->c_cflag &= ~CBAUD;
--                                      tp->c_cflag |= speed;
--                              }
-+                              if (speed > 0)
-+                                      cfsetspeed(tp, speed);
-                               break;
-                       }
-               }
-@@ -417,7 +416,7 @@ static char *get_logname(char *logname, unsigned size_logname,
-       /* Flush pending input (esp. after parsing or switching the baud rate). */
-       sleep(1);
--      ioctl(0, TCFLSH, TCIFLUSH); /* tcflush(0, TCIOFLUSH)? - same */
-+      tcflush(0, TCIOFLUSH);
-       /* Prompt for and read a login name. */
-       logname[0] = '\0';
-@@ -526,7 +525,9 @@ static void termios_final(struct options *op, struct termios *tp, struct chardat
-       tp->c_cc[VQUIT] = DEF_QUIT;     /* default quit */
-       tp->c_cc[VEOF] = DEF_EOF;       /* default EOF character */
-       tp->c_cc[VEOL] = DEF_EOL;
-+#ifdef VSWTC
-       tp->c_cc[VSWTC] = DEF_SWITCH;   /* default switch character */
-+#endif
-       /* Account for special characters seen in input. */
-       if (cp->eol == CR) {
-@@ -572,8 +573,8 @@ static void termios_final(struct options *op, struct termios *tp, struct chardat
- #endif
-       /* Finally, make the new settings effective */
--      /* It's tcsetattr_stdin_TCSANOW() + error check */
--      ioctl_or_perror_and_die(0, TCSETS, tp, "%s: TCSETS", op->tty);
-+      if (tcsetattr_stdin_TCSANOW(tp) < 0)
-+              bb_perror_msg_and_die("%s: tcsetattr", op->tty);
- }
- int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-@@ -650,8 +651,8 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
-        * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
-        * 5 seconds seems to be a good value.
-        */
--      /* tcgetattr() + error check */
--      ioctl_or_perror_and_die(0, TCGETS, &termios, "%s: TCGETS", options.tty);
-+      if (tcgetattr(0, &termios) < 0)
-+              bb_perror_msg_and_die("%s: tcgetattr", options.tty);
-       pid = getpid();
- #ifdef __linux__
-diff --git a/loginutils/login.c b/loginutils/login.c
-index 88ed0af..1001248 100644
---- a/loginutils/login.c
-+++ b/loginutils/login.c
-@@ -264,7 +264,7 @@ int login_main(int argc UNUSED_PARAM, char **argv)
-       while (1) {
-               /* flush away any type-ahead (as getty does) */
--              ioctl(0, TCFLSH, TCIFLUSH);
-+              tcflush(0, TCIFLUSH);
-               if (!username[0])
-                       get_username_or_die(username, sizeof(username));
--- 
-1.7.1
-
diff --git a/debian/patches/init-make-the-initial-TERM-value-configurable.patch b/debian/patches/init-make-the-initial-TERM-value-configurable.patch
deleted file mode 100644 (file)
index fb04a1e..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-From 1c05303fdc302725093294eb0305adc003d52bcb Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:46:07 +0200
-Subject: [PATCH 5/9] init: make the initial $TERM value configurable
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- init/Config.src |   12 ++++++++++++
- init/init.c     |    2 +-
- 2 files changed, 13 insertions(+), 1 deletions(-)
-
-diff --git a/init/Config.src b/init/Config.src
-index 590e298..2cac357 100644
---- a/init/Config.src
-+++ b/init/Config.src
-@@ -89,6 +89,18 @@ config FEATURE_INITRD
-         This does not apply to initramfs, which runs /init as PID 1 and
-         requires no special support.
-+config INIT_TERMINAL_TYPE
-+      string "Initial terminal type"
-+      default "linux"
-+      depends on INIT
-+      help
-+        This is the initial value set by init for the TERM environment
-+        variable. This variable is used by programs which make use of
-+        extended terminal capabilities.
-+
-+        Note that on Linux, init attempts to detect serial terminal and
-+        sets TERM to "vt102" if one is found.
-+
- config HALT
-       bool "poweroff, halt, and reboot"
-       default y
-diff --git a/init/init.c b/init/init.c
-index d8bf158..fa1af6d 100644
---- a/init/init.c
-+++ b/init/init.c
-@@ -205,7 +205,7 @@ static void console_init(void)
-       } else
- #endif
-       if (!s)
--              putenv((char*)"TERM=linux");
-+              putenv((char*)"TERM=" CONFIG_INIT_TERMINAL_TYPE);
- }
- /* Set terminal settings to reasonable defaults.
--- 
-1.7.1
-
diff --git a/debian/patches/klogd-make-it-work-on-non-linux-systems.patch b/debian/patches/klogd-make-it-work-on-non-linux-systems.patch
deleted file mode 100644 (file)
index 033617f..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-From 63c2e7ecc0c7a72b2ed35475a8d18d3052039ce4 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Sun, 1 Aug 2010 03:01:44 +0200
-Subject: [PATCH 1/2] klogd: make it work on non-linux systems
-
-The klogctl() interface allows changing the console loglevel, but is
-Linux-specific. The more portable method of reading from _PATH_KLOG is
-added as an alternative.
-
-Adapted from the Debian kFreeBSD patch at:
-http://svn.debian.org/viewsvn/d-i/people/slackydeb/kfreebsd/busybox/1.14/debian/klogd.diff
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- sysklogd/Config.src |   17 ++++++-
- sysklogd/klogd.c    |  128 +++++++++++++++++++++++++++++++++++++++++++-------
- 2 files changed, 126 insertions(+), 19 deletions(-)
-
-diff --git a/sysklogd/Config.src b/sysklogd/Config.src
-index 41c0d28..1e59872 100644
---- a/sysklogd/Config.src
-+++ b/sysklogd/Config.src
-@@ -109,7 +109,6 @@ config FEATURE_LOGREAD_REDUCED_LOCKING
- config KLOGD
-       bool "klogd"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         klogd is a utility which intercepts and logs all
-         messages from the Linux kernel and sends the messages
-@@ -117,6 +116,22 @@ config KLOGD
-         you wish to record the messages produced by the kernel,
-         you should enable this option.
-+config FEATURE_KLOGD_KLOGCTL
-+      bool "Use the klogctl() interface"
-+      default y
-+      depends on KLOGD && PLATFORM_LINUX
-+      help
-+        The klogd applet supports two interfaces for reading
-+        kernel messages. Linux provides the klogctl() interface
-+        which allows reading messages from the kernel ring buffer
-+        independently from the file system.
-+
-+        If you answer 'N' here, klogd will use the more portable
-+        approach of reading them from /proc or a device node.
-+        However, this method requires the file to be available.
-+
-+        If in doubt, say 'Y'.
-+
- config LOGGER
-       bool "logger"
-       default y
-diff --git a/sysklogd/klogd.c b/sysklogd/klogd.c
-index c54e80a..3468656 100644
---- a/sysklogd/klogd.c
-+++ b/sysklogd/klogd.c
-@@ -4,7 +4,7 @@
-  *
-  * Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>.
-  * Changes: Made this a standalone busybox module which uses standalone
-- *                                    syslog() client interface.
-+ * syslog() client interface.
-  *
-  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
-  *
-@@ -19,18 +19,93 @@
- #include "libbb.h"
- #include <syslog.h>
--#include <sys/klog.h>
--static void klogd_signal(int sig)
-+
-+/* The Linux-specific klogctl(3) interface does not rely on the filesystem and
-+ * allows us to change the console loglevel. Alternatively, we read the
-+ * messages from _PATH_KLOG. */
-+
-+#if ENABLE_FEATURE_KLOGD_KLOGCTL
-+
-+# include <sys/klog.h>
-+
-+static void klogd_open(void)
-+{
-+      /* "Open the log. Currently a NOP" */
-+      klogctl(1, NULL, 0);
-+}
-+
-+static void klogd_setloglevel(int lvl)
-+{
-+      /* "printk() prints a message on the console only if it has a loglevel
-+       * less than console_loglevel". Here we set console_loglevel = lvl. */
-+      klogctl(8, NULL, lvl);
-+}
-+
-+static int klogd_read(char *bufp, int len)
-+{
-+      return klogctl(2, bufp, len);
-+}
-+# define READ_ERROR "klogctl(2) error"
-+
-+static void klogd_close(void)
- {
-       /* FYI: cmd 7 is equivalent to setting console_loglevel to 7
-        * via klogctl(8, NULL, 7). */
-       klogctl(7, NULL, 0); /* "7 -- Enable printk's to console" */
-       klogctl(0, NULL, 0); /* "0 -- Close the log. Currently a NOP" */
--      syslog(LOG_NOTICE, "klogd: exiting");
--      kill_myself_with_sig(sig);
- }
-+#else
-+
-+# include <paths.h>
-+# ifndef _PATH_KLOG
-+#  ifdef __GNU__
-+#   define _PATH_KLOG "/dev/klog"
-+#  else
-+#   error "your system's _PATH_KLOG is unknown"
-+#  endif
-+# endif
-+# define PATH_PRINTK "/proc/sys/kernel/printk"
-+
-+enum { klogfd = 3 };
-+
-+static void klogd_open(void)
-+{
-+      int fd = xopen(_PATH_KLOG, O_RDONLY);
-+      xmove_fd(fd, klogfd);
-+}
-+
-+static void klogd_setloglevel(int lvl)
-+{
-+      FILE *fp = fopen_or_warn(PATH_PRINTK, "w");
-+      if (fp) {
-+              /* This changes only first value:
-+               * "messages with a higher priority than this
-+               * [that is, with numerically lower value]
-+               * will be printed to the console".
-+               * The other three values in this pseudo-file aren't changed.
-+               */
-+              fprintf(fp, "%u\n", lvl);
-+              fclose(fp);
-+      }
-+}
-+
-+static int klogd_read(char *bufp, int len)
-+{
-+      return read(klogfd, bufp, len);
-+}
-+# define READ_ERROR "read error"
-+
-+static void klogd_close(void)
-+{
-+      klogd_setloglevel(7);
-+      if (ENABLE_FEATURE_CLEAN_UP)
-+              close(klogfd);
-+}
-+
-+#endif
-+
- #define log_buffer bb_common_bufsiz1
- enum {
-       KLOGD_LOGBUF_SIZE = sizeof(log_buffer),
-@@ -38,6 +113,19 @@ enum {
-       OPT_FOREGROUND = (1 << 1),
- };
-+/* TODO: glibc openlog(LOG_KERN) reverts to LOG_USER instead,
-+ * because that's how they interpret word "default"
-+ * in the openlog() manpage:
-+ *      LOG_USER (default)
-+ *              generic user-level messages
-+ * and the fact that LOG_KERN is a constant 0.
-+ * glibc interprets it as "0 in openlog() call means 'use default'".
-+ * I think it means "if openlog wasn't called before syslog() is called,
-+ * use default".
-+ * Convincing glibc maintainers otherwise is, as usual, nearly impossible.
-+ * Should we open-code syslog() here to use correct facility?
-+ */
-+
- int klogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
- int klogd_main(int argc UNUSED_PARAM, char **argv)
- {
-@@ -55,34 +143,34 @@ int klogd_main(int argc UNUSED_PARAM, char **argv)
-               bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
-       }
--      openlog("kernel", 0, LOG_KERN);
--
--      bb_signals(BB_FATAL_SIGS, klogd_signal);
--      signal(SIGHUP, SIG_IGN);
-+      logmode = LOGMODE_SYSLOG;
--      /* "Open the log. Currently a NOP" */
--      klogctl(1, NULL, 0);
-+      /* klogd_open() before openlog(), since it might use fixed fd 3,
-+       * and openlog() also may use the same fd 3 if we swap them:
-+       */
-+      klogd_open();
-+      openlog("kernel", 0, LOG_KERN);
--      /* "printk() prints a message on the console only if it has a loglevel
--       * less than console_loglevel". Here we set console_loglevel = i. */
-       if (i)
--              klogctl(8, NULL, i);
-+              klogd_setloglevel(i);
-+
-+      bb_signals(BB_FATAL_SIGS, record_signo);
-+      signal(SIGHUP, SIG_IGN);
-       syslog(LOG_NOTICE, "klogd started: %s", bb_banner);
--      while (1) {
-+      while (!bb_got_signal) {
-               int n;
-               int priority;
-               char *start;
-               /* "2 -- Read from the log." */
-               start = log_buffer + used;
--              n = klogctl(2, start, KLOGD_LOGBUF_SIZE-1 - used);
-+              n = klogd_read(start, KLOGD_LOGBUF_SIZE-1 - used);
-               if (n < 0) {
-                       if (errno == EINTR)
-                               continue;
--                      syslog(LOG_ERR, "klogd: error %d in klogctl(2): %m",
--                                      errno);
-+                      bb_perror_msg(READ_ERROR);
-                       break;
-               }
-               start[n] = '\0';
-@@ -131,5 +219,9 @@ int klogd_main(int argc UNUSED_PARAM, char **argv)
-               }
-       }
-+      klogd_close();
-+      syslog(LOG_NOTICE, "klogd: exiting");
-+      if (bb_got_signal)
-+              kill_myself_with_sig(bb_got_signal);
-       return EXIT_FAILURE;
- }
--- 
-1.7.1
-
diff --git a/debian/patches/less-remove-misguided-dependency-on-PLATFORM_LINUX.patch b/debian/patches/less-remove-misguided-dependency-on-PLATFORM_LINUX.patch
deleted file mode 100644 (file)
index 5bec32f..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-From 5a71fb82025d8bbb87a2d0a0851cb4c9cc31e888 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 29 Jul 2010 04:29:47 +0200
-Subject: [PATCH 10/12] less: remove misguided dependency on PLATFORM_LINUX
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- miscutils/Config.src |    1 -
- 1 files changed, 0 insertions(+), 1 deletions(-)
-
-Index: busybox-1.17.1/miscutils/Config.src
-===================================================================
---- busybox-1.17.1.orig/miscutils/Config.src   2010-08-01 07:33:11.000000000 +0200
-+++ busybox-1.17.1/miscutils/Config.src        2010-08-02 00:45:01.000000000 +0200
-@@ -351,11 +351,6 @@
- config LESS
-       bool "less"
-       default y
--      depends on PLATFORM_LINUX
--      depends on PLATFORM_LINUX
--      depends on PLATFORM_LINUX
--      depends on PLATFORM_LINUX
--      depends on PLATFORM_LINUX
-       help
-         'less' is a pager, meaning that it displays text files. It possesses
-         a wide array of features, and is an improvement over 'more'.
diff --git a/debian/patches/libbb-conditionalize-AF_-usage-in-error-reporting.patch b/debian/patches/libbb-conditionalize-AF_-usage-in-error-reporting.patch
deleted file mode 100644 (file)
index 73c9c32..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-From 29885114a5e3d22ee7aa3ab0e373e00e7cff443c Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:39:24 +0200
-Subject: [PATCH 8/9] libbb: conditionalize AF_* usage in error reporting
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- libbb/xfuncs_printf.c |    4 ++++
- networking/Config.src |    1 -
- 2 files changed, 4 insertions(+), 1 deletions(-)
-
-diff --git a/libbb/xfuncs_printf.c b/libbb/xfuncs_printf.c
-index 7069a7c..91f7ba2 100644
---- a/libbb/xfuncs_printf.c
-+++ b/libbb/xfuncs_printf.c
-@@ -387,8 +387,12 @@ int FAST_FUNC xsocket(int domain, int type, int protocol)
-               /* Hijack vaguely related config option */
- #if ENABLE_VERBOSE_RESOLUTION_ERRORS
-               const char *s = "INET";
-+# ifdef AF_PACKET
-               if (domain == AF_PACKET) s = "PACKET";
-+# endif
-+# ifdef AF_NETLINK
-               if (domain == AF_NETLINK) s = "NETLINK";
-+# endif
- IF_FEATURE_IPV6(if (domain == AF_INET6) s = "INET6";)
-               bb_perror_msg_and_die("socket(AF_%s,%d,%d)", s, type, protocol);
- #else
-diff --git a/networking/Config.src b/networking/Config.src
-index 26c59e7..fc613e8 100644
---- a/networking/Config.src
-+++ b/networking/Config.src
-@@ -43,7 +43,6 @@ config FEATURE_PREFER_IPV4_ADDRESS
- config VERBOSE_RESOLUTION_ERRORS
-       bool "Verbose resolution errors"
-       default n
--      depends on PLATFORM_LINUX #because of xsocket() in libbb/xfuncs_prinf.c
-       help
-         Enable if you are not satisfied with simplistic
-         "can't resolve 'hostname.com'" and want to know more.
--- 
-1.7.1
-
diff --git a/debian/patches/libbb.h-add-device-names-for-Hurd-and-FreeBSD.patch b/debian/patches/libbb.h-add-device-names-for-Hurd-and-FreeBSD.patch
deleted file mode 100644 (file)
index 91ac9bf..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-From 816ed971e4ce60564f7ecbdc016d268d8e936230 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:46:29 +0200
-Subject: [PATCH 6/9] libbb.h: add device names for Hurd and FreeBSD
-
-Adapted from include.libbb.diff from the Debian kFreeBSD people:
-http://svn.debian.org/viewsvn/d-i/people/slackydeb/kfreebsd/busybox/1.14/debian
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- include/libbb.h |   26 +++++++++++++++++++++-----
- 1 files changed, 21 insertions(+), 5 deletions(-)
-
-Index: busybox-1.17.1/include/libbb.h
-===================================================================
---- busybox-1.17.1.orig/include/libbb.h        2010-08-01 05:24:36.000000000 +0200
-+++ busybox-1.17.1/include/libbb.h     2010-08-01 05:36:59.000000000 +0200
-@@ -1614,7 +1614,27 @@
- /* "sh" */
- #define DEFAULT_SHELL_SHORT_NAME   (bb_default_login_shell+6)
--#if ENABLE_FEATURE_DEVFS
-+/* The following devices are the same on all systems.  */
-+#define CURRENT_TTY "/dev/tty"
-+#define DEV_CONSOLE "/dev/console"
-+
-+#if defined(__FreeBSD_kernel__)
-+# define CURRENT_VC CURRENT_TTY
-+# define VC_1 "/dev/ttyv0"
-+# define VC_2 "/dev/ttyv1"
-+# define VC_3 "/dev/ttyv2"
-+# define VC_4 "/dev/ttyv3"
-+# define VC_5 "/dev/ttyv4"
-+# define VC_FORMAT "/dev/ttyv%d"
-+#elif defined(__GNU__)
-+# define CURRENT_VC CURRENT_TTY
-+# define VC_1 "/dev/tty1"
-+# define VC_2 "/dev/tty2"
-+# define VC_3 "/dev/tty3"
-+# define VC_4 "/dev/tty4"
-+# define VC_5 "/dev/tty5"
-+# define VC_FORMAT "/dev/tty%d"
-+#elif ENABLE_FEATURE_DEVFS /* from now on, assume Linux naming */
- # define CURRENT_VC "/dev/vc/0"
- # define VC_1 "/dev/vc/1"
- # define VC_2 "/dev/vc/2"
-@@ -1661,10 +1681,6 @@
- # define FB_0 "/dev/fb0"
- #endif
--/* The following devices are the same on devfs and non-devfs systems.  */
--#define CURRENT_TTY "/dev/tty"
--#define DEV_CONSOLE "/dev/console"
--
- #define ARRAY_SIZE(x) ((unsigned)(sizeof(x) / sizeof((x)[0])))
diff --git a/debian/patches/make_gen_build_files_skip_quilt.patch b/debian/patches/make_gen_build_files_skip_quilt.patch
deleted file mode 100644 (file)
index d654468..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-Index: busybox-1.17.1/scripts/gen_build_files.sh
-===================================================================
---- busybox-1.17.1.orig/scripts/gen_build_files.sh     2010-08-01 07:28:13.000000000 +0200
-+++ busybox-1.17.1/scripts/gen_build_files.sh  2010-08-01 07:31:03.000000000 +0200
-@@ -48,7 +48,7 @@
- fi
- # (Re)generate */Kbuild and */Config.in
--{ cd -- "$srctree" && find -type d; } | while read -r d; do
-+{ cd -- "$srctree" && find -name .\?\* -prune -or -type d -print; } | while read -r d; do
-       d="${d#./}"
-       src="$srctree/$d/Kbuild.src"
diff --git a/debian/patches/make_unicode_printable.patch b/debian/patches/make_unicode_printable.patch
deleted file mode 100644 (file)
index b62ed6f..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-diff --git a/libbb/printable_string.c b/libbb/printable_string.c
-index 83a4821..8a62725 100644
---- a/libbb/printable_string.c
-+++ b/libbb/printable_string.c
-@@ -31,8 +31,8 @@ const char* FAST_FUNC printable_string(uni_stat_t *stats, const char *str)
-               }
-               if (c < ' ')
-                       break;
--              if (c >= 0x7f)
--                      break;
-+              /* if (c >= 0x7f) */
-+                      /* break; */
-               s++;
-       }
-@@ -45,7 +45,8 @@ const char* FAST_FUNC printable_string(uni_stat_t *stats, const char *str)
-                       unsigned char c = *d;
-                       if (c == '\0')
-                               break;
--                      if (c < ' ' || c >= 0x7f)
-+                      /* if (c < ' ' || c >= 0x7f) */
-+                      if (c < ' ')
-                               *d = '?';
-                       d++;
-               }
diff --git a/debian/patches/mark-Linux-specific-configuration-options.patch b/debian/patches/mark-Linux-specific-configuration-options.patch
deleted file mode 100644 (file)
index 8f89c3e..0000000
+++ /dev/null
@@ -1,915 +0,0 @@
-From 1d7266d3b59be361763dab61f680103bbb70f3e9 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Mon, 19 Jul 2010 00:44:56 +0200
-Subject: [PATCH 2/9] mark Linux-specific configuration options
-
-PLATFORM_LINUX is used as a dependency for applets or features
-which require Linux-specific interfaces.
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- Config.in                     |   12 ++++++++++++
- console-tools/Config.src      |   13 +++++++++++++
- coreutils/Config.src          |    2 ++
- coreutils/date.c              |    2 +-
- e2fsprogs/Config.src          |    1 +
- init/Config.src               |    2 ++
- init/bootchartd.c             |    1 +
- libbb/Config.src              |    1 +
- loginutils/Config.src         |    3 +++
- miscutils/Config.src          |   19 ++++++++++++++++++-
- miscutils/conspy.c            |    1 +
- miscutils/ubi_attach_detach.c |    2 ++
- modutils/Config.src           |    1 +
- networking/Config.src         |   23 ++++++++++++++++++++++-
- networking/udhcp/Config.src   |    2 ++
- procps/Config.src             |    4 +++-
- shell/cttyhack.c              |    1 +
- sysklogd/Config.src           |    1 +
- util-linux/Config.src         |   27 +++++++++++++++++++++++++++
- 19 files changed, 114 insertions(+), 4 deletions(-)
-
-Index: busybox-1.17.1/Config.in
-===================================================================
---- busybox-1.17.1.orig/Config.in      2010-08-01 05:24:36.000000000 +0200
-+++ busybox-1.17.1/Config.in   2010-08-01 05:32:43.000000000 +0200
-@@ -47,6 +47,17 @@
-         compiler other than gcc.
-         If you do use gcc, this option may needlessly increase code size.
-+config PLATFORM_LINUX
-+      bool "Enable Linux-specific applets and features"
-+      default y
-+      help
-+        For the most part, busybox requires only POSIX compatibility
-+        from the target system, but some applets and features use
-+        Linux-specific interfaces.
-+
-+        Answering 'N' here will disable such applets and hide the
-+        corresponding configuration options.
-+
- choice
-       prompt "Buffer allocation policy"
-       default FEATURE_BUFFERS_USE_MALLOC
-@@ -353,6 +364,7 @@
- config SELINUX
-       bool "Support NSA Security Enhanced Linux"
-       default n
-+      depends on PLATFORM_LINUX
-       help
-         Enable support for SELinux in applets ls, ps, and id. Also provide
-         the option of compiling in SELinux applets.
-Index: busybox-1.17.1/console-tools/Config.src
-===================================================================
---- busybox-1.17.1.orig/console-tools/Config.src       2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/console-tools/Config.src    2010-08-01 05:32:43.000000000 +0200
-@@ -10,6 +10,7 @@
- config CHVT
-       bool "chvt"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program is used to change to another terminal.
-         Example: chvt 4 (change to terminal /dev/tty4)
-@@ -17,6 +18,7 @@
- config FGCONSOLE
-       bool "fgconsole"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program prints active (foreground) console number.
-@@ -29,12 +31,14 @@
- config DEALLOCVT
-       bool "deallocvt"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program deallocates unused virtual consoles.
- config DUMPKMAP
-       bool "dumpkmap"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program dumps the kernel's keyboard translation table to
-         stdout, in binary format. You can then use loadkmap to load it.
-@@ -42,18 +46,21 @@
- config KBD_MODE
-       bool "kbd_mode"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program reports and sets keyboard mode.
- config LOADFONT
-       bool "loadfont"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program loads a console font from standard input.
- config LOADKMAP
-       bool "loadkmap"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program loads a keyboard translation table from
-         standard input.
-@@ -61,6 +68,7 @@
- config OPENVT
-       bool "openvt"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program is used to start a command on an unused
-         virtual terminal.
-@@ -92,6 +100,7 @@
- config SETCONSOLE
-       bool "setconsole"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program redirects the system console to another device,
-         like the current tty while logged in via telnet.
-@@ -106,6 +115,7 @@
- config SETFONT
-       bool "setfont"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Allows to load console screen map. Useful for i18n.
-@@ -127,6 +137,7 @@
- config SETKEYCODES
-       bool "setkeycodes"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program loads entries into the kernel's scancode-to-keycode
-         map, allowing unusual keyboards to generate usable keycodes.
-@@ -134,12 +145,14 @@
- config SETLOGCONS
-       bool "setlogcons"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program redirects the output console of kernel messages.
- config SHOWKEY
-       bool "showkey"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Shows keys pressed.
-Index: busybox-1.17.1/coreutils/Config.src
-===================================================================
---- busybox-1.17.1.orig/coreutils/Config.src   2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/coreutils/Config.src        2010-08-01 05:32:43.000000000 +0200
-@@ -591,6 +591,7 @@
- config STAT
-       bool "stat"
-       default y
-+      depends on PLATFORM_LINUX # statfs()
-       help
-         display file or filesystem status.
-@@ -606,6 +607,7 @@
- config STTY
-       bool "stty"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         stty is used to change and print terminal line settings.
-Index: busybox-1.17.1/coreutils/date.c
-===================================================================
---- busybox-1.17.1.orig/coreutils/date.c       2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/coreutils/date.c    2010-08-01 05:32:43.000000000 +0200
-@@ -72,7 +72,7 @@
- //config:config FEATURE_DATE_NANO
- //config:     bool "Support %[num]N nanosecond format specifier"
- //config:     default n
--//config:     depends on DATE
-+//config:     depends on DATE && PLATFORM_LINUX # syscall(__NR_clock_gettime)
- //config:     help
- //config:       Support %[num]N format specifier. Adds ~250 bytes of code.
- //config:
-Index: busybox-1.17.1/e2fsprogs/Config.src
-===================================================================
---- busybox-1.17.1.orig/e2fsprogs/Config.src   2010-07-25 00:12:56.000000000 +0200
-+++ busybox-1.17.1/e2fsprogs/Config.src        2010-08-01 05:32:43.000000000 +0200
-@@ -33,6 +33,7 @@
- config LSATTR
-       bool "lsattr"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         lsattr lists the file attributes on a second extended file system.
-Index: busybox-1.17.1/init/Config.src
-===================================================================
---- busybox-1.17.1.orig/init/Config.src        2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/init/Config.src     2010-08-01 05:32:43.000000000 +0200
-@@ -10,6 +10,7 @@
- config INIT
-       bool "init"
-       default y
-+      depends on PLATFORM_LINUX
-       select FEATURE_SYSLOG
-       help
-         init is the first program run when the system boots.
-@@ -92,6 +93,7 @@
- config HALT
-       bool "poweroff, halt, and reboot"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Stop all processes and either halt, reboot, or power off the system.
-Index: busybox-1.17.1/init/bootchartd.c
-===================================================================
---- busybox-1.17.1.orig/init/bootchartd.c      2010-07-25 00:12:56.000000000 +0200
-+++ busybox-1.17.1/init/bootchartd.c   2010-08-01 05:32:43.000000000 +0200
-@@ -6,6 +6,7 @@
- //config:config BOOTCHARTD
- //config:     bool "bootchartd"
- //config:     default y
-+//config:     depends on PLATFORM_LINUX
- //config:     help
- //config:       bootchartd is commonly used to profile the boot process
- //config:       for the purpose of speeding it up. In this case, it is started
-Index: busybox-1.17.1/libbb/Config.src
-===================================================================
---- busybox-1.17.1.orig/libbb/Config.src       2010-07-25 00:12:56.000000000 +0200
-+++ busybox-1.17.1/libbb/Config.src    2010-08-01 05:32:43.000000000 +0200
-@@ -153,6 +153,7 @@
- config MONOTONIC_SYSCALL
-       bool "Use clock_gettime(CLOCK_MONOTONIC) syscall"
-       default n
-+      depends on PLATFORM_LINUX
-       help
-         Use clock_gettime(CLOCK_MONOTONIC) syscall for measuring
-         time intervals (time, ping, traceroute etc need this).
-Index: busybox-1.17.1/loginutils/Config.src
-===================================================================
---- busybox-1.17.1.orig/loginutils/Config.src  2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/loginutils/Config.src       2010-08-01 05:32:43.000000000 +0200
-@@ -179,6 +179,7 @@
- config GETTY
-       bool "getty"
-       default y
-+      depends on PLATFORM_LINUX
-       select FEATURE_SYSLOG
-       help
-         getty lets you log in on a tty, it is normally invoked by init.
-@@ -186,6 +187,7 @@
- config LOGIN
-       bool "login"
-       default y
-+      depends on PLATFORM_LINUX
-       select FEATURE_SUID
-       select FEATURE_SYSLOG
-       help
-@@ -295,6 +297,7 @@
- config VLOCK
-       bool "vlock"
-       default y
-+      depends on PLATFORM_LINUX
-       select FEATURE_SUID
-       help
-         Build the "vlock" applet which allows you to lock (virtual) terminals.
-Index: busybox-1.17.1/miscutils/Config.src
-===================================================================
---- busybox-1.17.1.orig/miscutils/Config.src   2010-07-25 00:12:56.000000000 +0200
-+++ busybox-1.17.1/miscutils/Config.src        2010-08-01 05:32:43.000000000 +0200
-@@ -10,6 +10,7 @@
- config ADJTIMEX
-       bool "adjtimex"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Adjtimex reads and optionally sets adjustment parameters for
-         the Linux clock adjustment algorithm.
-@@ -24,6 +25,7 @@
- config BEEP
-       bool "beep"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The beep applets beeps in a given freq/Hz.
-@@ -180,6 +182,7 @@
- config DEVFSD
-       bool "devfsd (obsolete)"
-       default n
-+      depends on PLATFORM_LINUX
-       select FEATURE_SYSLOG
-       help
-         This is deprecated and should NOT be used anymore.
-@@ -223,6 +226,7 @@
- config FEATURE_DEVFS
-       bool "Use devfs names for all devices (obsolete)"
-       default n
-+      depends on PLATFORM_LINUX
-       help
-         This is obsolete and should NOT be used anymore.
-         Use linux >= 2.6 (optionally with hotplug) and mdev instead!
-@@ -242,6 +246,7 @@
- config EJECT
-       bool "eject"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Used to eject cdroms. (defaults to /dev/cdrom)
-@@ -256,6 +261,7 @@
- config FBSPLASH
-       bool "fbsplash"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Shows splash image and progress bar on framebuffer device.
-         Can be used during boot phase of an embedded device. ~2kb.
-@@ -305,6 +311,7 @@
- config IONICE
-       bool "ionice"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Set/set program io scheduling class and priority
-         Requires kernel >= 2.6.13
-@@ -344,6 +351,11 @@
- config LESS
-       bool "less"
-       default y
-+      depends on PLATFORM_LINUX
-+      depends on PLATFORM_LINUX
-+      depends on PLATFORM_LINUX
-+      depends on PLATFORM_LINUX
-+      depends on PLATFORM_LINUX
-       help
-         'less' is a pager, meaning that it displays text files. It possesses
-         a wide array of features, and is an improvement over 'more'.
-@@ -410,6 +422,7 @@
- config HDPARM
-       bool "hdparm"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Get/Set hard drive parameters. Primarily intended for ATA
-         drives. Adds about 13k (or around 30k if you enable the
-@@ -526,6 +539,7 @@
- config RAIDAUTORUN
-       bool "raidautorun"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         raidautorun tells the kernel md driver to
-         search and start RAID arrays.
-@@ -533,7 +547,7 @@
- config READAHEAD
-       bool "readahead"
-       default y
--      depends on LFS
-+      depends on LFS && PLATFORM_LINUX
-       help
-         Preload the files listed on the command line into RAM cache so that
-         subsequent reads on these files will not block on disk I/O.
-@@ -550,6 +564,7 @@
- config RFKILL
-       bool "rfkill"
-       default n  # doesn't build on Ubuntu 9.04
-+      depends on PLATFORM_LINUX
-       help
-         Enable/disable wireless devices.
-@@ -570,6 +585,7 @@
- config RX
-       bool "rx"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Receive files using the Xmodem protocol.
-@@ -641,6 +657,7 @@
- config WATCHDOG
-       bool "watchdog"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The watchdog utility is used with hardware or software watchdog
-         device drivers. It opens the specified watchdog device special file
-Index: busybox-1.17.1/miscutils/conspy.c
-===================================================================
---- busybox-1.17.1.orig/miscutils/conspy.c     2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/miscutils/conspy.c  2010-08-01 05:32:43.000000000 +0200
-@@ -17,6 +17,7 @@
- //config:config CONSPY
- //config:     bool "conspy"
- //config:     default n
-+//config:     depends on PLATFORM_LINUX
- //config:     help
- //config:       A text-mode VNC like program for Linux virtual terminals.
- //config:       example:  conspy NUM      shared access to console num
-Index: busybox-1.17.1/miscutils/ubi_attach_detach.c
-===================================================================
---- busybox-1.17.1.orig/miscutils/ubi_attach_detach.c  2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/miscutils/ubi_attach_detach.c       2010-08-01 05:32:43.000000000 +0200
-@@ -12,12 +12,14 @@
- //config:config UBIATTACH
- //config:     bool "ubiattach"
- //config:     default n
-+//config:     depends on PLATFORM_LINUX
- //config:     help
- //config:       Attach MTD device to an UBI device.
- //config:
- //config:config UBIDETACH
- //config:     bool "ubidetach"
- //config:     default n
-+//config:     depends on PLATFORM_LINUX
- //config:     help
- //config:       Detach MTD device from an UBI device.
-Index: busybox-1.17.1/modutils/Config.src
-===================================================================
---- busybox-1.17.1.orig/modutils/Config.src    2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/modutils/Config.src 2010-08-01 05:32:43.000000000 +0200
-@@ -4,6 +4,7 @@
- #
- menu "Linux Module Utilities"
-+depends on PLATFORM_LINUX
- INSERT
-Index: busybox-1.17.1/networking/Config.src
-===================================================================
---- busybox-1.17.1.orig/networking/Config.src  2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/networking/Config.src       2010-08-01 05:32:43.000000000 +0200
-@@ -43,6 +43,7 @@
- config VERBOSE_RESOLUTION_ERRORS
-       bool "Verbose resolution errors"
-       default n
-+      depends on PLATFORM_LINUX #because of xsocket() in libbb/xfuncs_prinf.c
-       help
-         Enable if you are not satisfied with simplistic
-         "can't resolve 'hostname.com'" and want to know more.
-@@ -51,18 +52,21 @@
- config ARP
-       bool "arp"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Manipulate the system ARP cache.
- config ARPING
-       bool "arping"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Ping hosts by ARP packets.
- config BRCTL
-       bool "brctl"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Manage ethernet bridges.
-         Supports addbr/delbr and addif/delif.
-@@ -95,6 +99,7 @@
- config ETHER_WAKE
-       bool "ether-wake"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Send a magic packet to wake up sleeping machines.
-@@ -269,6 +274,7 @@
- config IFCONFIG
-       bool "ifconfig"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Ifconfig is used to configure the kernel-resident network interfaces.
-@@ -316,6 +322,7 @@
- config IFENSLAVE
-       bool "ifenslave"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Userspace application to bind several interfaces
-         to a logical interface (use with kernel bonding driver).
-@@ -323,6 +330,7 @@
- config IFPLUGD
-       bool "ifplugd"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Network interface plug detection daemon.
-@@ -364,7 +372,7 @@
- config FEATURE_IFUPDOWN_IP_BUILTIN
-       bool "Use busybox ip applet"
-       default y
--      depends on FEATURE_IFUPDOWN_IP
-+      depends on FEATURE_IFUPDOWN_IP && PLATFORM_LINUX
-       select IP
-       select FEATURE_IP_ADDRESS
-       select FEATURE_IP_LINK
-@@ -483,6 +491,7 @@
- config IP
-       bool "ip"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The "ip" applet is a TCP/IP interface configuration and routing
-         utility. You generally don't need "ip" to use busybox with
-@@ -598,6 +607,7 @@
- config NAMEIF
-       bool "nameif"
-       default y
-+      depends on PLATFORM_LINUX
-       select FEATURE_SYSLOG
-       help
-         nameif is used to rename network interface by its MAC address.
-@@ -626,6 +636,7 @@
- config NETSTAT
-       bool "netstat"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         netstat prints information about the Linux networking subsystem.
-@@ -654,6 +665,7 @@
- config NTPD
-       bool "ntpd"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The NTP client/server daemon.
-@@ -668,6 +680,7 @@
- config PING
-       bool "ping"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to
-         elicit an ICMP ECHO_RESPONSE from a host or gateway.
-@@ -696,12 +709,14 @@
- config ROUTE
-       bool "route"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Route displays or manipulates the kernel's IP routing tables.
- config SLATTACH
-       bool "slattach"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         slattach is a small utility to attach network interfaces to serial
-         lines.
-@@ -719,6 +734,7 @@
- config TCPSVD
-       bool "tcpsvd"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         tcpsvd listens on a TCP port and runs a program for each new
-         connection.
-@@ -888,6 +904,7 @@
- config TRACEROUTE
-       bool "traceroute"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Utility to trace the route of IP packets.
-@@ -924,6 +941,7 @@
- config TUNCTL
-       bool "tunctl"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         tunctl creates or deletes tun devices.
-@@ -949,6 +967,7 @@
- config UDPSVD
-       bool "udpsvd"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         udpsvd listens on an UDP port and runs a program for each new
-         connection.
-@@ -956,6 +975,7 @@
- config VCONFIG
-       bool "vconfig"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Creates, removes, and configures VLAN interfaces
-@@ -990,6 +1010,7 @@
- config ZCIP
-       bool "zcip"
-       default y
-+      depends on PLATFORM_LINUX
-       select FEATURE_SYSLOG
-       help
-         ZCIP provides ZeroConf IPv4 address selection, according to RFC 3927.
-Index: busybox-1.17.1/networking/udhcp/Config.src
-===================================================================
---- busybox-1.17.1.orig/networking/udhcp/Config.src    2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/networking/udhcp/Config.src 2010-08-01 05:32:43.000000000 +0200
-@@ -8,6 +8,7 @@
- config UDHCPD
-       bool "udhcp server (udhcpd)"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         udhcpd is a DHCP server geared primarily toward embedded systems,
-         while striving to be fully functional and RFC compliant.
-@@ -51,6 +52,7 @@
- config UDHCPC
-       bool "udhcp client (udhcpc)"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         udhcpc is a DHCP client geared primarily toward embedded systems,
-         while striving to be fully functional and RFC compliant.
-Index: busybox-1.17.1/procps/Config.src
-===================================================================
---- busybox-1.17.1.orig/procps/Config.src      2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/procps/Config.src   2010-08-01 05:32:43.000000000 +0200
-@@ -10,6 +10,7 @@
- config FREE
-       bool "free"
-       default y
-+      depends on PLATFORM_LINUX #sysinfo()
-       help
-         free displays the total amount of free and used physical and swap
-         memory in the system, as well as the buffers used by the kernel.
-@@ -104,7 +105,7 @@
- config FEATURE_PS_TIME
-       bool "Enable time and elapsed time output"
-       default y
--      depends on PS && DESKTOP
-+      depends on PS && DESKTOP && PLATFORM_LINUX #sysinfo()
-       help
-         Support -o time and -o etime output specifiers.
-@@ -200,6 +201,7 @@
- config UPTIME
-       bool "uptime"
-       default y
-+      depends on PLATFORM_LINUX #sysinfo()
-       help
-         uptime gives a one line display of the current time, how long
-         the system has been running, how many users are currently logged
-Index: busybox-1.17.1/sysklogd/Config.src
-===================================================================
---- busybox-1.17.1.orig/sysklogd/Config.src    2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/sysklogd/Config.src 2010-08-01 05:32:43.000000000 +0200
-@@ -109,6 +109,7 @@
- config KLOGD
-       bool "klogd"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         klogd is a utility which intercepts and logs all
-         messages from the Linux kernel and sends the messages
-Index: busybox-1.17.1/util-linux/Config.src
-===================================================================
---- busybox-1.17.1.orig/util-linux/Config.src  2010-07-25 00:12:56.000000000 +0200
-+++ busybox-1.17.1/util-linux/Config.src       2010-08-01 05:32:43.000000000 +0200
-@@ -10,6 +10,7 @@
- config ACPID
-       bool "acpid"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         acpid listens to ACPI events coming either in textual form from
-         /proc/acpi/event (though it is marked deprecated it is still widely
-@@ -32,6 +33,7 @@
- config BLKID
-       bool "blkid"
-       default y
-+      depends on PLATFORM_LINUX
-       select VOLUMEID
-       help
-         Lists labels and UUIDs of all filesystems.
-@@ -41,6 +43,7 @@
- config DMESG
-       bool "dmesg"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         dmesg is used to examine or control the kernel ring buffer. When the
-         Linux kernel prints messages to the system log, they are stored in
-@@ -74,6 +77,7 @@
- config FBSET
-       bool "fbset"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         fbset is used to show or change the settings of a Linux frame buffer
-         device. The frame buffer device provides a simple and unique
-@@ -102,6 +106,7 @@
- config FDFLUSH
-       bool "fdflush"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         fdflush is only needed when changing media on slightly-broken
-         removable media drives. It is used to make Linux believe that a
-@@ -114,12 +119,14 @@
- config FDFORMAT
-       bool "fdformat"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         fdformat is used to low-level format a floppy disk.
- config FDISK
-       bool "fdisk"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The fdisk utility is used to divide hard disks into one or more
-         logical disks, which are generally called partitions. This utility
-@@ -187,6 +194,7 @@
- config FINDFS
-       bool "findfs"
-       default y
-+      depends on PLATFORM_LINUX
-       select VOLUMEID
-       help
-         Prints the name of a filesystem with given label or UUID.
-@@ -202,6 +210,7 @@
- config FREERAMDISK
-       bool "freeramdisk"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Linux allows you to create ramdisks. This utility allows you to
-         delete them and completely free all memory that was used for the
-@@ -224,12 +233,14 @@
- config MKFS_EXT2
-       bool "mkfs_ext2"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Utility to create EXT2 filesystems.
- config MKFS_MINIX
-       bool "mkfs_minix"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The minix filesystem is a nice, small, compact, read-write filesystem
-         with little overhead. If you wish to be able to create minix
-@@ -247,6 +258,7 @@
- config MKFS_REISER
-       bool "mkfs_reiser"
-       default n
-+      depends on PLATFORM_LINUX
-       help
-         Utility to create ReiserFS filesystems.
-         Note: this applet needs a lot of testing and polishing.
-@@ -254,6 +266,7 @@
- config MKFS_VFAT
-       bool "mkfs_vfat"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Utility to create FAT32 filesystems.
-@@ -302,6 +315,7 @@
- config HWCLOCK
-       bool "hwclock"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The hwclock utility is used to read and set the hardware clock
-         on a system. This is primarily used to set the current time on
-@@ -341,6 +355,7 @@
- config IPCS
-       bool "ipcs"
-       default y
-+      depends on PLATFORM_LINUX
-       select FEATURE_SUID
-       help
-         The ipcs utility is used to provide information on the currently
-@@ -349,6 +364,7 @@
- config LOSETUP
-       bool "losetup"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         losetup is used to associate or detach a loop device with a regular
-         file or block device, and to query the status of a loop device. This
-@@ -357,6 +373,7 @@
- config LSPCI
-       bool "lspci"
-       default y
-+      #depends on PLATFORM_LINUX
-       help
-         lspci is a utility for displaying information about PCI buses in the
-         system and devices connected to them.
-@@ -366,6 +383,7 @@
- config LSUSB
-       bool "lsusb"
-       default y
-+      #depends on PLATFORM_LINUX
-       help
-         lsusb is a utility for displaying information about USB buses in the
-         system and devices connected to them.
-@@ -375,6 +393,7 @@
- config MDEV
-       bool "mdev"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         mdev is a mini-udev implementation for dynamically creating device
-         nodes in the /dev directory.
-@@ -473,6 +492,7 @@
- config MOUNT
-       bool "mount"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         All files and filesystems in Unix are arranged into one big directory
-         tree. The 'mount' utility is used to graft a filesystem onto a
-@@ -555,6 +575,7 @@
- config PIVOT_ROOT
-       bool "pivot_root"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The pivot_root utility swaps the mount points for the root filesystem
-         with some other mounted filesystem. This allows you to do all sorts
-@@ -582,12 +603,14 @@
- config READPROFILE
-       bool "readprofile"
-       default y
-+      #depends on PLATFORM_LINUX
-       help
-         This allows you to parse /proc/profile for basic profiling.
- config RTCWAKE
-       bool "rtcwake"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Enter a system sleep state until specified wakeup time.
-@@ -607,6 +630,7 @@
- config SETARCH
-       bool "setarch"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The linux32 utility is used to create a 32bit environment for the
-         specified program (usually a shell). It only makes sense to have
-@@ -616,6 +640,7 @@
- config SWAPONOFF
-       bool "swaponoff"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This option enables both the 'swapon' and the 'swapoff' utilities.
-         Once you have created some swap space using 'mkswap', you also need
-@@ -634,6 +659,7 @@
- config SWITCH_ROOT
-       bool "switch_root"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The switch_root utility is used from initramfs to select a new
-         root device. Under initramfs, you have to use this instead of
-@@ -653,6 +679,7 @@
- config UMOUNT
-       bool "umount"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         When you want to remove a mounted filesystem from its current mount
-         point, for example when you are shutting down the system, the
-Index: busybox-1.17.1/shell/Config.src
-===================================================================
---- busybox-1.17.1.orig/shell/Config.src       2010-08-01 05:33:24.000000000 +0200
-+++ busybox-1.17.1/shell/Config.src    2010-08-01 05:33:34.000000000 +0200
-@@ -370,6 +370,7 @@
- config CTTYHACK
-       bool "cttyhack"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         One common problem reported on the mailing list is "can't access tty;
-         job control turned off" error message which typically appears when
diff --git a/debian/patches/mkdir-fix-p-on-FreeBSD.patch b/debian/patches/mkdir-fix-p-on-FreeBSD.patch
deleted file mode 100644 (file)
index fc5bf7f..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-From 84b01d5afc8230c79a1b8469c222d940c0d4e792 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:46:33 +0200
-Subject: [PATCH 7/9] mkdir: fix -p on FreeBSD
-
-This patch is libbb.make_directory.diff from Debian kFreeBSD at:
-http://svn.debian.org/viewsvn/d-i/people/slackydeb/kfreebsd/busybox/1.14/debian
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- libbb/make_directory.c |    2 +-
- 1 files changed, 1 insertions(+), 1 deletions(-)
-
-diff --git a/libbb/make_directory.c b/libbb/make_directory.c
-index 4486eb1..6dd04cf 100644
---- a/libbb/make_directory.c
-+++ b/libbb/make_directory.c
-@@ -86,7 +86,7 @@ int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
-               if (mkdir(path, 0777) < 0) {
-                       /* If we failed for any other reason than the directory
-                        * already exists, output a diagnostic and return -1 */
--                      if (errno != EEXIST
-+                      if ((errno != EEXIST && errno != EISDIR)
-                        || !(flags & FILEUTILS_RECUR)
-                        || ((stat(path, &st) < 0) || !S_ISDIR(st.st_mode))
-                       ) {
--- 
-1.7.1
-
diff --git a/debian/patches/readlink-use-xmalloc_realpath.patch b/debian/patches/readlink-use-xmalloc_realpath.patch
deleted file mode 100644 (file)
index e1103ec..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-From b175462422f02a159a14dc5561d8bef6f84b2b66 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:32:19 +0200
-Subject: [PATCH 1/9] readlink: use xmalloc_realpath()
-
-Using realpath() directly with a non-NULL output buffer is unsafe because its
-behavior is unspecified on systems which don't have PATH_MAX (ie. Hurd)
-
-I beleive this also fixes a small bug whereby 'buf' would not be freed
-on 'readlink -v' with ENABLE_FEATURE_CLEANUP.
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- coreutils/readlink.c |    5 ++---
- 1 files changed, 2 insertions(+), 3 deletions(-)
-
-diff --git a/coreutils/readlink.c b/coreutils/readlink.c
-index 20df38b..2ed5e2c 100644
---- a/coreutils/readlink.c
-+++ b/coreutils/readlink.c
-@@ -36,7 +36,6 @@ int readlink_main(int argc UNUSED_PARAM, char **argv)
- {
-       char *buf;
-       char *fname;
--      char pathbuf[PATH_MAX];
-       IF_FEATURE_READLINK_FOLLOW(
-               unsigned opt;
-@@ -56,7 +55,7 @@ int readlink_main(int argc UNUSED_PARAM, char **argv)
-               logmode = LOGMODE_NONE;
-       if (opt & 1) { /* -f */
--              buf = realpath(fname, pathbuf);
-+              buf = xmalloc_realpath(fname);
-       } else {
-               buf = xmalloc_readlink_or_warn(fname);
-       }
-@@ -65,7 +64,7 @@ int readlink_main(int argc UNUSED_PARAM, char **argv)
-               return EXIT_FAILURE;
-       printf((opt & 2) ? "%s" : "%s\n", buf);
--      if (ENABLE_FEATURE_CLEAN_UP && !opt)
-+      if (ENABLE_FEATURE_CLEAN_UP)
-               free(buf);
-       fflush_stdout_and_exit(EXIT_SUCCESS);
--- 
-1.7.1
-
diff --git a/debian/patches/series b/debian/patches/series
deleted file mode 100644 (file)
index 9c507a4..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-doc-man-name.patch
-shell-ash-export-HOME.patch
-applets-fallback.patch
-version.patch
-init-console.patch
-05thumb.dpatch
-06ls.dpatch
-busybox-zero-ifr.ifr_hwaddr.sa_data.patch
-#shell-hist.patch
-top-display-rss.patch
-strip.patch
-make_gen_build_files_skip_quilt.patch
-
-# The following patches have been merged upstream and will be in version 1.18
-readlink-use-xmalloc_realpath.patch
-mark-Linux-specific-configuration-options.patch
-init-loginutils-termios-portability-fixes.patch
-init-halt-portability-improvements.patch
-init-make-the-initial-TERM-value-configurable.patch
-libbb.h-add-device-names-for-Hurd-and-FreeBSD.patch
-mkdir-fix-p-on-FreeBSD.patch
-libbb-conditionalize-AF_-usage-in-error-reporting.patch
-tcpsvd-udpsvd-conditionalize-usage-of-SO_ORIGINAL_DS.patch
-less-remove-misguided-dependency-on-PLATFORM_LINUX.patch
-bootchartd-mounting-tmpfs-is-Linux-specific.patch
-vlock-disable-linux-console-calls-on-other-systems.patch
-cttyhack-serial-console-detection-is-Linux-specific.patch
-klogd-make-it-work-on-non-linux-systems.patch
-stty-sort-out-preprocessor-conditionals.patch
-update-scripts-kconfig-_shipped.patch
-blockdev.patch
-
-# The following patches will likely be merged soon
-u-mount-FreeBSD-support.patch
-swaponoff-FreeBSD-support.patch
-
-# not sent upstream
-init-console-CRTSCTS.patch
-debian-changes-1:1.17.1-10
-
-# SLP
-udhcpc-fast-request.patch
-smack-busybox-1.17.1.patch
-smack-conflict-with-selinux.patch
-make_unicode_printable.patch
diff --git a/debian/patches/shell-ash-export-HOME.patch b/debian/patches/shell-ash-export-HOME.patch
deleted file mode 100644 (file)
index 0d3c5ab..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/shell/ash.c
-+++ b/shell/ash.c
-@@ -1774,7 +1774,7 @@
-       { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL"      , changemail      },
-       { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH"  , changemail      },
- #endif
--      { VSTRFIXED|VTEXTFIXED       , bb_PATH_root_path, changepath },
-+      { VSTRFIXED|VTEXTFIXED|VEXPORT, bb_PATH_root_path, changepath },
-       { VSTRFIXED|VTEXTFIXED       , "PS1=$ "    , NULL            },
-       { VSTRFIXED|VTEXTFIXED       , "PS2=> "    , NULL            },
-       { VSTRFIXED|VTEXTFIXED       , "PS4=+ "    , NULL            },
diff --git a/debian/patches/shell-hist.patch b/debian/patches/shell-hist.patch
deleted file mode 100644 (file)
index 889785a..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
---- a/libbb/lineedit.c 2009-03-30 17:15:10.000000000 +0300
-+++ b/libbb/lineedit.c 2009-03-31 17:52:48.000000000 +0300
-@@ -959,49 +959,48 @@
- static void load_history(const char *fromfile)
- {
-       FILE *fp;
--      int hi;
-+      int hi = 0;
--      /* cleanup old */
--      for (hi = state->cnt_history; hi > 0;) {
--              hi--;
--              free(state->history[hi]);
--      }
--
--      fp = fopen(fromfile, "r");
--      if (fp) {
--              for (hi = 0; hi < MAX_HISTORY;) {
--                      char *hl = xmalloc_getline(fp);
--                      int l;
--
--                      if (!hl)
--                              break;
--                      l = strlen(hl);
--                      if (l >= MAX_LINELEN)
--                              hl[MAX_LINELEN-1] = '\0';
--                      if (l == 0 || hl[0] == ' ') {
--                              free(hl);
--                              continue;
-+      if (!state->cnt_history) {
-+              fp = fopen(fromfile, "r");
-+              if (fp) {
-+                      for (hi = 0; hi < MAX_HISTORY;) {
-+                              char *hl = xmalloc_getline(fp);
-+                              int l;
-+
-+                              if (!hl)
-+                                      break;
-+                              l = strlen(hl);
-+                              if (l >= MAX_LINELEN)
-+                                      hl[MAX_LINELEN-1] = '\0';
-+                              if (l == 0 || hl[0] == ' ') {
-+                                      free(hl);
-+                                      continue;
-+                              }
-+                              state->history[hi++] = hl;
-                       }
--                      state->history[hi++] = hl;
-+                      fclose(fp);
-               }
--              fclose(fp);
-+              state->cur_history = state->cnt_history = hi;
-       }
--      state->cur_history = state->cnt_history = hi;
- }
- /* state->flags is already checked to be nonzero */
--static void save_history(const char *tofile)
-+void save_history(line_input_t *);
-+void save_history(line_input_t *st)
- {
-       FILE *fp;
--      fp = fopen(tofile, "w");
--      if (fp) {
--              int i;
-+      if (st->cnt_history) {
-+              fp = fopen(st->hist_file, "w");
-+              if (fp) {
-+                      int i;
--              for (i = 0; i < state->cnt_history; i++) {
--                      fprintf(fp, "%s\n", state->history[i]);
-+                      for (i = 0; i < st->cnt_history; i++) {
-+                              fprintf(fp, "%s\n", st->history[i]);
-+                      }
-+                      fclose(fp);
-               }
--              fclose(fp);
-       }
- }
- #else
-@@ -1030,10 +1029,6 @@
-       state->history[i++] = xstrdup(str);
-       state->cur_history = i;
-       state->cnt_history = i;
--#if ENABLE_FEATURE_EDITING_SAVEHISTORY
--      if ((state->flags & SAVE_HISTORY) && state->hist_file)
--              save_history(state->hist_file);
--#endif
-       USE_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;)
- }
-diff -r -u busybox-1.10.2.legal.orig/shell/ash.c busybox-1.10.2.legal/shell/ash.c
---- busybox-1.10.2.legal.orig/shell/ash.c      2008-07-24 10:16:00.000000000 +0100
-+++ busybox-1.10.2.legal/shell/ash.c   2009-03-23 17:09:11.000000000 +0000
-@@ -1733,6 +1733,9 @@
- #if ENABLE_ASH_RANDOM_SUPPORT
- static void change_random(const char *);
- #endif
-+#if ENABLE_FEATURE_EDITING_SAVEHISTORY
-+void save_history(line_input_t *);
-+#endif
- static const struct {
-       int flags;
-@@ -12815,6 +12818,10 @@
-       char *p;
-       int status;
-+      if (iflag && (line_input_state->flags & SAVE_HISTORY)
-+          && line_input_state->hist_file && !shlvl) {
-+              save_history(line_input_state);
-+      }
-       status = exitstatus;
-       TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
-       if (setjmp(loc.loc)) {
diff --git a/debian/patches/smack-busybox-1.17.1.patch b/debian/patches/smack-busybox-1.17.1.patch
deleted file mode 100644 (file)
index a95697f..0000000
+++ /dev/null
@@ -1,1604 +0,0 @@
-
-Smack updates for busybox-1.17.1
-
----
- Config.in             |    9 ++
- Makefile              |    1 
- coreutils/id.c        |   26 +++++-
- coreutils/ls.c        |   28 ++++++
- coreutils/stat.c      |   87 ++++++++++++++++----
- findutils/find.c      |   31 +++++++
- include/applets.src.h |    4 
- include/libbb.h       |   21 ++++
- include/usage.src.h   |   30 ++++++-
- libbb/Kbuild.src      |    1 
- libbb/messages.c      |    3 
- libbb/procps.c        |    6 +
- libbb/smack_common.c  |  178 ++++++++++++++++++++++++++++++++++++++++++
- loginutils/adduser.c  |   13 ++-
- loginutils/login.c    |    8 +
- loginutils/su.c       |    8 +
- loginutils/sulogin.c  |    6 +
- miscutils/crond.c     |   11 ++
- miscutils/crontab.c   |   13 +++
- procps/ps.c           |   48 +++++++++--
- smack/Config.src      |   40 +++++++++
- smack/Kbuild.src      |   14 +++
- smack/newsmack.c      |   61 ++++++++++++++
- smack/smackcipso.c    |  119 ++++++++++++++++++++++++++++
- smack/smackenabled.c  |   17 ++++
- smack/smackload.c     |   93 +++++++++++++++++++++
- 26 files changed, 844 insertions(+), 32 deletions(-)
-
-diff -uprN busybox-1.17.1/Config.in busybox-1.17.1-smack/Config.in
---- busybox-1.17.1/Config.in   2010-07-24 15:12:56.000000000 -0700
-+++ busybox-1.17.1-smack/Config.in     2011-08-17 15:27:12.889107702 -0700
-@@ -370,6 +370,14 @@ config SELINUX
-         Most people will leave this set to 'N'.
-+config SMACK
-+      bool "Support Smack"
-+      default n
-+      help
-+        Enable support for Smack in applets ls, ps, and id.  Also provide
-+        the option of compiling in Smack applets.
-+
-+
- config FEATURE_PREFER_APPLETS
-       bool "exec prefers applets"
-       default n
-@@ -739,4 +747,5 @@ source procps/Config.in
- source runit/Config.in
- source selinux/Config.in
- source shell/Config.in
-+source smack/Config.in
- source sysklogd/Config.in
-diff -uprN busybox-1.17.1/coreutils/id.c busybox-1.17.1-smack/coreutils/id.c
---- busybox-1.17.1/coreutils/id.c      2010-07-05 19:25:53.000000000 -0700
-+++ busybox-1.17.1-smack/coreutils/id.c        2011-08-17 15:27:12.889107702 -0700
-@@ -34,6 +34,9 @@ enum {
- #if ENABLE_SELINUX
-       JUST_CONTEXT    = (1 << 5),
- #endif
-+#if ENABLE_SMACK
-+      JUST_LABEL      = (1 << 5),
-+#endif
- };
- static int print_common(unsigned id, const char *name, const char *prefix)
-@@ -116,11 +119,15 @@ int id_main(int argc UNUSED_PARAM, char
- #if ENABLE_SELINUX
-       security_context_t scontext = NULL;
- #endif
-+#if ENABLE_SMACK
-+      char smack[SMACKBUFFSIZE];
-+#endif
-       /* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/
-       /* Don't allow more than one username */
-       opt_complementary = "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG"
--                       IF_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G");
--      opt = getopt32(argv, "rnugG" IF_SELINUX("Z"));
-+                       IF_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G")
-+                       IF_SMACK(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G");
-+      opt = getopt32(argv, "rnugG" IF_SELINUX("Z") IF_SMACK("Z"));
-       username = argv[optind];
-       if (username) {
-@@ -187,6 +194,10 @@ int id_main(int argc UNUSED_PARAM, char
-                               printf(" context=%s", scontext);
-               }
- #endif
-+#if ENABLE_SMACK
-+              if (smack_from_proc(-1, smack, sizeof(smack)) == 0)
-+                      printf(" label=%s", smack);
-+#endif
-       } else if (opt & PRINT_REAL) {
-               euid = ruid;
-               egid = rgid;
-@@ -205,6 +216,17 @@ int id_main(int argc UNUSED_PARAM, char
-               }
-               fputs(scontext, stdout);
-       }
-+#endif
-+#if ENABLE_SMACK
-+      else if (opt & JUST_LABEL) {
-+              if (username || smack_from_proc(-1, smack, sizeof(smack)) < 0) {
-+                      bb_error_msg_and_die("can't get process label%s",
-+                              username ? " for a different user" : "");
-+              }
-+              fputs(smack, stdout);
-+      }
-+#endif
-+#if ENABLE_SELINUX
-       /* freecon(NULL) seems to be harmless */
-       if (ENABLE_FEATURE_CLEAN_UP)
-               freecon(scontext);
-diff -uprN busybox-1.17.1/coreutils/ls.c busybox-1.17.1-smack/coreutils/ls.c
---- busybox-1.17.1/coreutils/ls.c      2010-07-05 19:25:53.000000000 -0700
-+++ busybox-1.17.1-smack/coreutils/ls.c        2011-08-17 16:23:44.839147638 -0700
-@@ -141,6 +141,7 @@ static const char ls_options[] ALIGN1 =
-       IF_FEATURE_LS_RECURSIVE("R")     /* 1, 25 */
-       IF_FEATURE_HUMAN_READABLE("h")   /* 1, 26 */
-       IF_SELINUX("KZ") /* 2, 28 */
-+      IF_SMACK("KZ") /* 2, 28 */
-       IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */
-       ;
- enum {
-@@ -165,6 +166,7 @@ enum {
-               + 1 * ENABLE_FEATURE_LS_RECURSIVE
-               + 1 * ENABLE_FEATURE_HUMAN_READABLE
-               + 2 * ENABLE_SELINUX
-+              + 2 * ENABLE_SMACK
-               + 2 * ENABLE_FEATURE_AUTOWIDTH,
-       OPT_color = 1 << OPTBIT_color,
- };
-@@ -191,6 +193,7 @@ static const unsigned opt_flags[] = {
-       0,                          /* Q (quote filename) - handled via OPT_Q */
-       DISP_HIDDEN,                /* A */
-       ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
-+      ENABLE_SMACK * LIST_CONTEXT, /* k (ignored if !SMACK) */
- #if ENABLE_FEATURE_LS_TIMESTAMPS
-       TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME),   /* c */
-       LIST_FULLTIME,              /* e */
-@@ -216,10 +219,10 @@ static const unsigned opt_flags[] = {
- #if ENABLE_FEATURE_HUMAN_READABLE
-       LS_DISP_HR,                 /* h */
- #endif
--#if ENABLE_SELINUX
-+#if ENABLE_SELINUX || ENABLE_SMACK
-       LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
- #endif
--#if ENABLE_SELINUX
-+#if ENABLE_SELINUX || ENABLE_SMACK
-       LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
- #endif
-       (1U<<31)
-@@ -239,6 +242,7 @@ struct dnode {
-       smallint fname_allocated;
-       struct stat dstat;      /* the file stat info */
-       IF_SELINUX(security_context_t sid;)
-+      IF_SMACK(char xattr[SMACKBUFFSIZE];)
- };
- struct globals {
-@@ -288,6 +292,10 @@ static struct dnode *my_stat(const char
-       struct stat dstat;
-       struct dnode *cur;
-       IF_SELINUX(security_context_t sid = NULL;)
-+#if ENABLE_SMACK
-+      char xattr[SMACKBUFFSIZE];
-+      int i;
-+#endif
-       if ((all_fmt & FOLLOW_LINKS) || force_follow) {
- #if ENABLE_SELINUX
-@@ -295,6 +303,11 @@ static struct dnode *my_stat(const char
-                        getfilecon(fullname, &sid);
-               }
- #endif
-+#if ENABLE_SMACK
-+              i = smack_from_file(fullname, xattr, sizeof(xattr), 1);
-+              if (i < 0)
-+                      xattr[0] = '\0';
-+#endif
-               if (stat(fullname, &dstat)) {
-                       bb_simple_perror_msg(fullname);
-                       exit_code = EXIT_FAILURE;
-@@ -306,6 +319,11 @@ static struct dnode *my_stat(const char
-                       lgetfilecon(fullname, &sid);
-               }
- #endif
-+#if ENABLE_SMACK
-+              i = smack_from_file(fullname, xattr, sizeof(xattr), 0);
-+              if (i < 0)
-+                      xattr[0] = '\0';
-+#endif
-               if (lstat(fullname, &dstat)) {
-                       bb_simple_perror_msg(fullname);
-                       exit_code = EXIT_FAILURE;
-@@ -318,6 +336,7 @@ static struct dnode *my_stat(const char
-       cur->name = name;
-       cur->dstat = dstat;
-       IF_SELINUX(cur->sid = sid;)
-+      IF_SMACK(strncpy(cur->xattr, xattr, sizeof(xattr));)
-       return cur;
- }
-@@ -685,6 +704,10 @@ static NOINLINE unsigned list_single(con
-               freecon(dn->sid);
-       }
- #endif
-+#if ENABLE_SMACK
-+      if (all_fmt & LIST_CONTEXT)
-+              column += printf("%-23s ", dn->xattr);
-+#endif
-       if (all_fmt & LIST_FILENAME) {
- #if ENABLE_FEATURE_LS_COLOR
-               if (show_color) {
-@@ -753,6 +776,7 @@ static void showfiles(struct dnode **dn,
-               }
-               column_width += tabstops +
-                       IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
-+                      IF_SMACK( ((all_fmt & LIST_CONTEXT) ? 24 : 0) + )
-                               ((all_fmt & LIST_INO) ? 8 : 0) +
-                               ((all_fmt & LIST_BLOCKS) ? 5 : 0);
-               ncols = (int) (terminal_width / column_width);
-diff -uprN busybox-1.17.1/coreutils/stat.c busybox-1.17.1-smack/coreutils/stat.c
---- busybox-1.17.1/coreutils/stat.c    2010-07-05 19:25:53.000000000 -0700
-+++ busybox-1.17.1-smack/coreutils/stat.c      2011-08-18 17:01:04.060191320 -0700
-@@ -18,6 +18,7 @@
- #define OPT_TERSE       (1 << 1)
- #define OPT_DEREFERENCE (1 << 2)
- #define OPT_SELINUX     (1 << 3)
-+#define OPT_SMACK       (1 << 4)
- #if ENABLE_FEATURE_STAT_FORMAT
- typedef bool (*statfunc_ptr)(const char *, const char *);
-@@ -154,7 +155,8 @@ static void printfs(char *pformat, const
- /* print statfs info */
- static void FAST_FUNC print_statfs(char *pformat, const char m,
-               const char *const filename, const void *data
--              IF_SELINUX(, security_context_t scontext))
-+              IF_SELINUX(, security_context_t scontext)
-+              IF_SMACK(, char *smack))
- {
-       const struct statfs *statfsbuf = data;
-       if (m == 'n') {
-@@ -192,6 +194,10 @@ static void FAST_FUNC print_statfs(char
-       } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
-               printfs(pformat, scontext);
- # endif
-+#if ENABLE_SMACK
-+      } else if (m == 'C' && (option_mask32 & OPT_SMACK)) {
-+              printfs(pformat, smack);
-+#endif
-       } else {
-               strcatc(pformat, 'c');
-               printf(pformat, m);
-@@ -201,7 +207,8 @@ static void FAST_FUNC print_statfs(char
- /* print stat info */
- static void FAST_FUNC print_stat(char *pformat, const char m,
-               const char *const filename, const void *data
--              IF_SELINUX(, security_context_t scontext))
-+              IF_SELINUX(, security_context_t scontext)
-+              IF_SMACK(, char *smack))
- {
- #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
-       struct stat *statbuf = (struct stat *) data;
-@@ -296,6 +303,10 @@ static void FAST_FUNC print_stat(char *p
-       } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
-               printfs(pformat, scontext);
- # endif
-+#if ENABLE_SMACK
-+      } else if (m == 'C' && (option_mask32 & OPT_SMACK)) {
-+              printfs(pformat, smack);
-+#endif
-       } else {
-               strcatc(pformat, 'c');
-               printf(pformat, m);
-@@ -304,9 +315,10 @@ static void FAST_FUNC print_stat(char *p
- static void print_it(const char *masterformat,
-               const char *filename,
--              void FAST_FUNC (*print_func)(char*, char, const char*, const void* IF_SELINUX(, security_context_t scontext)),
-+              void FAST_FUNC (*print_func)(char*, char, const char*, const void* IF_SELINUX(, security_context_t scontext) IF_SMACK(, char *smack)),
-               const void *data
--              IF_SELINUX(, security_context_t scontext))
-+              IF_SELINUX(, security_context_t scontext)
-+              IF_SMACK(, char *smack) )
- {
-       /* Create a working copy of the format string */
-       char *format = xstrdup(masterformat);
-@@ -349,7 +361,7 @@ static void print_it(const char *masterf
-                       break;
-               default:
-                       /* Completes "%<modifiers>" with specifier and printfs */
--                      print_func(dest, *p, filename, data IF_SELINUX(,scontext));
-+                      print_func(dest, *p, filename, data IF_SELINUX(,scontext) IF_SMACK(,smack) );
-                       break;
-               }
-       }
-@@ -383,6 +395,18 @@ static bool do_statfs(const char *filena
-               }
-       }
- #endif
-+#if ENABLE_SMACK
-+      char smack[SMACKBUFFSIZE];
-+
-+      if (option_mask32 & OPT_SMACK) {
-+              int i = option_mask32 & OPT_DEREFERENCE;
-+
-+              if (smack_from_file(filename, smack, sizeof(smack), i) < 0) {
-+                      bb_perror_msg("%s", filename);
-+                      return 0;
-+              }
-+      }
-+#endif
-       if (statfs(filename, &statfsbuf) != 0) {
-               bb_perror_msg("can't read file system information for '%s'", filename);
-               return 0;
-@@ -390,7 +414,7 @@ static bool do_statfs(const char *filena
- #if ENABLE_FEATURE_STAT_FORMAT
-       if (format == NULL) {
--# if !ENABLE_SELINUX
-+# if !ENABLE_SELINUX && !ENABLE_SMACK
-               format = (option_mask32 & OPT_TERSE
-                       ? "%n %i %l %t %s %b %f %a %c %d\n"
-                       : "  File: \"%n\"\n"
-@@ -400,9 +424,9 @@ static bool do_statfs(const char *filena
-                         "Inodes: Total: %-10c Free: %d");
- # else
-               format = (option_mask32 & OPT_TERSE
--                      ? (option_mask32 & OPT_SELINUX ? "%n %i %l %t %s %b %f %a %c %d %C\n":
-+                      ? (option_mask32 & (OPT_SELINUX | OPT_SMACK) ? "%n %i %l %t %s %b %f %a %c %d %C\n":
-                       "%n %i %l %t %s %b %f %a %c %d\n")
--                      : (option_mask32 & OPT_SELINUX ?
-+                      : (option_mask32 & (OPT_SELINUX | OPT_SMACK) ?
-                       "  File: \"%n\"\n"
-                       "    ID: %-8i Namelen: %-7l Type: %T\n"
-                       "Block size: %-10s\n"
-@@ -415,9 +439,9 @@ static bool do_statfs(const char *filena
-                       "Blocks: Total: %-10b Free: %-10f Available: %a\n"
-                       "Inodes: Total: %-10c Free: %d\n")
-                       );
--# endif /* SELINUX */
-+# endif /* SELINUX or Smack */
-       }
--      print_it(format, filename, print_statfs, &statfsbuf IF_SELINUX(, scontext));
-+      print_it(format, filename, print_statfs, &statfsbuf IF_SELINUX(, scontext) IF_SMACK(, smack) );
- #else /* FEATURE_STAT_FORMAT */
-       format = (option_mask32 & OPT_TERSE
-               ? "%s %llx %lu "
-@@ -433,7 +457,7 @@ static bool do_statfs(const char *filena
-       else
-               printf("Type: %s\n", human_fstype(statfsbuf.f_type));
--# if !ENABLE_SELINUX
-+# if !ENABLE_SELINUX && !ENABLE_SMACK
-       format = (option_mask32 & OPT_TERSE
-               ? "%lu %llu %llu %llu %llu %llu\n"
-               : "Block size: %-10lu\n"
-@@ -448,8 +472,8 @@ static bool do_statfs(const char *filena
-              (unsigned long long) statfsbuf.f_ffree);
- # else
-       format = (option_mask32 & OPT_TERSE
--              ? (option_mask32 & OPT_SELINUX ? "%lu %llu %llu %llu %llu %llu %C\n" : "%lu %llu %llu %llu %llu %llu\n")
--              : (option_mask32 & OPT_SELINUX
-+              ? (option_mask32 & (OPT_SELINUX | OPT_SMACK) ? "%lu %llu %llu %llu %llu %llu %C\n" : "%lu %llu %llu %llu %llu %llu\n")
-+              : (option_mask32 & (OPT_SELINUX | OPT_SMACK)
-                       ?       "Block size: %-10lu\n"
-                               "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n"
-                               "Inodes: Total: %-10llu Free: %llu"
-@@ -496,6 +520,18 @@ static bool do_stat(const char *filename
-               }
-       }
- #endif
-+#if ENABLE_SMACK
-+      char smack[SMACKBUFFSIZE];
-+
-+      if (option_mask32 & OPT_SMACK) {
-+              int i = option_mask32 & OPT_DEREFERENCE;
-+
-+              if (smack_from_file(filename, smack, sizeof(smack), i) < 0) {
-+                      bb_perror_msg("%s", filename);
-+                      return 0;
-+              }
-+      }
-+#endif
-       if ((option_mask32 & OPT_DEREFERENCE ? stat : lstat) (filename, &statbuf) != 0) {
-               bb_perror_msg("can't stat '%s'", filename);
-               return 0;
-@@ -503,7 +539,7 @@ static bool do_stat(const char *filename
- #if ENABLE_FEATURE_STAT_FORMAT
-       if (format == NULL) {
--# if !ENABLE_SELINUX
-+#if !ENABLE_SELINUX && !ENABLE_SMACK
-               if (option_mask32 & OPT_TERSE) {
-                       format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o";
-               } else {
-@@ -526,12 +562,12 @@ static bool do_stat(const char *filename
-               }
- # else
-               if (option_mask32 & OPT_TERSE) {
--                      format = (option_mask32 & OPT_SELINUX ?
-+                      format = (option_mask32 & (OPT_SELINUX | OPT_SMACK) ?
-                                 "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C\n":
-                                 "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n");
-               } else {
-                       if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
--                              format = (option_mask32 & OPT_SELINUX ?
-+                              format = (option_mask32 & (OPT_SELINUX | OPT_SMACK) ?
-                                         "  File: %N\n"
-                                         "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
-                                         "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
-@@ -546,7 +582,7 @@ static bool do_stat(const char *filename
-                                         "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
-                                         "Access: %x\n" "Modify: %y\n" "Change: %z\n");
-                       } else {
--                              format = (option_mask32 & OPT_SELINUX ?
-+                              format = (option_mask32 & (OPT_SELINUX | OPT_SMACK) ?
-                                         "  File: %N\n"
-                                         "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
-                                         "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
-@@ -562,11 +598,14 @@ static bool do_stat(const char *filename
-               }
- # endif
-       }
--      print_it(format, filename, print_stat, &statbuf IF_SELINUX(, scontext));
-+      print_it(format, filename, print_stat, &statbuf IF_SELINUX(, scontext) IF_SMACK(, smack) );
- #else /* FEATURE_STAT_FORMAT */
-       if (option_mask32 & OPT_TERSE) {
-+#if !ENABLE_SELINUX && !ENABLE_SMACK
-+              printf("%s %llu %llu %lx %lu %lu %llx %llu %lu %lx %lx %lu %lu %lu %lu\n"
-+#else
-               printf("%s %llu %llu %lx %lu %lu %llx %llu %lu %lx %lx %lu %lu %lu %lu"
--                     IF_NOT_SELINUX("\n"),
-+#endif
-                      filename,
-                      (unsigned long long) statbuf.st_size,
-                      (unsigned long long) statbuf.st_blocks,
-@@ -589,6 +628,12 @@ static bool do_stat(const char *filename
-               else
-                       bb_putchar('\n');
- # endif
-+#if ENABLE_SMACK
-+              if (option_mask32 & OPT_SMACK)
-+                      printf(" %lc\n", smack);
-+              else
-+                      bb_putchar('\n');
-+#endif
-       } else {
-               char *linkname = NULL;
-@@ -632,6 +677,9 @@ static bool do_stat(const char *filename
- # if ENABLE_SELINUX
-               printf("   S_Context: %lc\n", *scontext);
- # endif
-+#if ENABLE_SMACK
-+              printf("   Smack: %lc\n", smack);
-+#endif
-               printf("Access: %s\n" "Modify: %s\n" "Change: %s\n",
-                      human_time(statbuf.st_atime),
-                      human_time(statbuf.st_mtime),
-@@ -653,6 +701,7 @@ int stat_main(int argc UNUSED_PARAM, cha
-       opt_complementary = "-1"; /* min one arg */
-       opts = getopt32(argv, "ftL"
-               IF_SELINUX("Z")
-+              IF_SMACK("Z")
-               IF_FEATURE_STAT_FORMAT("c:", &format)
-       );
-       if (opts & OPT_FILESYS) /* -f */
-diff -uprN busybox-1.17.1/findutils/find.c busybox-1.17.1-smack/findutils/find.c
---- busybox-1.17.1/findutils/find.c    2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/findutils/find.c      2011-08-18 17:39:38.790218576 -0700
-@@ -223,6 +223,13 @@
- //config:     help
- //config:       Support the 'find -context' option for matching security context.
- //config:
-+//config:config FEATURE_FIND_SMACK
-+//config:     bool "Enable -smack: Smack label matching"
-+//config:     default n
-+//config:     depends on FIND && SMACK
-+//config:     help
-+//config:       Support the 'find -smack' option for matching Smack label.
-+//config:
- //config:config FEATURE_FIND_LINKS
- //config:     bool "Enable -links: link count matching"
- //config:     default y
-@@ -268,6 +275,7 @@ IF_FEATURE_FIND_INUM(   ACTS(inum,  ino_
- IF_FEATURE_FIND_USER(   ACTS(user,  uid_t uid;))
- IF_FEATURE_FIND_SIZE(   ACTS(size,  char size_char; off_t size;))
- IF_FEATURE_FIND_CONTEXT(ACTS(context, security_context_t context;))
-+IF_FEATURE_FIND_SMACK(  ACTS(smack,  const char *smack;))
- IF_FEATURE_FIND_PAREN(  ACTS(paren, action ***subexpr;))
- IF_FEATURE_FIND_PRUNE(  ACTS(prune))
- IF_FEATURE_FIND_DELETE( ACTS(delete))
-@@ -573,6 +581,17 @@ ACTF(context)
-       return rc == 0;
- }
- #endif
-+#if ENABLE_FEATURE_FIND_SMACK
-+ACTF(smack)
-+{
-+      char smack[SMACKBUFFSIZE];
-+
-+      if (smack_from_file(fileName, smack, SMACKBUFFSIZE,
-+              (G.recurse_flags & ACTION_FOLLOWLINKS) ? 1 : 0) < 0)
-+              return FALSE;
-+      return strcmp(ap->smack, smack) == 0;
-+}
-+#endif
- #if ENABLE_FEATURE_FIND_LINKS
- ACTF(links)
- {
-@@ -704,6 +723,7 @@ static action*** parse_params(char **arg
-       IF_FEATURE_FIND_GROUP(  PARM_group     ,)
-       IF_FEATURE_FIND_SIZE(   PARM_size      ,)
-       IF_FEATURE_FIND_CONTEXT(PARM_context   ,)
-+      IF_FEATURE_FIND_SMACK(  PARM_smack     ,)
-       IF_FEATURE_FIND_LINKS(  PARM_links     ,)
-       };
-@@ -738,6 +758,7 @@ static action*** parse_params(char **arg
-       IF_FEATURE_FIND_GROUP(  "-group\0"  )
-       IF_FEATURE_FIND_SIZE(   "-size\0"   )
-       IF_FEATURE_FIND_CONTEXT("-context\0")
-+      IF_FEATURE_FIND_SMACK(  "-smack\0"  )
-       IF_FEATURE_FIND_LINKS(  "-links\0"  )
-                                ;
-@@ -1028,6 +1049,13 @@ static action*** parse_params(char **arg
-                               bb_simple_perror_msg(arg1);
-               }
- #endif
-+#if ENABLE_FEATURE_FIND_SMACK
-+              else if (parm == PARM_smack) {
-+                      action_smack *ap;
-+                      ap = ALLOC_ACTION(smack);
-+                      ap->smack = arg1;
-+              }
-+#endif
- #if ENABLE_FEATURE_FIND_LINKS
-               else if (parm == PARM_links) {
-                       action_links *ap;
-@@ -1115,6 +1143,9 @@ static action*** parse_params(char **arg
- //usage:      IF_FEATURE_FIND_CONTEXT(
- //usage:     "\n      -context        File has specified security context"
- //usage:      )
-+//usage:      IF_FEATURE_FIND_CONTEXT(
-+//usage:     "\n      -smack          File has specified Smack label"
-+//usage:      )
- //usage:      IF_FEATURE_FIND_EXEC(
- //usage:     "\n      -exec CMD ARG ; Run CMD with all instances of {} replaced by the"
- //usage:     "\n                      matching files"
-diff -uprN busybox-1.17.1/include/applets.src.h busybox-1.17.1-smack/include/applets.src.h
---- busybox-1.17.1/include/applets.src.h       2010-07-24 15:12:43.000000000 -0700
-+++ busybox-1.17.1-smack/include/applets.src.h 2011-08-18 11:40:15.809964682 -0700
-@@ -279,6 +279,7 @@ IF_MV(APPLET(mv, _BB_DIR_BIN, _BB_SUID_D
- IF_NAMEIF(APPLET(nameif, _BB_DIR_SBIN, _BB_SUID_DROP))
- IF_NC(APPLET(nc, _BB_DIR_USR_BIN, _BB_SUID_DROP))
- IF_NETSTAT(APPLET(netstat, _BB_DIR_BIN, _BB_SUID_DROP))
-+IF_NEWSMACK(APPLET(newsmack, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
- IF_NICE(APPLET(nice, _BB_DIR_BIN, _BB_SUID_DROP))
- IF_NMETER(APPLET(nmeter, _BB_DIR_USR_BIN, _BB_SUID_DROP))
- IF_NOHUP(APPLET(nohup, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-@@ -356,6 +357,9 @@ IF_SHA512SUM(APPLET_ODDNAME(sha512sum, m
- IF_SHOWKEY(APPLET(showkey, _BB_DIR_USR_BIN, _BB_SUID_DROP))
- IF_SLATTACH(APPLET(slattach, _BB_DIR_SBIN, _BB_SUID_DROP))
- IF_SLEEP(APPLET_NOFORK(sleep, sleep, _BB_DIR_BIN, _BB_SUID_DROP, sleep))
-+IF_SMACKCIPSO(APPLET(smackcipso, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-+IF_SMACKENABLED(APPLET(smackenabled, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-+IF_SMACKLOAD(APPLET(smackload, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
- IF_SOFTLIMIT(APPLET_ODDNAME(softlimit, chpst, _BB_DIR_USR_BIN, _BB_SUID_DROP, softlimit))
- IF_SORT(APPLET_NOEXEC(sort, sort, _BB_DIR_USR_BIN, _BB_SUID_DROP, sort))
- IF_SPLIT(APPLET(split, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-diff -uprN busybox-1.17.1/include/libbb.h busybox-1.17.1-smack/include/libbb.h
---- busybox-1.17.1/include/libbb.h     2010-07-24 15:12:43.000000000 -0700
-+++ busybox-1.17.1-smack/include/libbb.h       2011-08-18 11:54:34.569974792 -0700
-@@ -52,6 +52,12 @@
- #include <selinux/flask.h>
- #include <selinux/av_permissions.h>
- #endif
-+#if ENABLE_SMACK
-+#define       SMACKBUFFSIZE   24
-+#define       SMACKATTR               "security.SMACK64"
-+#define       SMACKFLOOR              "_"
-+#define       SMACKSTAR               "*"
-+#endif
- #if ENABLE_LOCALE_SUPPORT
- # include <locale.h>
- #else
-@@ -1183,6 +1189,16 @@ extern void selinux_preserve_fcontext(in
- #endif
- extern void selinux_or_die(void) FAST_FUNC;
-+#if ENABLE_SMACK
-+extern int smack_from_file(const char *path, char *result, int len, int follow);
-+extern int smack_from_proc(const int pid, char *result, int len);
-+extern int smack_to_file(const char *path, const char *smack, int follow);
-+extern int smack_to_proc(const int pid, const char *smack);
-+extern int smack_user_default(const char *user, char *result, int len);
-+extern int smack_user_allowed(const char *user, const char *smack);
-+extern int smack_user_add(const char *user, const char *smack);
-+#endif
-+
- /* setup_environment:
-  * if chdir pw->pw_dir: ok: else if to_tmp == 1: goto /tmp else: goto / or die
-  * if clear_env = 1: cd(pw->pw_dir), clear environment, then set
-@@ -1391,6 +1407,7 @@ typedef struct procps_status_t {
-       char *argv0;
-       char *exe;
-       IF_SELINUX(char *context;)
-+      IF_SMACK(char smack[SMACKBUFFSIZE];)
-       /* Everything below must contain no ptrs to malloc'ed data:
-        * it is memset(0) for each process in procps_scan() */
-       unsigned long vsz, rss; /* we round it to kbytes */
-@@ -1453,6 +1470,7 @@ enum {
-                               || ENABLE_SESTATUS
-                               ),
-       PSSCAN_CONTEXT  = (1 << 17) * ENABLE_SELINUX,
-+      PSSCAN_SMACK    = (1 << 17) * ENABLE_SMACK,
-       PSSCAN_START_TIME = 1 << 18,
-       PSSCAN_CPU      = (1 << 19) * ENABLE_FEATURE_TOP_SMP_PROCESS,
-       PSSCAN_NICE     = (1 << 20) * ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS,
-@@ -1574,6 +1592,9 @@ extern const char bb_path_group_file[];
- extern const char bb_path_motd_file[];
- extern const char bb_path_wtmp_file[];
- extern const char bb_dev_null[];
-+#if ENABLE_SMACK
-+extern const char bb_path_smack_user[];
-+#endif
- extern const char bb_busybox_exec_path[];
- extern const char *bb_busybox_exec_paths[];
- /* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin,
-diff -uprN busybox-1.17.1/include/usage.src.h busybox-1.17.1-smack/include/usage.src.h
---- busybox-1.17.1/include/usage.src.h 2010-07-24 15:12:43.000000000 -0700
-+++ busybox-1.17.1-smack/include/usage.src.h   2011-08-18 12:10:12.649985839 -0700
-@@ -1764,6 +1764,9 @@ INSERT
-       IF_SELINUX( \
-      "\n      -Z      Print the security context" \
-       ) \
-+      IF_SMACK( \
-+     "\n      -Z      Print the Smack label" \
-+      ) \
-      "\n      -u      Print user ID" \
-      "\n      -g      Print group ID" \
-      "\n      -G      Print supplementary group IDs" \
-@@ -2982,6 +2985,11 @@ INSERT
-      "\n      -p      Display PID/Program name for sockets" \
-       )
-+#define newsmack_trivial_usage \
-+     "label [COMMAND [ARG] ...]"
-+#define newsmack_full_usage \
-+     "Run a program or shell with the specified Smack label" \
-+
- #define nmeter_trivial_usage \
-        "format_string"
- #define nmeter_full_usage "\n\n" \
-@@ -3303,7 +3311,7 @@ INSERT
- #else /* !ENABLE_DESKTOP */
--#if !ENABLE_SELINUX && !ENABLE_FEATURE_PS_WIDE
-+#if !ENABLE_SELINUX && !ENABLE_FEATURE_PS_WIDE && !ENABLE_SMACK
- #define USAGE_PS "\nThis version of ps accepts no options"
- #else
- #define USAGE_PS "\nOptions:"
-@@ -3317,6 +3325,9 @@ INSERT
-       IF_SELINUX( \
-      "\n      -Z      Show selinux context" \
-       ) \
-+      IF_SMACK( \
-+     "\n      -Z      Show Smack label" \
-+      ) \
-       IF_FEATURE_PS_WIDE( \
-      "\n      w       Wide output" \
-       )
-@@ -3845,6 +3856,17 @@ INSERT
-        "$ sleep 1d 3h 22m 8s\n" \
-        "[98528 second delay results]\n")
-+#define smackcipso_trivial_usage
-+#define smackcipso_full_usage \
-+       "Read and set Smack CIPSO mappings from the standard input"
-+
-+#define smackenabled_trivial_usage
-+#define smackenabled_full_usage
-+
-+#define smackload_trivial_usage
-+#define smackload_full_usage \
-+       "Read and set Smack access rules from the standard input"
-+
- #define sort_trivial_usage \
-        "[-nru" \
-       IF_FEATURE_SORT_BIG("gMcszbdfimSTokt] [-o FILE] [-k start[.offset][opts][,end[.offset][opts]] [-t CHAR") \
-@@ -3982,6 +4004,9 @@ INSERT
-       IF_SELINUX( \
-      "\n      -Z      Print security context" \
-       ) \
-+      IF_SMACK( \
-+     "\n      -Z      Print Smack label" \
-+      ) \
-       IF_FEATURE_STAT_FORMAT( \
-        "\n\nValid format sequences for files:\n" \
-        " %a   Access rights in octal\n" \
-@@ -4019,6 +4044,9 @@ INSERT
-       IF_SELINUX( \
-        " %C   Security context in selinux\n" \
-       ) \
-+      IF_SMACK( \
-+       " %C   Smack label\n" \
-+      ) \
-        " %i   File System ID in hex\n" \
-        " %l   Maximum length of filenames\n" \
-        " %n   File name\n" \
-diff -uprN busybox-1.17.1/libbb/Kbuild.src busybox-1.17.1-smack/libbb/Kbuild.src
---- busybox-1.17.1/libbb/Kbuild.src    2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/libbb/Kbuild.src      2011-08-18 15:10:58.330113541 -0700
-@@ -126,6 +126,7 @@ lib-$(CONFIG_FEATURE_UTMP) += utmp.o
- # A mix of optimizations (why build stuff we know won't be used)
- # and objects which may fail to build (SELinux on selinux-less system)
- lib-$(CONFIG_SELINUX) += selinux_common.o
-+lib-$(CONFIG_SMACK) += smack_common.o
- lib-$(CONFIG_FEATURE_MTAB_SUPPORT) += mtab.o
- lib-$(CONFIG_UNICODE_SUPPORT) += unicode.o
- lib-$(CONFIG_FEATURE_CHECK_NAMES) += die_if_bad_username.o
-diff -uprN busybox-1.17.1/libbb/messages.c busybox-1.17.1-smack/libbb/messages.c
---- busybox-1.17.1/libbb/messages.c    2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/libbb/messages.c      2011-08-17 15:27:12.889107702 -0700
-@@ -43,6 +43,9 @@ const char bb_path_shadow_file[] ALIGN1
- const char bb_path_group_file[] ALIGN1 = "/etc/group";
- const char bb_path_gshadow_file[] ALIGN1 = "/etc/gshadow";
- const char bb_path_motd_file[] ALIGN1 = "/etc/motd";
-+#if ENABLE_SMACK
-+const char bb_path_smack_user[] ALIGN1 = "/etc/smack/user";
-+#endif
- const char bb_dev_null[] ALIGN1 = "/dev/null";
- const char bb_busybox_exec_path[] ALIGN1 = CONFIG_BUSYBOX_EXEC_PATH;
- const char *bb_busybox_exec_paths[] ALIGN1 = {
-diff -uprN busybox-1.17.1/libbb/procps.c busybox-1.17.1-smack/libbb/procps.c
---- busybox-1.17.1/libbb/procps.c      2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/libbb/procps.c        2011-08-17 15:27:12.909107701 -0700
-@@ -240,6 +240,12 @@ procps_status_t* FAST_FUNC procps_scan(p
-                               sp->context = NULL;
-               }
- #endif
-+#if ENABLE_SMACK
-+              if (flags & PSSCAN_SMACK) {
-+                      if (smack_from_proc(sp->pid, sp->smack, SMACKBUFFSIZE) < 0)
-+                              strcpy(sp->smack, "?");
-+              }
-+#endif
-               filename_tail = filename + sprintf(filename, "/proc/%u/", pid);
-diff -uprN busybox-1.17.1/libbb/smack_common.c busybox-1.17.1-smack/libbb/smack_common.c
---- busybox-1.17.1/libbb/smack_common.c        1969-12-31 16:00:00.000000000 -0800
-+++ busybox-1.17.1-smack/libbb/smack_common.c  2011-08-18 17:17:53.730203208 -0700
-@@ -0,0 +1,178 @@
-+/*
-+ * libbb/smack_common.c
-+ *   -- common Smack utility functions
-+ *
-+ * Copyright 2007 Casey Schaufler <casey@schaufler-ca.com>
-+ */
-+#include "libbb.h"
-+#include <sys/xattr.h>
-+
-+#define       SMACKPROCPATH   "/proc/%d/attr/current"
-+#define SMACKUSERFILE "/etc/smack/user"
-+#define SMACKFILELINE 256
-+
-+int smack_from_file(const char *path, char *result, int len, int follow)
-+{
-+      char buffer[SMACKBUFFSIZE];
-+      int rc;
-+
-+      if (follow)
-+              rc = getxattr(path, SMACKATTR, buffer, SMACKBUFFSIZE - 1);
-+      else
-+              rc = lgetxattr(path, SMACKATTR, buffer, SMACKBUFFSIZE - 1);
-+
-+      if (rc < 0)
-+              return rc;
-+
-+      buffer[rc] = '\0';
-+      if (strlen(buffer) > len)
-+              return -EINVAL;
-+
-+      strcpy(result, buffer);
-+      return rc;
-+}
-+
-+int smack_from_proc(const int pid, char *result, int len)
-+{
-+      char path[SMACKFILELINE];
-+      char buffer[SMACKFILELINE];
-+      int fd;
-+      int i;
-+
-+      sprintf(path, SMACKPROCPATH, (pid < 0) ? getpid() : pid);
-+
-+      fd = open(path, O_RDONLY);
-+      if (fd < 0)
-+              return -EACCES;
-+
-+      i = read(fd, buffer, SMACKFILELINE);
-+      close(fd);
-+
-+      if (i <= 0 || i >= len)
-+              return -ERANGE;
-+      buffer[i] = '\0';
-+      strcpy(result, buffer);
-+      return 0;
-+}
-+
-+int smack_to_file(const char *path, const char *smack, int follow)
-+{
-+      if (follow)
-+              return setxattr(path, SMACKATTR, smack, strlen(smack)+1, 0);
-+      return lsetxattr(path, SMACKATTR, smack, strlen(smack)+1, 0);
-+}
-+
-+int smack_to_proc(const int pid, const char *smack)
-+{
-+      char path[SMACKFILELINE];
-+      int fd;
-+      int i;
-+      int slen;
-+
-+      slen = strlen(smack) + 1;
-+      if (slen >= SMACKBUFFSIZE)
-+              return -ERANGE;
-+
-+      sprintf(path, SMACKPROCPATH, (pid < 0) ? getpid() : pid);
-+
-+      fd = open(path, O_RDWR);
-+      if (fd < 0)
-+              return -EACCES;
-+
-+      i = write(fd, smack, slen);
-+      close(fd);
-+
-+      if (i != slen)
-+              return -EINVAL;
-+      return 0;
-+}
-+
-+int smack_user_default(const char *user, char *result, int len)
-+{
-+      char line[SMACKFILELINE];
-+      char ruser[SMACKFILELINE];
-+      char rsmack[SMACKFILELINE];
-+      FILE *fp;
-+
-+      fp = fopen(bb_path_smack_user, "r");
-+      if (fp == NULL)
-+              return -ENOENT;
-+
-+      while (fgets(line, SMACKFILELINE, fp) != NULL) {
-+              if (line[0] == '#' || line[0] == '\n')
-+                      continue;
-+              if (sscanf(line, "%s %s", ruser, rsmack) != 2)
-+                      continue;
-+              if (strcmp(ruser, user) != 0)
-+                      continue;
-+              if (strlen(rsmack) >= len)
-+                      return -ERANGE;
-+              strcpy(result, rsmack);
-+              return 0;
-+      }
-+      return -ENOENT;
-+}
-+
-+int smack_user_allowed(const char *user, const char *smack)
-+{
-+      char line[SMACKFILELINE];
-+      char *ruser;
-+      char *rsmack;
-+      FILE *fp;
-+      int rc = 1;
-+
-+      fp = fopen(SMACKUSERFILE, "r");
-+      if (fp == NULL)
-+              return -ENOENT;
-+
-+      while (rc == 1 && fgets(line, SMACKFILELINE, fp) != NULL) {
-+              if (line[0] == '#' || line[0] == '\n')
-+                      continue;
-+
-+              ruser = strtok(line, " \t\n");
-+              if (ruser == NULL)
-+                      continue;
-+
-+              if (strcmp(ruser, user) != 0)
-+                      continue;
-+
-+              while (rc == 1 && (rsmack = strtok(NULL, " \t\n")) != NULL) {
-+                      if (strcmp(rsmack, smack) == 0 || strcmp(rsmack, "+") == 0)
-+                              rc = 0;
-+              }
-+      }
-+      fclose(fp);
-+      return (rc == 1) ? -ENOENT : rc;
-+}
-+
-+int smack_user_add(const char *user, const char *smack)
-+{
-+      char line[SMACKFILELINE];
-+      char *ruser;
-+      FILE *fp;
-+      int rc = 0;
-+
-+      fp = fopen(SMACKUSERFILE, "a+");
-+      if (fp == NULL)
-+              return -ENOENT;
-+
-+      while (fgets(line, SMACKFILELINE, fp) != NULL) {
-+              if (line[0] == '#' || line[0] == '\n')
-+                      continue;
-+
-+              ruser = strtok(line, " \t\n");
-+              if (ruser == NULL)
-+                      continue;
-+
-+              if (strcmp(ruser, user) == 0) {
-+                      rc = -EEXIST;
-+                      break;
-+              }
-+      }
-+
-+      if (rc == 0)
-+              fprintf(fp, "%s %s\n", user, smack);
-+
-+      fclose(fp);
-+      return rc;
-+}
-diff -uprN busybox-1.17.1/loginutils/adduser.c busybox-1.17.1-smack/loginutils/adduser.c
---- busybox-1.17.1/loginutils/adduser.c        2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/loginutils/adduser.c  2011-08-17 15:27:12.909107701 -0700
-@@ -112,6 +112,7 @@ int adduser_main(int argc UNUSED_PARAM,
-       const char *usegroup = NULL;
-       char *p;
-       unsigned opts;
-+      IF_SMACK(const char *smack = SMACKSTAR;)
- #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
-       applet_long_options = adduser_longopts;
-@@ -130,10 +131,10 @@ int adduser_main(int argc UNUSED_PARAM,
-       /* disable interactive passwd for system accounts */
-       opt_complementary = "=1:SD:u+";
-       if (sizeof(pw.pw_uid) == sizeof(int)) {
--              opts = getopt32(argv, "h:g:s:G:DSHu:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &pw.pw_uid);
-+              opts = getopt32(argv, "h:g:s:G:DSHu:" IF_SMACK("L:"), &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &pw.pw_uid IF_SMACK(, &smack) );
-       } else {
-               unsigned uid;
--              opts = getopt32(argv, "h:g:s:G:DSHu:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &uid);
-+              opts = getopt32(argv, "h:g:s:G:DSHu:" IF_SMACK("L:"), &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &uid IF_SMACK(, &smack) );
-               if (opts & OPT_UID) {
-                       pw.pw_uid = uid;
-               }
-@@ -220,6 +221,14 @@ int adduser_main(int argc UNUSED_PARAM,
-                       bb_simple_perror_msg(pw.pw_dir);
-               }
-       }
-+#if ENABLE_SMACK
-+      if (smack_user_add(pw.pw_name, smack) < 0)
-+              bb_error_msg("cannot add user smack entry");
-+      if (strtok(smack, " \t") != NULL)
-+              if (smack_to_file(pw.pw_dir, smack, 0) != 0)
-+                      bb_error_msg("cannot set smack on home directory");
-+#endif
-+
-       if (!(opts & OPT_DONT_SET_PASS)) {
-               /* interactively set passwd */
-diff -uprN busybox-1.17.1/loginutils/login.c busybox-1.17.1-smack/loginutils/login.c
---- busybox-1.17.1/loginutils/login.c  2010-07-24 15:12:43.000000000 -0700
-+++ busybox-1.17.1-smack/loginutils/login.c    2011-08-17 15:27:12.909107701 -0700
-@@ -218,6 +218,7 @@ int login_main(int argc UNUSED_PARAM, ch
-       struct passwd pwdstruct;
-       char pwdbuf[256];
- #endif
-+      IF_SMACK(char smack[SMACKBUFFSIZE];)
-       username[0] = '\0';
-       signal(SIGALRM, alarm_handler);
-@@ -376,6 +377,13 @@ int login_main(int argc UNUSED_PARAM, ch
-               die_if_nologin();
-       IF_SELINUX(initselinux(username, full_tty, &user_sid));
-+#if ENABLE_SMACK
-+      if (smack_user_default(pw->pw_name, smack, SMACKBUFFSIZE) < 0)
-+              strcpy(smack, SMACKFLOOR);
-+      if (smack_to_proc(-1, smack) < 0)
-+              bb_perror_msg_and_die("cannot set smack");
-+#endif
-+
-       /* Try these, but don't complain if they fail.
-        * _f_chown is safe wrt race t=ttyname(0);...;chown(t); */
-diff -uprN busybox-1.17.1/loginutils/su.c busybox-1.17.1-smack/loginutils/su.c
---- busybox-1.17.1/loginutils/su.c     2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/loginutils/su.c       2011-08-18 15:13:07.830115065 -0700
-@@ -44,6 +44,7 @@ int su_main(int argc UNUSED_PARAM, char
-       const char *tty;
-       char user_buf[64];
-       const char *old_user;
-+      IF_SMACK(char smack[SMACKBUFFSIZE];)
-       flags = getopt32(argv, "mplc:s:", &opt_command, &opt_shell);
-       //argc -= optind;
-@@ -129,6 +130,13 @@ int su_main(int argc UNUSED_PARAM, char
-                       + (!(flags & SU_OPT_mp) * SETUP_ENV_CHANGEENV),
-                       pw);
-       IF_SELINUX(set_current_security_context(NULL);)
-+#if ENABLE_SMACK
-+      if (smack_from_proc(-1, smack, SMACKBUFFSIZE) < 0)
-+              bb_error_msg_and_die("cannot identify process smack");
-+      if (smack_user_allowed(pw->pw_name, smack) < 0)
-+              bb_error_msg_and_die("new usr not allowed current smack");
-+#endif
-+
-       /* Never returns */
-       run_shell(opt_shell, flags & SU_OPT_l, opt_command, (const char**)argv);
-diff -uprN busybox-1.17.1/loginutils/sulogin.c busybox-1.17.1-smack/loginutils/sulogin.c
---- busybox-1.17.1/loginutils/sulogin.c        2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/loginutils/sulogin.c  2011-08-17 15:27:12.909107701 -0700
-@@ -26,6 +26,7 @@ int sulogin_main(int argc UNUSED_PARAM,
-       char buffer[256];
-       struct spwd spw;
- #endif
-+      IF_SMACK(char smack[SMACKBUFFSIZE];)
-       logmode = LOGMODE_BOTH;
-       openlog(applet_name, 0, LOG_AUTH);
-@@ -97,6 +98,11 @@ int sulogin_main(int argc UNUSED_PARAM,
-       bb_info_msg("System Maintenance Mode");
-       IF_SELINUX(renew_current_security_context());
-+#if ENABLE_SMACK
-+      if (smack_to_proc(-1, SMACKFLOOR) < 0)
-+              bb_error_msg("login incorrect");
-+#endif
-+
-       shell = getenv("SUSHELL");
-       if (!shell)
-diff -uprN busybox-1.17.1/Makefile busybox-1.17.1-smack/Makefile
---- busybox-1.17.1/Makefile    2010-07-24 15:13:44.000000000 -0700
-+++ busybox-1.17.1-smack/Makefile      2011-08-17 15:27:12.909107701 -0700
-@@ -482,6 +482,7 @@ libs-y             := \
-               runit/ \
-               selinux/ \
-               shell/ \
-+              smack/ \
-               sysklogd/ \
-               util-linux/ \
-               util-linux/volume_id/ \
-diff -uprN busybox-1.17.1/miscutils/crond.c busybox-1.17.1-smack/miscutils/crond.c
---- busybox-1.17.1/miscutils/crond.c   2010-07-24 15:12:43.000000000 -0700
-+++ busybox-1.17.1-smack/miscutils/crond.c     2011-08-17 15:27:12.909107701 -0700
-@@ -906,6 +906,7 @@ static void RunJob(const char *user, Cro
- {
-       struct passwd *pas;
-       pid_t pid;
-+      IF_SMACK(char smack[SMACKBUFFSIZE];)
-       /* prepare things before vfork */
-       pas = getpwnam(user);
-@@ -919,6 +920,16 @@ static void RunJob(const char *user, Cro
-       pid = vfork();
-       if (pid == 0) {
-               /* CHILD */
-+#if ENABLE_SMACK
-+              if (smack_user_default(user, smack, SMACKBUFFSIZE) < 0) {
-+                      crondlog("\024user %s default smack unknown\n", user);
-+                      exit(0);
-+              }
-+              if (smack_to_proc(-1, smack) < 0) {
-+                      crondlog("\024user %s smack unsettable\n", user);
-+                      exit(0);
-+              }
-+#endif
-               /* change running state to the user in question */
-               ChangeUser(pas);
-               if (DebugOpt) {
-diff -uprN busybox-1.17.1/miscutils/crontab.c busybox-1.17.1-smack/miscutils/crontab.c
---- busybox-1.17.1/miscutils/crontab.c 2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/miscutils/crontab.c   2011-08-17 15:27:12.909107701 -0700
-@@ -77,6 +77,10 @@ int crontab_main(int argc UNUSED_PARAM,
-       int fd;
-       int src_fd;
-       int opt_ler;
-+#if ENABLE_SMACK
-+      char psmack[SMACKBUFFSIZE];
-+      char dsmack[SMACKBUFFSIZE];
-+#endif
-       /* file [opts]     Replace crontab from file
-        * - [opts]        Replace crontab from stdin
-@@ -120,6 +124,15 @@ int crontab_main(int argc UNUSED_PARAM,
-       if ((opt_ler - 1) & opt_ler) /* more than one bit set? */
-               bb_show_usage();
-+#if ENABLE_SMACK
-+      if (smack_user_default(pas->pw_name, dsmack, SMACKBUFFSIZE) < 0)
-+              bb_perror_msg_and_die("user default smack unknown");
-+      if (smack_from_proc(-1, psmack, SMACKBUFFSIZE) < 0)
-+              bb_perror_msg_and_die("user default smack unknown");
-+      if (strcmp(dsmack, psmack) != 0)
-+              bb_perror_msg_and_die("current smack is not default smack");
-+#endif
-+
-       /* Read replacement file under user's UID/GID/group vector */
-       src_fd = STDIN_FILENO;
-       if (!opt_ler) { /* Replace? */
-diff -uprN busybox-1.17.1/procps/ps.c busybox-1.17.1-smack/procps/ps.c
---- busybox-1.17.1/procps/ps.c 2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/procps/ps.c   2011-08-18 17:28:10.070210464 -0700
-@@ -25,7 +25,12 @@ enum { MAX_WIDTH = 2*1024 };
- #if ENABLE_SELINUX
- #define SELINUX_O_PREFIX "label,"
- #define DEFAULT_O_STR    (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args")
--#else
-+#endif
-+#if ENABLE_SMACK
-+#define SMACK_O_PREFIX "label,"
-+#define DEFAULT_O_STR    (SMACK_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args")
-+#endif
-+#if !ENABLE_SELINUX && !ENABLE_SMACK
- #define DEFAULT_O_STR    ("pid,user" IF_FEATURE_PS_TIME(",time") ",args")
- #endif
-@@ -288,6 +293,13 @@ static void func_label(char *buf, int si
- }
- #endif
-+#if ENABLE_SMACK
-+static void func_label(char *buf, int size, const procps_status_t *ps)
-+{
-+      safe_strncpy(buf, ps->smack[0] ? ps->smack : "?", size+1);
-+}
-+#endif
-+
- /*
- static void func_nice(char *buf, int size, const procps_status_t *ps)
- {
-@@ -327,6 +339,9 @@ static const ps_out_t out_spec[] = {
- #if ENABLE_SELINUX
-       { 35                 , "label" ,"LABEL"  ,func_label ,PSSCAN_CONTEXT },
- #endif
-+#if ENABLE_SMACK
-+      { 24                 , "label" ,"LABEL"  ,func_label ,PSSCAN_SMACK },
-+#endif
- };
- static ps_out_t* new_out_t(void)
-@@ -516,6 +531,12 @@ int ps_main(int argc UNUSED_PARAM, char
-                       strcpy(default_o, DEFAULT_O_STR + sizeof(SELINUX_O_PREFIX)-1);
-               } else
- #endif
-+#if ENABLE_SMACK
-+              if (!(opt & 1)) {
-+                      /* no -Z : do not show LABEL */
-+                      strcpy(default_o, DEFAULT_O_STR + sizeof(SMACK_O_PREFIX) -1);
-+              } else
-+#endif
-               {
-                       strcpy(default_o, DEFAULT_O_STR);
-               }
-@@ -557,17 +578,17 @@ int ps_main(int argc UNUSED_PARAM, char
-                       | PSSCAN_STATE | PSSCAN_VSZ | PSSCAN_COMM;
-       unsigned terminal_width IF_NOT_FEATURE_PS_WIDE(= 79);
-       enum {
--              OPT_Z = (1 << 0) * ENABLE_SELINUX,
--              OPT_T = (1 << ENABLE_SELINUX) * ENABLE_FEATURE_SHOW_THREADS,
-+              OPT_Z = (1 << 0) * (ENABLE_SELINUX | ENABLE_SMACK),
-+              OPT_T = (1 << (ENABLE_SELINUX | ENABLE_SMACK)) * ENABLE_FEATURE_SHOW_THREADS,
-       };
-       int opts = 0;
-       /* If we support any options, parse argv */
--#if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_PS_WIDE
-+#if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_PS_WIDE || ENABLE_SMACK
- # if ENABLE_FEATURE_PS_WIDE
-       /* -w is a bit complicated */
-       int w_count = 0;
-       opt_complementary = "-:ww";
--      opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")"w", &w_count);
-+      opts = getopt32(argv, IF_SELINUX("Z")IF_SMACK("Z")IF_FEATURE_SHOW_THREADS("T")"w", &w_count);
-       /* if w is given once, GNU ps sets the width to 132,
-        * if w is given more than once, it is "unlimited"
-        */
-@@ -582,7 +603,7 @@ int ps_main(int argc UNUSED_PARAM, char
- # else
-       /* -w is not supported, only -Z and/or -T */
-       opt_complementary = "-";
--      opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T"));
-+      opts = getopt32(argv, IF_SELINUX("Z")IF_SMACK("Z")IF_FEATURE_SHOW_THREADS("T"));
- # endif
- #endif
-@@ -593,6 +614,13 @@ int ps_main(int argc UNUSED_PARAM, char
-               puts("  PID CONTEXT                          STAT COMMAND");
-       } else
- #endif
-+#if ENABLE_SMACK
-+      if (opts & OPT_Z) {
-+              psscan_flags = PSSCAN_PID | PSSCAN_SMACK
-+                              | PSSCAN_STATE | PSSCAN_COMM;
-+              puts("  PID CONTEXT                          STAT COMMAND");
-+      } else
-+#endif
-       {
-               puts("  PID USER       VSZ STAT COMMAND");
-       }
-@@ -611,6 +639,14 @@ int ps_main(int argc UNUSED_PARAM, char
-                                       p->state);
-               } else
- #endif
-+#if ENABLE_SMACK
-+              if (psscan_flags & PSSCAN_SMACK) {
-+                      len = printf("%5u %-24.24s %s  ",
-+                                      p->pid,
-+                                      p->smack ? p->smack : "unknown",
-+                                      p->state);
-+              } else
-+#endif
-               {
-                       const char *user = get_cached_username(p->uid);
-                       //if (p->vsz == 0)
-diff -uprN busybox-1.17.1/smack/Config.src busybox-1.17.1-smack/smack/Config.src
---- busybox-1.17.1/smack/Config.src    1969-12-31 16:00:00.000000000 -0800
-+++ busybox-1.17.1-smack/smack/Config.src      2011-08-18 11:19:44.669950186 -0700
-@@ -0,0 +1,40 @@
-+#
-+# For a description of the syntax of this configuration file,
-+# see scripts/kbuild/config-language.txt.
-+#
-+
-+menu "Smack Utilities"
-+      depends on SMACK
-+
-+INSERT
-+
-+config SMACKLOAD
-+      bool "smackload"
-+      default n
-+      depends on SMACK
-+      help
-+        Enable support to load Smack access rules.
-+
-+config SMACKCIPSO
-+      bool "smackcipso"
-+      default n
-+      depends on SMACK
-+      help
-+        Enable support to specify Smack CIPSO mappings.
-+
-+config NEWSMACK
-+      bool "newsmack"
-+      default n
-+      depends on SMACK
-+      help
-+        Enable support to run command with specified Smack label.
-+
-+config SMACKENABLED
-+      bool "smackenabled"
-+      default n
-+      depends on SMACK
-+      help
-+        Enable support for this command to be used within shell scripts
-+        to determine if smack is enabled.
-+
-+endmenu
-diff -uprN busybox-1.17.1/smack/Kbuild.src busybox-1.17.1-smack/smack/Kbuild.src
---- busybox-1.17.1/smack/Kbuild.src    1969-12-31 16:00:00.000000000 -0800
-+++ busybox-1.17.1-smack/smack/Kbuild.src      2011-08-19 13:22:11.301054005 -0700
-@@ -0,0 +1,14 @@
-+# Makefile for busybox
-+#
-+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
-+# Copyright (C) 2007 by KaiGai Kohei <kaigai@kaigai.gr.jp>
-+#
-+# Licensed under the GPL v2, see the file LICENSE in this tarball.
-+
-+INSERT
-+
-+lib-y:=
-+lib-$(CONFIG_NEWSMACK)                += newsmack.o
-+lib-$(CONFIG_SMACKLOAD)               += smackload.o
-+lib-$(CONFIG_SMACKCIPSO)      += smackcipso.o
-+lib-$(CONFIG_SMACKENABLED)    += smackenabled.o
-diff -uprN busybox-1.17.1/smack/newsmack.c busybox-1.17.1-smack/smack/newsmack.c
---- busybox-1.17.1/smack/newsmack.c    1969-12-31 16:00:00.000000000 -0800
-+++ busybox-1.17.1-smack/smack/newsmack.c      2011-08-17 15:27:12.909107701 -0700
-@@ -0,0 +1,61 @@
-+/*
-+ * newsmack label [ command [ arg ] ... ]
-+ *
-+ * Port to busybox: Casey Schaufler <casey@schaufler-ca.com>
-+ *
-+ * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
-+ *
-+ */
-+
-+#include "libbb.h"
-+
-+int newsmack_main(int argc, char **argv);
-+int newsmack_main(int argc, char **argv)
-+{
-+      struct passwd *pwd;
-+      char *newargv[256]; /* yes, I know */
-+      char *smack;
-+      int i;
-+      int newargc;
-+
-+      if (argc <= 1)
-+              bb_error_msg_and_die("No smack value specified");
-+
-+      smack = argv[1];
-+
-+      /*
-+       * Start a shell if no command is specified
-+       */
-+      if (argc == 2) {
-+              fprintf(stderr, "%s: start a shell at \"%s\"\n",
-+                      argv[0], smack);
-+              newargv[0] = strdup("sh");
-+              newargv[1] = NULL;
-+      } else {
-+              for (newargc = 0, i = 2; i < argc; newargc++, i++)
-+                      newargv[newargc] = argv[i];
-+              newargv[newargc] = NULL;
-+      }
-+
-+      /*
-+       * Verify the user is allowed the Smack label
-+       */
-+      pwd = getpwuid(getuid());
-+      if (pwd == NULL)
-+              bb_error_msg_and_die("User name not obtained");
-+      if (smack_user_allowed(pwd->pw_name, smack) != 0)
-+              bb_error_msg_and_die("User not allowed this smack label");
-+      /*
-+       * Set the process label.
-+       */
-+      i = smack_to_proc(-1, smack);
-+      if (i < 0)
-+              bb_error_msg_and_die("Cannot set smack");
-+
-+      /*
-+       * Do the exec
-+       */
-+      execvp(newargv[0], newargv);
-+
-+      bb_error_msg_and_die("%s: exec failure.", newargv[0]);
-+}
-diff -uprN busybox-1.17.1/smack/smackcipso.c busybox-1.17.1-smack/smack/smackcipso.c
---- busybox-1.17.1/smack/smackcipso.c  1969-12-31 16:00:00.000000000 -0800
-+++ busybox-1.17.1-smack/smack/smackcipso.c    2011-08-17 15:27:12.909107701 -0700
-@@ -0,0 +1,119 @@
-+/*
-+ * smackcipso - properly format smack access cipsos for
-+ * loading into the kernel by writing to /smack/cipso.
-+ *
-+ * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
-+ *
-+ *    This program is free software; you can redistribute it and/or modify
-+ *    it under the terms of the GNU General Public License as published by
-+ *    the Free Software Foundation, version 2.
-+ *
-+ * Authors:
-+ *    Casey Schaufler <casey@schaufler-ca.com>
-+ *    Ahmed S. Darwish <darwish.07@gmail.com>
-+ *
-+ */
-+/*
-+#include <sys/types.h>
-+#include <sys/stat.h>
-+#include <fcntl.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <ctype.h>
-+*/
-+#include "libbb.h"
-+
-+#define LSIZE 23
-+#define NSIZE 4
-+#define MAXCATNUM 239
-+#define MAXCATVAL 63
-+#define MAXLEVEL 255
-+
-+int smackcipso_main(int argc, char **argv);
-+int smackcipso_main(int argc, char **argv)
-+{
-+      int status = EXIT_SUCCESS;
-+      int cipsofd;
-+      char line[512];
-+      char cipso[LSIZE + NSIZE + NSIZE + (NSIZE * MAXCATNUM)];
-+      char cats[MAXCATNUM+1][NSIZE+1];
-+      char *cp;
-+      int level;
-+      int cat;
-+      int catcount;
-+      int i;
-+      int err;
-+
-+      cipsofd = open("/smack/cipso", O_RDWR);
-+      if (cipsofd < 0)
-+              bb_error_msg_and_die("failed opening /smack/cipso");
-+
-+      while (fgets(line, sizeof(line), stdin) != NULL) {
-+              catcount = 0;
-+              err = 0;
-+
-+              if ((cp = strchr(line, '\n')) == NULL) {
-+                      fprintf(stderr, "missing newline \"%s\"\n", line);
-+                      continue;
-+              }
-+              *cp = '\0';
-+              cp = strtok(line, " \t");
-+              if (cp == NULL) {
-+                      fprintf(stderr, "Empty line: \"%s\"\n", line);
-+                      continue;
-+              }
-+              sprintf(cipso, "%-23s ", line);
-+              if (strlen(cipso) != 24) {
-+                      fprintf(stderr, "Bad label starting: \"%s\"\n", line);
-+                      continue;
-+              }
-+              cp = strtok(NULL, " \t");
-+              if (cp == NULL) {
-+                      fprintf(stderr, "Missing level: \"%s\"\n", line);
-+                      continue;
-+              }
-+              if (!isdigit(*cp)) {
-+                      fprintf(stderr, "Bad level: \"%s\"\n", cp);
-+                      continue;
-+              }
-+              level = atoi(cp);
-+              if (level > MAXLEVEL) {
-+                      fprintf(stderr, "Bad level: \"%s\"\n", cp);
-+                      continue;
-+              }
-+              sprintf(cipso+LSIZE+1, "%-4d", level);
-+
-+              cp = strtok(NULL, " \t");
-+              for (i = 0; cp != NULL; cp = strtok(NULL, " \t"), i++) {
-+                      if (!isdigit(*cp)) {
-+                              fprintf(stderr, "Bad category \"%s\"\n", cp);
-+                              err = 1;
-+                              break;
-+                      }
-+                      cat = atoi(cp);
-+                      if (i >= MAXCATNUM) {
-+                              fprintf(stderr, "Maximum number of categories"
-+                                      "exceeded \"%d\"\n", i);
-+                              err = 1;
-+                              break;
-+                      }
-+                      if (cat > MAXCATVAL) {
-+                              fprintf(stderr, "Bad category \"%s\"\n", cp);
-+                              err = 1;
-+                              break;
-+                      }
-+                      sprintf(cats[i], "%-4d", cat);
-+              }
-+              if (err)
-+                      continue;
-+
-+              sprintf(cipso+LSIZE+1+NSIZE, "%-4d", i);
-+              while (i > 0)
-+                      strcat(cipso, cats[--i]);
-+              err = write(cipsofd, cipso, strlen(cipso));
-+              if (err < 0)
-+                      perror("writing /smack/cipso");
-+      }
-+      return status;
-+}
-diff -uprN busybox-1.17.1/smack/smackenabled.c busybox-1.17.1-smack/smack/smackenabled.c
---- busybox-1.17.1/smack/smackenabled.c        1969-12-31 16:00:00.000000000 -0800
-+++ busybox-1.17.1-smack/smack/smackenabled.c  2011-08-17 15:27:12.909107701 -0700
-@@ -0,0 +1,17 @@
-+/*
-+ * smackenabled
-+ *
-+ * Port to BusyBox  Casey Schaufler <casey@schaufler-ca.com
-+ *
-+ */
-+#include "libbb.h"
-+
-+int smackenabled_main(int argc, char **argv);
-+int smackenabled_main(int argc, char **argv)
-+{
-+      char buffer[80];
-+
-+      if (smack_from_file("/", buffer, sizeof(buffer), 0) < 0)
-+              return 0;
-+      return 1;
-+}
-diff -uprN busybox-1.17.1/smack/smackload.c busybox-1.17.1-smack/smack/smackload.c
---- busybox-1.17.1/smack/smackload.c   1969-12-31 16:00:00.000000000 -0800
-+++ busybox-1.17.1-smack/smack/smackload.c     2011-08-17 15:27:12.909107701 -0700
-@@ -0,0 +1,93 @@
-+/*
-+ * smackload - properly format smack access rules for
-+ * loading into the kernel by writing to /smack/load.
-+ *
-+ * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
-+ *
-+ *    This program is free software; you can redistribute it and/or modify
-+ *    it under the terms of the GNU General Public License as published by
-+ *    the Free Software Foundation, version 2.
-+ *
-+ * Author:
-+ *    Casey Schaufler <casey@schaufler-ca.com>
-+ */
-+/*
-+#include <sys/types.h>
-+#include <sys/stat.h>
-+#include <fcntl.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+*/
-+#include "libbb.h"
-+
-+#define LSIZE 23
-+#define ASIZE 4
-+
-+int smackload_main(int argc, char **argv);
-+int smackload_main(int argc, char **argv)
-+{
-+      int status = EXIT_SUCCESS;
-+      int loadfd;
-+      char line[80];
-+      char rule[LSIZE + LSIZE + ASIZE + 3];
-+      char subject[LSIZE+1];
-+      char object[LSIZE+1];
-+      char accesses[ASIZE+1];
-+      char real[ASIZE+1];
-+      char *cp;
-+      int i;
-+      int err;
-+
-+      loadfd = open("/smack/load", O_RDWR);
-+      if (loadfd < 0)
-+              bb_error_msg_and_die("failed opening /smack/load");
-+
-+      while (fgets(line, 80, stdin) != NULL) {
-+              err = 0;
-+              if ((cp = strchr(line, '\n')) != NULL)
-+                      *cp = '\0';
-+              if (sscanf(line,"%23s %23s %4s",subject,object,accesses) != 3) 
-+                      err = 1;
-+              else {
-+                      strcpy(real, "----");
-+                      for (i = 0;
-+                           i < ASIZE && accesses[i] != '\0' && err == 0;
-+                           i++) {
-+                              switch (accesses[i]) {
-+                              case 'r':
-+                              case 'R':
-+                                      real[0] = 'r';
-+                                      break;
-+                              case 'w':
-+                              case 'W':
-+                                      real[1] = 'w';
-+                                      break;
-+                              case 'x':
-+                              case 'X':
-+                                      real[2] = 'x';
-+                                      break;
-+                              case 'a':
-+                              case 'A':
-+                                      real[3] = 'a';
-+                                      break;
-+                              case '\0':
-+                              case '-':
-+                                      break;
-+                              default:
-+                                      err = 1;
-+                                      break;
-+                              }
-+                      }
-+              }
-+              if (err == 0) {
-+                      sprintf(rule, "%-23s %-23s %4s", subject,object,real);
-+                      err = write(loadfd, rule, LSIZE + LSIZE + ASIZE + 2);
-+                      if (err < 0)
-+                              perror("writing /smack/load");
-+              }
-+              else
-+                      fprintf(stderr, "Bad input line \"%s\"\n", line);
-+      }
-+      return status;
-+}
diff --git a/debian/patches/smack-conflict-with-selinux.patch b/debian/patches/smack-conflict-with-selinux.patch
deleted file mode 100644 (file)
index 8e9d425..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-From 0bfd7de40919494aa4a2cada5ca4c475d1a87b66 Mon Sep 17 00:00:00 2001
-From: Karol Lewandowski <k.lewandowsk@samsung.com>
-Date: Wed, 11 Jan 2012 11:21:15 +0100
-Subject: [PATCH] Don't allow SELINUX and SMACK to be enabled at the same time
-
-Source code simply doesn't support it.
----
- Config.in |    1 +
- 1 files changed, 1 insertions(+), 0 deletions(-)
-
-diff --git a/Config.in b/Config.in
-index 76c2b68..cac552c 100644
---- a/Config.in
-+++ b/Config.in
-@@ -385,6 +385,7 @@ config SELINUX
- config SMACK
-       bool "Support Smack"
-       default n
-+      depends on !SELINUX
-       help
-         Enable support for Smack in applets ls, ps, and id.  Also provide
-         the option of compiling in Smack applets.
--- 
-1.7.7.3
-
diff --git a/debian/patches/strip.patch b/debian/patches/strip.patch
deleted file mode 100644 (file)
index 1188e67..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
---- a/Makefile
-+++ b/Makefile
-@@ -690,20 +690,10 @@
- endif # ifdef CONFIG_KALLSYMS
- # busybox image - including updated kernel symbols
--busybox_unstripped: $(busybox-all) FORCE
-+busybox: $(busybox-all) FORCE
-       $(call if_changed_rule,busybox__)
-       $(Q)rm -f .old_version
--busybox: busybox_unstripped
--ifeq ($(SKIP_STRIP),y)
--      $(Q)cp $< $@
--else
--      $(Q)$(STRIP) -s --remove-section=.note --remove-section=.comment \
--              busybox_unstripped -o $@
--# strip is confused by PIE executable and does not set exec bits
--      $(Q)chmod a+x $@
--endif
--
- # The actual objects are generated when descending,
- # make sure no implicit rule kicks in
- $(sort $(busybox-all)): $(busybox-dirs) ;
diff --git a/debian/patches/stty-sort-out-preprocessor-conditionals.patch b/debian/patches/stty-sort-out-preprocessor-conditionals.patch
deleted file mode 100644 (file)
index e078f3d..0000000
+++ /dev/null
@@ -1,705 +0,0 @@
-From 138ce54c9c1930348bc842be781accd7c50c2cef Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Fri, 30 Jul 2010 06:01:37 +0200
-Subject: [PATCH 2/2] stty: sort out preprocessor conditionals
-
-* Move the definitions of missing constants to the top of the file.
-* Fix undefined IDX_xxx on missing termios constants.
-* FreeBSD has TABDLY, TAB0 and TAB3, but no TAB1 or TAB2
-* Omit the definition of set_window_size() if TIOCGWINSZ is not available.
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- coreutils/Config.src |    1 -
- coreutils/stty.c     |  339 +++++++++++++++++++++++++++++++------------------
- 2 files changed, 214 insertions(+), 126 deletions(-)
-
-diff --git a/coreutils/Config.src b/coreutils/Config.src
-index 780b73f..0eb70af 100644
---- a/coreutils/Config.src
-+++ b/coreutils/Config.src
-@@ -607,7 +607,6 @@ config FEATURE_STAT_FORMAT
- config STTY
-       bool "stty"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         stty is used to change and print terminal line settings.
-diff --git a/coreutils/stty.c b/coreutils/stty.c
-index c40d718..33f7b21 100644
---- a/coreutils/stty.c
-+++ b/coreutils/stty.c
-@@ -115,6 +115,113 @@
- # define CSTATUS Control('t')
- #endif
-+/* Save us from #ifdef forest plague */
-+#ifndef BSDLY
-+# define BSDLY 0
-+#endif
-+#ifndef CIBAUD
-+# define CIBAUD 0
-+#endif
-+#ifndef CRDLY
-+# define CRDLY 0
-+#endif
-+#ifndef CRTSCTS
-+# define CRTSCTS 0
-+#endif
-+#ifndef ECHOCTL
-+# define ECHOCTL 0
-+#endif
-+#ifndef ECHOKE
-+# define ECHOKE 0
-+#endif
-+#ifndef ECHOPRT
-+# define ECHOPRT 0
-+#endif
-+#ifndef FFDLY
-+# define FFDLY 0
-+#endif
-+#ifndef IEXTEN
-+# define IEXTEN 0
-+#endif
-+#ifndef IMAXBEL
-+# define IMAXBEL 0
-+#endif
-+#ifndef IUCLC
-+# define IUCLC 0
-+#endif
-+#ifndef IXANY
-+# define IXANY 0
-+#endif
-+#ifndef NLDLY
-+# define NLDLY 0
-+#endif
-+#ifndef OCRNL
-+# define OCRNL 0
-+#endif
-+#ifndef OFDEL
-+# define OFDEL 0
-+#endif
-+#ifndef OFILL
-+# define OFILL 0
-+#endif
-+#ifndef OLCUC
-+# define OLCUC 0
-+#endif
-+#ifndef ONLCR
-+# define ONLCR 0
-+#endif
-+#ifndef ONLRET
-+# define ONLRET 0
-+#endif
-+#ifndef ONOCR
-+# define ONOCR 0
-+#endif
-+#ifndef OXTABS
-+# define OXTABS 0
-+#endif
-+#ifndef TABDLY
-+# define TABDLY 0
-+#endif
-+#ifndef TAB1
-+# define TAB1 0
-+#endif
-+#ifndef TAB2
-+# define TAB2 0
-+#endif
-+#ifndef TOSTOP
-+# define TOSTOP 0
-+#endif
-+#ifndef VDSUSP
-+# define VDSUSP 0
-+#endif
-+#ifndef VEOL2
-+# define VEOL2 0
-+#endif
-+#ifndef VFLUSHO
-+# define VFLUSHO 0
-+#endif
-+#ifndef VLNEXT
-+# define VLNEXT 0
-+#endif
-+#ifndef VREPRINT
-+# define VREPRINT 0
-+#endif
-+#ifndef VSTATUS
-+# define VSTATUS 0
-+#endif
-+#ifndef VSWTCH
-+# define VSWTCH 0
-+#endif
-+#ifndef VTDLY
-+# define VTDLY 0
-+#endif
-+#ifndef VWERASE
-+# define VWERASE 0
-+#endif
-+#ifndef XCASE
-+# define XCASE 0
-+#endif
-+
- /* Which speeds to set */
- enum speed_setting {
-       input_speed, output_speed, both_speeds
-@@ -167,13 +274,13 @@ enum {
-       IDX_cbreak,
-       IDX_crt,
-       IDX_dec,
--#ifdef IXANY
-+#if IXANY
-       IDX_decctlq,
- #endif
--#if defined(TABDLY) || defined(OXTABS)
-+#if TABDLY || OXTABS
-       IDX_tabs,
- #endif
--#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
-+#if XCASE && IUCLC && OLCUC
-       IDX_lcase,
-       IDX_LCASE,
- #endif
-@@ -196,13 +303,13 @@ static const char mode_name[] =
-       MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
-       MI_ENTRY("crt",      combination, OMIT,              0,          0 )
-       MI_ENTRY("dec",      combination, OMIT,              0,          0 )
--#ifdef IXANY
-+#if IXANY
-       MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
- #endif
--#if defined(TABDLY) || defined(OXTABS)
-+#if TABDLY || OXTABS
-       MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
- #endif
--#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
-+#if XCASE && IUCLC && OLCUC
-       MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
-       MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
- #endif
-@@ -217,7 +324,7 @@ static const char mode_name[] =
-       MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
-       MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
-       MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
--#ifdef CRTSCTS
-+#if CRTSCTS
-       MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
- #endif
-       MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
-@@ -232,74 +339,78 @@ static const char mode_name[] =
-       MI_ENTRY("ixon",     input,       REV,               IXON,       0 )
-       MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 )
-       MI_ENTRY("tandem",   input,       REV        | OMIT, IXOFF,      0 )
--#ifdef IUCLC
-+#if IUCLC
-       MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
- #endif
--#ifdef IXANY
-+#if IXANY
-       MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
- #endif
--#ifdef IMAXBEL
-+#if IMAXBEL
-       MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 )
- #endif
-       MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 )
--#ifdef OLCUC
-+#if OLCUC
-       MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
- #endif
--#ifdef OCRNL
-+#if OCRNL
-       MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
- #endif
--#ifdef ONLCR
-+#if ONLCR
-       MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
- #endif
--#ifdef ONOCR
-+#if ONOCR
-       MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
- #endif
--#ifdef ONLRET
-+#if ONLRET
-       MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
- #endif
--#ifdef OFILL
-+#if OFILL
-       MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
- #endif
--#ifdef OFDEL
-+#if OFDEL
-       MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
- #endif
--#ifdef NLDLY
-+#if NLDLY
-       MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
-       MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
- #endif
--#ifdef CRDLY
-+#if CRDLY
-       MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY)
-       MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY)
-       MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY)
-       MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY)
- #endif
--#ifdef TABDLY
-+#if TABDLY
-       MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY)
-+# if TAB2
-       MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY)
-+# endif
-+# if TAB1
-       MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY)
-+# endif
-       MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY)
- #else
--# ifdef OXTABS
-+# if OXTABS
-       MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
- # endif
- #endif
--#ifdef BSDLY
-+#if BSDLY
-       MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
-       MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
- #endif
--#ifdef VTDLY
-+#if VTDLY
-       MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
-       MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
- #endif
--#ifdef FFDLY
-+#if FFDLY
-       MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY)
-       MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY)
- #endif
-       MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 )
-       MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 )
--#ifdef IEXTEN
-+#if IEXTEN
-       MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 )
- #endif
-       MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 )
-@@ -308,21 +419,21 @@ static const char mode_name[] =
-       MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 )
-       MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 )
-       MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 )
--#ifdef XCASE
-+#if XCASE
-       MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
- #endif
--#ifdef TOSTOP
-+#if TOSTOP
-       MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
- #endif
--#ifdef ECHOPRT
-+#if ECHOPRT
-       MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
-       MI_ENTRY("prterase", local,       REV | OMIT,        ECHOPRT,    0 )
- #endif
--#ifdef ECHOCTL
-+#if ECHOCTL
-       MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
-       MI_ENTRY("ctlecho",  local,       REV        | OMIT, ECHOCTL,    0 )
- #endif
--#ifdef ECHOKE
-+#if ECHOKE
-       MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
-       MI_ENTRY("crtkill",  local,       REV        | OMIT, ECHOKE,     0 )
- #endif
-@@ -346,13 +457,13 @@ static const struct mode_info mode_info[] = {
-       MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
-       MI_ENTRY("crt",      combination, OMIT,              0,          0 )
-       MI_ENTRY("dec",      combination, OMIT,              0,          0 )
--#ifdef IXANY
-+#if IXANY
-       MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
- #endif
--#if defined(TABDLY) || defined(OXTABS)
-+#if TABDLY || OXTABS
-       MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
- #endif
--#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
-+#if XCASE && IUCLC && OLCUC
-       MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
-       MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
- #endif
-@@ -367,7 +478,7 @@ static const struct mode_info mode_info[] = {
-       MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
-       MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
-       MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
--#ifdef CRTSCTS
-+#if CRTSCTS
-       MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
- #endif
-       MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
-@@ -382,74 +493,78 @@ static const struct mode_info mode_info[] = {
-       MI_ENTRY("ixon",     input,       REV,               IXON,       0 )
-       MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 )
-       MI_ENTRY("tandem",   input,       REV        | OMIT, IXOFF,      0 )
--#ifdef IUCLC
-+#if IUCLC
-       MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
- #endif
--#ifdef IXANY
-+#if IXANY
-       MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
- #endif
--#ifdef IMAXBEL
-+#if IMAXBEL
-       MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 )
- #endif
-       MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 )
--#ifdef OLCUC
-+#if OLCUC
-       MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
- #endif
--#ifdef OCRNL
-+#if OCRNL
-       MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
- #endif
--#ifdef ONLCR
-+#if ONLCR
-       MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
- #endif
--#ifdef ONOCR
-+#if ONOCR
-       MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
- #endif
--#ifdef ONLRET
-+#if ONLRET
-       MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
- #endif
--#ifdef OFILL
-+#if OFILL
-       MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
- #endif
--#ifdef OFDEL
-+#if OFDEL
-       MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
- #endif
--#ifdef NLDLY
-+#if NLDLY
-       MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
-       MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
- #endif
--#ifdef CRDLY
-+#if CRDLY
-       MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY)
-       MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY)
-       MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY)
-       MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY)
- #endif
--#ifdef TABDLY
-+#if TABDLY
-       MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY)
-+# if TAB2
-       MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY)
-+# endif
-+# if TAB1
-       MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY)
-+# endif
-       MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY)
- #else
--# ifdef OXTABS
-+# if OXTABS
-       MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
- # endif
- #endif
--#ifdef BSDLY
-+#if BSDLY
-       MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
-       MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
- #endif
--#ifdef VTDLY
-+#if VTDLY
-       MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
-       MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
- #endif
--#ifdef FFDLY
-+#if FFDLY
-       MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY)
-       MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY)
- #endif
-       MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 )
-       MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 )
--#ifdef IEXTEN
-+#if IEXTEN
-       MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 )
- #endif
-       MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 )
-@@ -458,21 +573,21 @@ static const struct mode_info mode_info[] = {
-       MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 )
-       MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 )
-       MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 )
--#ifdef XCASE
-+#if XCASE
-       MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
- #endif
--#ifdef TOSTOP
-+#if TOSTOP
-       MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
- #endif
--#ifdef ECHOPRT
-+#if ECHOPRT
-       MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
-       MI_ENTRY("prterase", local,       REV | OMIT,        ECHOPRT,    0 )
- #endif
--#ifdef ECHOCTL
-+#if ECHOCTL
-       MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
-       MI_ENTRY("ctlecho",  local,       REV        | OMIT, ECHOCTL,    0 )
- #endif
--#ifdef ECHOKE
-+#if ECHOKE
-       MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
-       MI_ENTRY("crtkill",  local,       REV        | OMIT, ECHOKE,     0 )
- #endif
-@@ -497,31 +612,31 @@ enum {
-       CIDX_kill,
-       CIDX_eof,
-       CIDX_eol,
--#ifdef VEOL2
-+#if VEOL2
-       CIDX_eol2,
- #endif
--#ifdef VSWTCH
-+#if VSWTCH
-       CIDX_swtch,
- #endif
-       CIDX_start,
-       CIDX_stop,
-       CIDX_susp,
--#ifdef VDSUSP
-+#if VDSUSP
-       CIDX_dsusp,
- #endif
--#ifdef VREPRINT
-+#if VREPRINT
-       CIDX_rprnt,
- #endif
--#ifdef VWERASE
-+#if VWERASE
-       CIDX_werase,
- #endif
--#ifdef VLNEXT
-+#if VLNEXT
-       CIDX_lnext,
- #endif
--#ifdef VFLUSHO
-+#if VFLUSHO
-       CIDX_flush,
- #endif
--#ifdef VSTATUS
-+#if VSTATUS
-       CIDX_status,
- #endif
-       CIDX_min,
-@@ -538,31 +653,31 @@ static const char control_name[] =
-       CI_ENTRY("kill",     CKILL,   VKILL   )
-       CI_ENTRY("eof",      CEOF,    VEOF    )
-       CI_ENTRY("eol",      CEOL,    VEOL    )
--#ifdef VEOL2
-+#if VEOL2
-       CI_ENTRY("eol2",     CEOL2,   VEOL2   )
- #endif
--#ifdef VSWTCH
-+#if VSWTCH
-       CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
- #endif
-       CI_ENTRY("start",    CSTART,  VSTART  )
-       CI_ENTRY("stop",     CSTOP,   VSTOP   )
-       CI_ENTRY("susp",     CSUSP,   VSUSP   )
--#ifdef VDSUSP
-+#if VDSUSP
-       CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
- #endif
--#ifdef VREPRINT
-+#if VREPRINT
-       CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
- #endif
--#ifdef VWERASE
-+#if VWERASE
-       CI_ENTRY("werase",   CWERASE, VWERASE )
- #endif
--#ifdef VLNEXT
-+#if VLNEXT
-       CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
- #endif
--#ifdef VFLUSHO
-+#if VFLUSHO
-       CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
- #endif
--#ifdef VSTATUS
-+#if VSTATUS
-       CI_ENTRY("status",   CSTATUS, VSTATUS )
- #endif
-       /* These must be last because of the display routines */
-@@ -581,31 +696,31 @@ static const struct control_info control_info[] = {
-       CI_ENTRY("kill",     CKILL,   VKILL   )
-       CI_ENTRY("eof",      CEOF,    VEOF    )
-       CI_ENTRY("eol",      CEOL,    VEOL    )
--#ifdef VEOL2
-+#if VEOL2
-       CI_ENTRY("eol2",     CEOL2,   VEOL2   )
- #endif
--#ifdef VSWTCH
-+#if VSWTCH
-       CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
- #endif
-       CI_ENTRY("start",    CSTART,  VSTART  )
-       CI_ENTRY("stop",     CSTOP,   VSTOP   )
-       CI_ENTRY("susp",     CSUSP,   VSUSP   )
--#ifdef VDSUSP
-+#if VDSUSP
-       CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
- #endif
--#ifdef VREPRINT
-+#if VREPRINT
-       CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
- #endif
--#ifdef VWERASE
-+#if VWERASE
-       CI_ENTRY("werase",   CWERASE, VWERASE )
- #endif
--#ifdef VLNEXT
-+#if VLNEXT
-       CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
- #endif
--#ifdef VFLUSHO
-+#if VFLUSHO
-       CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
- #endif
--#ifdef VSTATUS
-+#if VSTATUS
-       CI_ENTRY("status",   CSTATUS, VSTATUS )
- #endif
-       /* These must be last because of the display routines */
-@@ -740,6 +855,7 @@ static void newline(void)
-               wrapf("\n");
- }
-+#ifdef TIOCGWINSZ
- static void set_window_size(int rows, int cols)
- {
-       struct winsize win = { 0, 0, 0, 0 };
-@@ -760,6 +876,7 @@ static void set_window_size(int rows, int cols)
- bail:
-               perror_on_device("%s");
- }
-+#endif
- static void display_window_size(int fancy)
- {
-@@ -973,41 +1090,6 @@ static void sane_mode(struct termios *mode)
-       }
- }
--/* Save set_mode from #ifdef forest plague */
--#ifndef ONLCR
--#define ONLCR 0
--#endif
--#ifndef OCRNL
--#define OCRNL 0
--#endif
--#ifndef ONLRET
--#define ONLRET 0
--#endif
--#ifndef XCASE
--#define XCASE 0
--#endif
--#ifndef IXANY
--#define IXANY 0
--#endif
--#ifndef TABDLY
--#define TABDLY 0
--#endif
--#ifndef OXTABS
--#define OXTABS 0
--#endif
--#ifndef IUCLC
--#define IUCLC 0
--#endif
--#ifndef OLCUC
--#define OLCUC 0
--#endif
--#ifndef ECHOCTL
--#define ECHOCTL 0
--#endif
--#ifndef ECHOKE
--#define ECHOKE 0
--#endif
--
- static void set_mode(const struct mode_info *info, int reversed,
-                                       struct termios *mode)
- {
-@@ -1093,27 +1175,32 @@ static void set_mode(const struct mode_info *info, int reversed,
-                       mode->c_cc[VTIME] = 0;
-               }
-       }
--      else if (IXANY && info == &mode_info[IDX_decctlq]) {
-+#if IXANY
-+      else if (info == &mode_info[IDX_decctlq]) {
-               if (reversed)
-                       mode->c_iflag |= IXANY;
-               else
-                       mode->c_iflag &= ~IXANY;
-       }
--      else if (TABDLY && info == &mode_info[IDX_tabs]) {
-+#endif
-+#if TABDLY
-+      else if (info == &mode_info[IDX_tabs]) {
-               if (reversed)
-                       mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
-               else
-                       mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
-       }
--      else if (OXTABS && info == &mode_info[IDX_tabs]) {
-+#endif
-+#if OXTABS
-+      else if (info == &mode_info[IDX_tabs]) {
-               if (reversed)
-                       mode->c_oflag |= OXTABS;
-               else
-                       mode->c_oflag &= ~OXTABS;
--      } else
--      if (XCASE && IUCLC && OLCUC
--       && (info == &mode_info[IDX_lcase] || info == &mode_info[IDX_LCASE])
--      ) {
-+      }
-+#endif
-+#if XCASE && IUCLC && OLCUC
-+      else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) {
-               if (reversed) {
-                       mode->c_lflag &= ~XCASE;
-                       mode->c_iflag &= ~IUCLC;
-@@ -1123,7 +1210,9 @@ static void set_mode(const struct mode_info *info, int reversed,
-                       mode->c_iflag |= IUCLC;
-                       mode->c_oflag |= OLCUC;
-               }
--      } else if (info == &mode_info[IDX_crt]) {
-+      }
-+#endif
-+      else if (info == &mode_info[IDX_crt]) {
-               mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
-       } else if (info == &mode_info[IDX_dec]) {
-               mode->c_cc[VINTR] = 3; /* ^C */
-@@ -1419,7 +1508,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
-                       perror_on_device_and_die("%s");
-               if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
--#ifdef CIBAUD
-+#if CIBAUD
-                       /* SunOS 4.1.3 (at least) has the problem that after this sequence,
-                          tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
-                          sometimes (m1 != m2).  The only difference is in the four bits
--- 
-1.7.1
-
diff --git a/debian/patches/swaponoff-FreeBSD-support.patch b/debian/patches/swaponoff-FreeBSD-support.patch
deleted file mode 100644 (file)
index da273f5..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-From a5b837c34a96bdbb53151af455912b691c9aaa52 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 29 Jul 2010 21:59:54 +0200
-Subject: [PATCH 19/19] swaponoff: FreeBSD support
-
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
----
- util-linux/Config.src  |    3 +--
- util-linux/swaponoff.c |    6 +++---
- util-linux/xmount.c    |   10 ++++++++++
- util-linux/xmount.h    |   16 ++++++++++------
- 4 files changed, 24 insertions(+), 11 deletions(-)
-
-diff --git a/util-linux/Config.src b/util-linux/Config.src
-index 99a6fbe..cb4de95 100644
---- a/util-linux/Config.src
-+++ b/util-linux/Config.src
-@@ -639,7 +639,6 @@ config SETARCH
- config SWAPONOFF
-       bool "swaponoff"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         This option enables both the 'swapon' and the 'swapoff' utilities.
-         Once you have created some swap space using 'mkswap', you also need
-@@ -651,7 +650,7 @@ config SWAPONOFF
- config FEATURE_SWAPON_PRI
-       bool "Support priority option -p"
-       default y
--      depends on SWAPONOFF
-+      depends on SWAPONOFF && PLATFORM_LINUX
-       help
-         Enable support for setting swap device priority in swapon.
-diff --git a/util-linux/swaponoff.c b/util-linux/swaponoff.c
-index f2f52fb..d13c37e 100644
---- a/util-linux/swaponoff.c
-+++ b/util-linux/swaponoff.c
-@@ -8,8 +8,8 @@
-  */
- #include "libbb.h"
-+#include "xmount.h"
- #include <mntent.h>
--#include <sys/swap.h>
- #if ENABLE_FEATURE_MOUNT_LABEL
- # include "volume_id.h"
-@@ -43,9 +43,9 @@ static int swap_enable_disable(char *device)
- #endif
-       if (applet_name[5] == 'n')
--              status = swapon(device, g_flags);
-+              status = xswapon(device, g_flags);
-       else
--              status = swapoff(device);
-+              status = xswapoff(device);
-       if (status != 0) {
-               bb_simple_perror_msg(device);
-diff --git a/util-linux/xmount.c b/util-linux/xmount.c
-index 3f322b8..16543f1 100644
---- a/util-linux/xmount.c
-+++ b/util-linux/xmount.c
-@@ -63,4 +63,14 @@ int FAST_FUNC xumount(const char *target, int flags)
-       return unmount(target, flags);
- }
-+int FAST_FUNC xswapon(const char *path, int swapflags UNUSED_PARAM)
-+{
-+      return swapon(path);
-+}
-+
-+int FAST_FUNC xswapoff(const char *path)
-+{
-+      return swapoff(path);
-+}
-+
- #endif
-diff --git a/util-linux/xmount.h b/util-linux/xmount.h
-index caef564..bcd6d18 100644
---- a/util-linux/xmount.h
-+++ b/util-linux/xmount.h
-@@ -5,9 +5,9 @@
-  * Copyright (C) 2010 by Jeremie Koenig <jk@jk.fr.eu.org>
-  * Copyright (C) 2010 by Luca Favatella <slackydeb@gmail.com>
-  *
-- * The Linux prototypes for mount() and umount2() are used as a reference for
-- * our xmount() and xumount(), which should be implemented as a compatibility
-- * wrappers for non-Linux systems (see xmount.c).
-+ * The Linux prototypes for mount(), umount2(), swapon() and swapoff()  are
-+ * used as a reference for our versions of them. On non-Linux system those
-+ * should be implemented as compatibility wrappers (see xmount.c).
-  */
- /*
-@@ -17,6 +17,7 @@
- #ifdef __linux__
- # include <sys/mount.h>
-+# include <sys/swap.h>
- /* Make sure we have all the new mount flags we actually try to use
-  * (grab more as needed from util-linux's mount/mount_constants.h). */
- # ifndef MS_DIRSYNC
-@@ -56,6 +57,7 @@
- #elif defined(__FreeBSD_kernel__)
- # include <sys/mount.h>
-+# include <sys/swap.h>
- # define MS_NOSUID      MNT_NOSUID
- # define MS_NODEV       MNT_NODEV
- # define MS_NOEXEC      MNT_NOEXEC
-@@ -82,16 +84,18 @@
- #endif
- /*
-- * Prototypes for xmount() and xumount(): on Linux we use the system calls
-- * directly, otherwise xmount() and xumount() should be implemented as
-- * compatibility wrappers (see xmount.c).
-+ * Prototypes for the compatibility wrappers
-  */
- #ifdef __linux__
- # define xmount mount
- # define xumount umount2
-+# define xswapon swapon
-+# define xswapoff swapoff
- #else
- int xmount(const char *source, const char *target, const char *filesystemtype,
-               unsigned long mountflags, const void *data) FAST_FUNC;
- int xumount(const char *target, int flags) FAST_FUNC;
-+int xswapon(const char *path, int swapflags) FAST_FUNC;
-+int xswapoff(const char *path) FAST_FUNC;
- #endif
--- 
-1.7.1
-
diff --git a/debian/patches/tcpsvd-udpsvd-conditionalize-usage-of-SO_ORIGINAL_DS.patch b/debian/patches/tcpsvd-udpsvd-conditionalize-usage-of-SO_ORIGINAL_DS.patch
deleted file mode 100644 (file)
index eb797bc..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-From 2ea12d8b6d2a36c5d49df1ae97b86ba287835249 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:46:25 +0200
-Subject: [PATCH 9/9] tcpsvd,udpsvd: conditionalize usage of SO_ORIGINAL_DST
-
-On systems without this call, $TCPORIGDSTADDR is not set.
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- networking/Config.src |    2 --
- networking/tcpudp.c   |    5 +++++
- 2 files changed, 5 insertions(+), 2 deletions(-)
-
-diff --git a/networking/Config.src b/networking/Config.src
-index fc613e8..2d29c42 100644
---- a/networking/Config.src
-+++ b/networking/Config.src
-@@ -733,7 +733,6 @@ config SLATTACH
- config TCPSVD
-       bool "tcpsvd"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         tcpsvd listens on a TCP port and runs a program for each new
-         connection.
-@@ -966,7 +965,6 @@ config IFUPDOWN_UDHCPC_CMD_OPTIONS
- config UDPSVD
-       bool "udpsvd"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         udpsvd listens on an UDP port and runs a program for each new
-         connection.
-diff --git a/networking/tcpudp.c b/networking/tcpudp.c
-index 53e622b..40f6825 100644
---- a/networking/tcpudp.c
-+++ b/networking/tcpudp.c
-@@ -30,9 +30,12 @@
-  */
- #include "libbb.h"
-+
- /* Wants <limits.h> etc, thus included after libbb.h: */
-+#ifdef __linux__
- #include <linux/types.h> /* for __be32 etc */
- #include <linux/netfilter_ipv4.h>
-+#endif
- // TODO: move into this file:
- #include "tcpudp_perhost.h"
-@@ -464,6 +467,7 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
-                       /* setup ucspi env */
-                       const char *proto = tcp ? "TCP" : "UDP";
-+#ifdef SO_ORIGINAL_DST
-                       /* Extract "original" destination addr:port
-                        * from Linux firewall. Useful when you redirect
-                        * an outbond connection to local handler, and it needs
-@@ -473,6 +477,7 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
-                               xsetenv_plain("TCPORIGDSTADDR", addr);
-                               free(addr);
-                       }
-+#endif
-                       xsetenv_plain("PROTO", proto);
-                       xsetenv_proto(proto, "LOCALADDR", local_addr);
-                       xsetenv_proto(proto, "REMOTEADDR", remote_addr);
--- 
-1.7.1
-
diff --git a/debian/patches/top-display-rss.patch b/debian/patches/top-display-rss.patch
deleted file mode 100644 (file)
index e7776f1..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-diff --git a/procps/top.c b/procps/top.c
-index ec84374..9049578 100644
---- a/procps/top.c
-+++ b/procps/top.c
-@@ -35,7 +35,7 @@
- typedef struct top_status_t {
--      unsigned long vsz;
-+      unsigned long rss;
- #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
-       unsigned long ticks;
-       unsigned pcpu; /* delta of ticks */
-@@ -147,8 +147,8 @@ static int pid_sort(top_status_t *P, top_status_t *Q)
- static int mem_sort(top_status_t *P, top_status_t *Q)
- {
-       /* We want to avoid unsigned->signed and truncation errors */
--      if (Q->vsz < P->vsz) return -1;
--      return Q->vsz != P->vsz; /* 0 if ==, 1 if > */
-+      if (Q->rss < P->rss) return -1;
-+      return Q->rss != P->rss; /* 0 if ==, 1 if > */
- }
-@@ -519,7 +519,7 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width)
-       /* what info of the processes is shown */
-       printf(OPT_BATCH_MODE ? "%.*s" : "\033[7m%.*s\033[0m", scr_width,
--              "  PID  PPID USER     STAT   VSZ %MEM"
-+              "  PID  PPID USER     STAT   RSS %MEM"
-               IF_FEATURE_TOP_SMP_PROCESS(" CPU")
-               IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
-               " COMMAND");
-@@ -588,16 +588,16 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width)
-       s = top;
-       while (--lines_rem >= 0) {
-               unsigned col;
--              CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift);
-+              CALC_STAT(pmem, (s->rss*pmem_scale + pmem_half) >> pmem_shift);
- #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
-               CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
- #endif
--              if (s->vsz >= 100000)
--                      sprintf(vsz_str_buf, "%6ldm", s->vsz/1024);
-+              if (s->rss >= 100000)
-+                      sprintf(vsz_str_buf, "%6ldm", s->rss/1024);
-               else
--                      sprintf(vsz_str_buf, "%7ld", s->vsz);
--              /* PID PPID USER STAT VSZ %MEM [%CPU] COMMAND */
-+                      sprintf(vsz_str_buf, "%7ld", s->rss);
-+              /* PID PPID USER STAT VSZ %RSS [%CPU] COMMAND */
-               col = snprintf(line_buf, scr_width,
-                               "\n" "%5u%6u %-8.8s %s%s" FMT
-                               IF_FEATURE_TOP_SMP_PROCESS(" %3d")
-@@ -929,7 +929,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
-                               top = xrealloc_vector(top, 6, ntop++);
-                               top[n].pid = p->pid;
-                               top[n].ppid = p->ppid;
--                              top[n].vsz = p->vsz;
-+                              top[n].rss = p->rss;
- #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
-                               top[n].ticks = p->stime + p->utime;
- #endif
diff --git a/debian/patches/u-mount-FreeBSD-support.patch b/debian/patches/u-mount-FreeBSD-support.patch
deleted file mode 100644 (file)
index 702c051..0000000
+++ /dev/null
@@ -1,386 +0,0 @@
-From 5a075618b1deb735a6170e322052c7ba54b17d9e Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 29 Jul 2010 21:16:09 +0200
-Subject: [PATCH 18/19] (u)mount: FreeBSD support
-
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
----
- util-linux/Config.src |    4 +--
- util-linux/Kbuild.src |    4 +-
- util-linux/mount.c    |   41 ++-------------------
- util-linux/umount.c   |   41 +++------------------
- util-linux/xmount.c   |   66 +++++++++++++++++++++++++++++++++
- util-linux/xmount.h   |   97 +++++++++++++++++++++++++++++++++++++++++++++++++
- 6 files changed, 174 insertions(+), 79 deletions(-)
- create mode 100644 util-linux/xmount.c
- create mode 100644 util-linux/xmount.h
-
-diff --git a/util-linux/Config.src b/util-linux/Config.src
-index 98953c1..99a6fbe 100644
---- a/util-linux/Config.src
-+++ b/util-linux/Config.src
-@@ -492,7 +492,6 @@ config FEATURE_USE_TERMIOS
- config MOUNT
-       bool "mount"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         All files and filesystems in Unix are arranged into one big directory
-         tree. The 'mount' utility is used to graft a filesystem onto a
-@@ -679,7 +678,6 @@ config SWITCH_ROOT
- config UMOUNT
-       bool "umount"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         When you want to remove a mounted filesystem from its current mount
-         point, for example when you are shutting down the system, the
-@@ -699,7 +697,7 @@ comment "Common options for mount/umount"
- config FEATURE_MOUNT_LOOP
-       bool "Support loopback mounts"
-       default y
--      depends on MOUNT || UMOUNT
-+      depends on (MOUNT || UMOUNT) && PLATFORM_LINUX
-       help
-         Enabling this feature allows automatic mounting of files (containing
-         filesystem images) via the linux kernel's loopback devices.
-diff --git a/util-linux/Kbuild.src b/util-linux/Kbuild.src
-index afc0db5..312fc9e 100644
---- a/util-linux/Kbuild.src
-+++ b/util-linux/Kbuild.src
-@@ -33,7 +33,7 @@ lib-$(CONFIG_MKFS_REISER)       += mkfs_reiser.o
- lib-$(CONFIG_MKFS_VFAT)         += mkfs_vfat.o
- lib-$(CONFIG_MKSWAP)            += mkswap.o
- lib-$(CONFIG_MORE)              += more.o
--lib-$(CONFIG_MOUNT)             += mount.o
-+lib-$(CONFIG_MOUNT)             += mount.o xmount.o
- lib-$(CONFIG_PIVOT_ROOT)        += pivot_root.o
- lib-$(CONFIG_RDATE)             += rdate.o
- lib-$(CONFIG_RDEV)              += rdev.o
-@@ -44,4 +44,4 @@ lib-$(CONFIG_SCRIPTREPLAY)      += scriptreplay.o
- lib-$(CONFIG_SETARCH)           += setarch.o
- lib-$(CONFIG_SWAPONOFF)         += swaponoff.o
- lib-$(CONFIG_SWITCH_ROOT)       += switch_root.o
--lib-$(CONFIG_UMOUNT)            += umount.o
-+lib-$(CONFIG_UMOUNT)            += umount.o xmount.o
-diff --git a/util-linux/mount.c b/util-linux/mount.c
-index 9107e43..a62c4e8 100644
---- a/util-linux/mount.c
-+++ b/util-linux/mount.c
-@@ -18,44 +18,9 @@
- //
- #include <mntent.h>
- #include <syslog.h>
--#include <sys/mount.h>
--// Grab more as needed from util-linux's mount/mount_constants.h
--#ifndef MS_DIRSYNC
--# define MS_DIRSYNC     (1 << 7) // Directory modifications are synchronous
--#endif
--#ifndef MS_UNION
--# define MS_UNION       (1 << 8)
--#endif
--#ifndef MS_BIND
--# define MS_BIND        (1 << 12)
--#endif
--#ifndef MS_MOVE
--# define MS_MOVE        (1 << 13)
--#endif
--#ifndef MS_RECURSIVE
--# define MS_RECURSIVE   (1 << 14)
--#endif
--#ifndef MS_SILENT
--# define MS_SILENT      (1 << 15)
--#endif
--// The shared subtree stuff, which went in around 2.6.15
--#ifndef MS_UNBINDABLE
--# define MS_UNBINDABLE  (1 << 17)
--#endif
--#ifndef MS_PRIVATE
--# define MS_PRIVATE     (1 << 18)
--#endif
--#ifndef MS_SLAVE
--# define MS_SLAVE       (1 << 19)
--#endif
--#ifndef MS_SHARED
--# define MS_SHARED      (1 << 20)
--#endif
--#ifndef MS_RELATIME
--# define MS_RELATIME    (1 << 21)
--#endif
- #include "libbb.h"
-+#include "xmount.h"
- #if ENABLE_FEATURE_MOUNT_LABEL
- # include "volume_id.h"
- #else
-@@ -288,7 +253,7 @@ static int verbose_mount(const char *source, const char *target,
-       int rc;
-       errno = 0;
--      rc = mount(source, target, filesystemtype, mountflags, data);
-+      rc = xmount(source, target, filesystemtype, mountflags, data);
-       if (verbose >= 2)
-               bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
-                       source, target, filesystemtype,
-@@ -296,7 +261,7 @@ static int verbose_mount(const char *source, const char *target,
-       return rc;
- }
- #else
--#define verbose_mount(...) mount(__VA_ARGS__)
-+#define verbose_mount(...) xmount(__VA_ARGS__)
- #endif
- // Append mount options to string
-diff --git a/util-linux/umount.c b/util-linux/umount.c
-index a19f86c..781e019 100644
---- a/util-linux/umount.c
-+++ b/util-linux/umount.c
-@@ -8,40 +8,9 @@
-  * Licensed under GPL version 2, see file LICENSE in this tarball for details.
-  */
- #include <mntent.h>
--#include <sys/mount.h>
--/* Make sure we have all the new mount flags we actually try to use. */
--#ifndef MS_BIND
--# define MS_BIND        (1 << 12)
--#endif
--#ifndef MS_MOVE
--# define MS_MOVE        (1 << 13)
--#endif
--#ifndef MS_RECURSIVE
--# define MS_RECURSIVE   (1 << 14)
--#endif
--#ifndef MS_SILENT
--# define MS_SILENT      (1 << 15)
--#endif
--/* The shared subtree stuff, which went in around 2.6.15. */
--#ifndef MS_UNBINDABLE
--# define MS_UNBINDABLE  (1 << 17)
--#endif
--#ifndef MS_PRIVATE
--# define MS_PRIVATE     (1 << 18)
--#endif
--#ifndef MS_SLAVE
--# define MS_SLAVE       (1 << 19)
--#endif
--#ifndef MS_SHARED
--# define MS_SHARED      (1 << 20)
--#endif
--#ifndef MS_RELATIME
--# define MS_RELATIME    (1 << 21)
--#endif
-+
- #include "libbb.h"
--#ifndef PATH_MAX
--# define PATH_MAX (4*1024)
--#endif
-+#include "xmount.h"
- #if defined(__dietlibc__)
-@@ -154,11 +123,11 @@ int umount_main(int argc UNUSED_PARAM, char **argv)
-               if (m) zapit = m->dir;
-               // Let's ask the thing nicely to unmount.
--              curstat = umount(zapit);
-+              curstat = xumount(zapit, 0);
-               // Force the unmount, if necessary.
-               if (curstat && doForce)
--                      curstat = umount2(zapit, doForce);
-+                      curstat = xumount(zapit, doForce);
-               // If still can't umount, maybe remount read-only?
-               if (curstat) {
-@@ -166,7 +135,7 @@ int umount_main(int argc UNUSED_PARAM, char **argv)
-                               // Note! Even if we succeed here, later we should not
-                               // free loop device or erase mtab entry!
-                               const char *msg = "%s busy - remounted read-only";
--                              curstat = mount(m->device, zapit, NULL, MS_REMOUNT|MS_RDONLY, NULL);
-+                              curstat = xmount(m->device, zapit, NULL, MS_REMOUNT|MS_RDONLY, NULL);
-                               if (curstat) {
-                                       msg = "can't remount %s read-only";
-                                       status = EXIT_FAILURE;
-diff --git a/util-linux/xmount.c b/util-linux/xmount.c
-new file mode 100644
-index 0000000..3f322b8
---- /dev/null
-+++ b/util-linux/xmount.c
-@@ -0,0 +1,70 @@
-+#include "libbb.h"
-+#include "xmount.h"
-+
-+#ifdef __linux__
-+
-+/* xmount and xumount short-circuited to mount and umount2 in xmount.h */
-+
-+#elif defined(__FreeBSD_kernel__)
-+
-+static void build_iovec(struct iovec **iov, int *iovlen, const char *name,
-+              void *val, size_t len)
-+{
-+      int i;
-+
-+      if (*iovlen < 0)
-+              return;
-+      i = *iovlen;
-+      *iov = realloc(*iov, sizeof **iov * (i + 2));
-+      if (*iov == NULL) {
-+              *iovlen = -1;
-+              return;
-+      }
-+      (*iov)[i].iov_base = strdup(name);
-+      (*iov)[i].iov_len = strlen(name) + 1;
-+      i++;
-+      (*iov)[i].iov_base = val;
-+      if (len == (size_t)-1) {
-+              if (val != NULL)
-+                      len = strlen(val) + 1;
-+              else
-+                      len = 0;
-+      }
-+      (*iov)[i].iov_len = (int)len;
-+      *iovlen = ++i;
-+}
-+
-+int FAST_FUNC xmount(const char *source, const char *target,
-+              const char *filesystemtype, unsigned long mountflags,
-+              const void *data UNUSED_PARAM)
-+{
-+      struct iovec *iov = NULL;
-+      int iovlen = 0;
-+      char *fspath, *from;
-+      int ret;
-+
-+      fspath = realpath(target, NULL);
-+      from = realpath(source, NULL);
-+
-+      build_iovec(&iov, &iovlen, "fstype", (void*)filesystemtype, (size_t)-1);
-+      build_iovec(&iov, &iovlen, "fspath", fspath, (size_t)-1);
-+      if (!strcmp(filesystemtype, "nullfs"))
-+              /* nullfs uses a "target" instead of "from" */
-+              build_iovec(&iov, &iovlen, "target", from, (size_t)-1);
-+      else
-+              build_iovec(&iov, &iovlen, "from", from, (size_t)-1);
-+
-+      ret = nmount(iov, iovlen, mountflags);
-+
-+      free(from);
-+      free(fspath);
-+      
-+      return ret;
-+}
-+
-+int FAST_FUNC xumount(const char *target, int flags)
-+{
-+      return unmount(target, flags);
-+}
-+
-+#endif
-diff --git a/util-linux/xmount.h b/util-linux/xmount.h
-new file mode 100644
-index 0000000..caef564
---- /dev/null
-+++ b/util-linux/xmount.h
-@@ -0,0 +1,97 @@
-+/* vi: set sw=4 ts=4: */
-+/*
-+ * System-specific definitions for mount.
-+ *
-+ * Copyright (C) 2010 by Jeremie Koenig <jk@jk.fr.eu.org>
-+ * Copyright (C) 2010 by Luca Favatella <slackydeb@gmail.com>
-+ *
-+ * The Linux prototypes for mount() and umount2() are used as a reference for
-+ * our xmount() and xumount(), which should be implemented as a compatibility
-+ * wrappers for non-Linux systems (see xmount.c).
-+ */
-+
-+/*
-+ * Definitions for mount flags. Non-Linux systems are free to use whatever
-+ * their version of xmount() will work with.
-+ */
-+
-+#ifdef __linux__
-+# include <sys/mount.h>
-+/* Make sure we have all the new mount flags we actually try to use
-+ * (grab more as needed from util-linux's mount/mount_constants.h). */
-+# ifndef MS_DIRSYNC
-+#  define MS_DIRSYNC     (1 << 7) // Directory modifications are synchronous
-+# endif
-+# ifndef MS_UNION
-+#  define MS_UNION       (1 << 8)
-+# endif
-+# ifndef MS_BIND
-+#  define MS_BIND        (1 << 12)
-+# endif
-+# ifndef MS_MOVE
-+#  define MS_MOVE        (1 << 13)
-+# endif
-+# ifndef MS_RECURSIVE
-+#  define MS_RECURSIVE   (1 << 14)
-+# endif
-+# ifndef MS_SILENT
-+#  define MS_SILENT      (1 << 15)
-+# endif
-+/* The shared subtree stuff, which went in around 2.6.15. */
-+# ifndef MS_UNBINDABLE
-+#  define MS_UNBINDABLE  (1 << 17)
-+# endif
-+# ifndef MS_PRIVATE
-+#  define MS_PRIVATE     (1 << 18)
-+# endif
-+# ifndef MS_SLAVE
-+#  define MS_SLAVE       (1 << 19)
-+# endif
-+# ifndef MS_SHARED
-+#  define MS_SHARED      (1 << 20)
-+# endif
-+# ifndef MS_RELATIME
-+#  define MS_RELATIME    (1 << 21)
-+# endif
-+
-+#elif defined(__FreeBSD_kernel__)
-+# include <sys/mount.h>
-+# define MS_NOSUID      MNT_NOSUID
-+# define MS_NODEV       MNT_NODEV
-+# define MS_NOEXEC      MNT_NOEXEC
-+# define MS_SYNCHRONOUS MNT_SYNCHRONOUS
-+# define MS_DIRSYNC     0
-+# define MS_NOATIME     MNT_NOATIME
-+# define MS_NODIRATIME  0
-+# define MS_MANDLOCK    0
-+# define MS_RELATIME    0
-+# define MS_SILENT      0
-+# define MS_UNION       MNT_UNION
-+# define MS_BIND        0
-+# define MS_MOVE        0
-+# define MS_SHARED      0
-+# define MS_SLAVE       0
-+# define MS_PRIVATE     0
-+# define MS_UNBINDABLE  0
-+# define MS_RECURSIVE   0
-+# define MS_RDONLY      MNT_RDONLY
-+# define MS_REMOUNT     MNT_UPDATE
-+
-+#else
-+# error There is no xmount() implementation for your system.
-+#endif
-+
-+/*
-+ * Prototypes for xmount() and xumount(): on Linux we use the system calls
-+ * directly, otherwise xmount() and xumount() should be implemented as
-+ * compatibility wrappers (see xmount.c).
-+ */
-+
-+#ifdef __linux__
-+# define xmount mount
-+# define xumount umount2
-+#else
-+int xmount(const char *source, const char *target, const char *filesystemtype,
-+              unsigned long mountflags, const void *data) FAST_FUNC;
-+int xumount(const char *target, int flags) FAST_FUNC;
-+#endif
--- 
-1.7.1
-
diff --git a/debian/patches/udhcpc-fast-request.patch b/debian/patches/udhcpc-fast-request.patch
deleted file mode 100644 (file)
index 47f5f3e..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-From: Hakgoo Lee <goodguy.lee@samsung.com>
-Date: Thu, 16 Sep 2010 14:51:22 +0900
-Subject: [PATCH] Add FEATURE_UDHCPC_FAST_REQUEST to udhcp.
-
-If selected, udhcpc will send Request if leased IP and DHCP server is specified.
-So Discover/Offer routine can be skipped. It can reduce service recovery time in WiFi.
----
- include/usage.src.h         |    4 ++++
- networking/udhcp/Config.src |    8 ++++++++
- networking/udhcp/dhcpc.c    |   28 ++++++++++++++++++++++++++++
- 3 files changed, 40 insertions(+), 0 deletions(-)
-
-diff --git a/include/usage.src.h b/include/usage.src.h
-index 94a3256..e3c987c 100644
---- a/include/usage.src.h
-+++ b/include/usage.src.h
-@@ -4539,6 +4539,10 @@ INSERT
-       IF_FEATURE_UDHCPC_ARPING( \
-      "\n      -a,--arping             Use arping to validate offered address" \
-       ) \
-+      IF_FEATURE_UDHCPC_FAST_REQUEST( \
-+     "\n      -d,--request-direct=IP  IP address to request without discover, must be used with -D" \
-+     "\n      -D,--dhcp-server=IP     DHCP server IP address to get leased IP address" \
-+      ) \
-      "\n      -O,--request-option OPT Request DHCP option OPT (cumulative)" \
-      "\n      -o,--no-default-options Don't request any options (unless -O is given)" \
-      "\n      -x OPT:VAL              Include option OPT in sent packets (cumulative)" \
-diff --git a/networking/udhcp/Config.src b/networking/udhcp/Config.src
-index 331dffc..c4ec82f 100644
---- a/networking/udhcp/Config.src
-+++ b/networking/udhcp/Config.src
-@@ -130,3 +130,11 @@ 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_UDHCPC_FAST_REQUEST
-+      bool "Send Fast Request without Discover/Offer (e.g. When same subnet is connected again like WiFi AP)."
-+      default y
-+      depends on UDHCPC
-+      help
-+        If selected, udhcpc will send Request if leased IP and DHCP server is specified.
-+        So Discover/Offer routine can be skipped. It can reduce service recovery time in WiFi.
-diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
-index de1b798..e740871 100644
---- a/networking/udhcp/dhcpc.c
-+++ b/networking/udhcp/dhcpc.c
-@@ -767,6 +767,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
- {
-       uint8_t *temp, *message;
-       const char *str_c, *str_V, *str_h, *str_F, *str_r;
-+      IF_FEATURE_UDHCPC_FAST_REQUEST(char *str_d, *str_D;)
-       IF_FEATURE_UDHCP_PORT(char *str_P;)
-       llist_t *list_O = NULL;
-       llist_t *list_x = NULL;
-@@ -812,6 +813,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
-               "background\0"     No_argument       "b"
-               IF_FEATURE_UDHCPC_ARPING("arping\0"     No_argument       "a")
-               IF_FEATURE_UDHCP_PORT("client-port\0"   Required_argument "P")
-+              IF_FEATURE_UDHCPC_FAST_REQUEST("request-direct\0" Required_argument "d")
-+              IF_FEATURE_UDHCPC_FAST_REQUEST("dhcp-server\0"    Required_argument "D")
-               ;
- #endif
-       enum {
-@@ -841,9 +844,13 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
-               USE_FOR_MMU(             OPTBIT_b,)
-               IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
-               IF_FEATURE_UDHCP_PORT(   OPTBIT_P,)
-+              IF_FEATURE_UDHCPC_FAST_REQUEST(OPTBIT_d,)
-+              IF_FEATURE_UDHCPC_FAST_REQUEST(OPTBIT_D,)
-               USE_FOR_MMU(             OPT_b = 1 << OPTBIT_b,)
-               IF_FEATURE_UDHCPC_ARPING(OPT_a = 1 << OPTBIT_a,)
-               IF_FEATURE_UDHCP_PORT(   OPT_P = 1 << OPTBIT_P,)
-+              IF_FEATURE_UDHCPC_FAST_REQUEST(OPT_d = 1 << OPTBIT_d,)
-+              IF_FEATURE_UDHCPC_FAST_REQUEST(OPT_D = 1 << OPTBIT_D,)
-       };
-       /* Default options. */
-@@ -865,6 +872,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
-               USE_FOR_MMU("b")
-               IF_FEATURE_UDHCPC_ARPING("a")
-               IF_FEATURE_UDHCP_PORT("P:")
-+              IF_FEATURE_UDHCPC_FAST_REQUEST("d:D:")
-               "v"
-               , &str_c, &str_V, &str_h, &str_h, &str_F
-               , &client_config.interface, &client_config.pidfile, &str_r /* i,p */
-@@ -873,6 +881,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
-               , &list_O
-               , &list_x
-               IF_FEATURE_UDHCP_PORT(, &str_P)
-+              IF_FEATURE_UDHCPC_FAST_REQUEST(, &str_d, &str_D)
- #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
-               , &dhcp_verbose
- #endif
-@@ -950,6 +959,18 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
-               logmode |= LOGMODE_SYSLOG;
-       }
-+#if ENABLE_FEATURE_UDHCPC_FAST_REQUEST
-+      if (opt & OPT_d) {
-+              log1("Parsing request-direct option");
-+              requested_ip = inet_addr(str_d);
-+              state = REQUESTING;
-+      }
-+      if (opt & OPT_D) {
-+              log1("Parsing dhcp-server option");
-+              server_addr = inet_addr(str_D);
-+      }
-+#endif
-+
-       /* Make sure fd 0,1,2 are open */
-       bb_sanitize_stdio();
-       /* Equivalent of doing a fflush after every \n */
-@@ -963,7 +984,14 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
-       /* We want random_xid to be random... */
-       srand(monotonic_us());
-+#if ENABLE_FEATURE_UDHCPC_FAST_REQUEST
-+      if (state != REQUESTING)
-+              state = INIT_SELECTING;
-+      log1("Initial state is %d", state);
-+#else
-       state = INIT_SELECTING;
-+#endif
-+
-       udhcp_run_script(NULL, "deconfig");
-       change_listen_mode(LISTEN_RAW);
-       packet_num = 0;
--- 
diff --git a/debian/patches/update-scripts-kconfig-_shipped.patch b/debian/patches/update-scripts-kconfig-_shipped.patch
deleted file mode 100644 (file)
index 52cc64e..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-commit 6e06da5efd5d6e341ae2f5116c449994740f5613
-Author: Denys Vlasenko <vda.linux@googlemail.com>
-Date:   Mon Aug 2 02:17:25 2010 +0200
-
-    update _shipped file with hurd fix
-    
-    Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-
-diff --git a/scripts/kconfig/lex.zconf.c_shipped b/scripts/kconfig/lex.zconf.c_shipped
-index 4837bbf..51f15e1 100644
---- a/scripts/kconfig/lex.zconf.c_shipped
-+++ b/scripts/kconfig/lex.zconf.c_shipped
-@@ -2235,13 +2235,14 @@ static void zconf_endhelp(void)
-  */
- FILE *zconf_fopen(const char *name)
- {
--      char *env, fullname[PATH_MAX+1];
-+      char *env;
-       FILE *f;
-       f = fopen(name, "r");
-       if (!f && name[0] != '/') {
-               env = getenv(SRCTREE);
-               if (env) {
-+                      char *fullname = alloca(strlen(env) + strlen(name) + 2);
-                       sprintf(fullname, "%s/%s", env, name);
-                       f = fopen(fullname, "r");
-               }
diff --git a/debian/patches/version.patch b/debian/patches/version.patch
deleted file mode 100644 (file)
index a09b782..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
---- a/Makefile.flags
-+++ b/Makefile.flags
-@@ -4,6 +4,11 @@
- BB_VER = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
- export BB_VER
-+ifndef BB_EXTRA_VERSION
-+BB_BT = AUTOCONF_TIMESTAMP
-+else
-+BB_BT = KBUILD_STR($(BB_EXTRA_VERSION))
-+endif
- SKIP_STRIP = n
- # -std=gnu99 needed for [U]LLONG_MAX on some systems
-@@ -15,7 +20,7 @@
-       -include include/autoconf.h \
-       -D_GNU_SOURCE -DNDEBUG \
-       $(if $(CONFIG_LFS),-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64) \
--      -D"BB_VER=KBUILD_STR($(BB_VER))" -DBB_BT=AUTOCONF_TIMESTAMP
-+      -D"BB_VER=KBUILD_STR($(BB_VER))" -D"BB_BT=$(BB_BT)"
- CFLAGS += $(call cc-option,-Wall,)
- CFLAGS += $(call cc-option,-Wshadow,)
diff --git a/debian/patches/vlock-disable-linux-console-calls-on-other-systems.patch b/debian/patches/vlock-disable-linux-console-calls-on-other-systems.patch
deleted file mode 100644 (file)
index f717473..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-From 68fca4cd55e7bf6075eb1ccd303ae57a7ec1b8da Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 29 Jul 2010 04:29:52 +0200
-Subject: [PATCH 12/12] vlock: disable linux console calls on other systems
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- loginutils/Config.src |    1 -
- loginutils/vlock.c    |   15 +++++++++++++--
- 2 files changed, 13 insertions(+), 3 deletions(-)
-
-diff --git a/loginutils/Config.src b/loginutils/Config.src
-index 6ec2893..5d497c4 100644
---- a/loginutils/Config.src
-+++ b/loginutils/Config.src
-@@ -295,7 +295,6 @@ config SULOGIN
- config VLOCK
-       bool "vlock"
-       default y
--      depends on PLATFORM_LINUX
-       select FEATURE_SUID
-       help
-         Build the "vlock" applet which allows you to lock (virtual) terminals.
-diff --git a/loginutils/vlock.c b/loginutils/vlock.c
-index 85f489c..59aeb54 100644
---- a/loginutils/vlock.c
-+++ b/loginutils/vlock.c
-@@ -15,9 +15,11 @@
- /* Fixed by Erik Andersen to do passwords the tinylogin way...
-  * It now works with md5, sha1, etc passwords. */
--#include <sys/vt.h>
- #include "libbb.h"
-+#ifdef __linux__
-+#include <sys/vt.h>
-+
- static void release_vt(int signo UNUSED_PARAM)
- {
-       /* If -a, param is 0, which means:
-@@ -30,14 +32,17 @@ static void acquire_vt(int signo UNUSED_PARAM)
-       /* ACK to kernel that switch to console is successful */
-       ioctl(STDIN_FILENO, VT_RELDISP, VT_ACKACQ);
- }
-+#endif
- int vlock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
- int vlock_main(int argc UNUSED_PARAM, char **argv)
- {
-+#ifdef __linux__
-       struct vt_mode vtm;
-+      struct vt_mode ovtm;
-+#endif
-       struct termios term;
-       struct termios oterm;
--      struct vt_mode ovtm;
-       struct passwd *pw;
-       pw = xgetpwuid(getuid());
-@@ -55,6 +60,7 @@ int vlock_main(int argc UNUSED_PARAM, char **argv)
-               + (1 << SIGINT )
-               , SIG_IGN);
-+#ifdef __linux__
-       /* We will use SIGUSRx for console switch control: */
-       /* 1: set handlers */
-       signal_SA_RESTART_empty_mask(SIGUSR1, release_vt);
-@@ -62,12 +68,14 @@ int vlock_main(int argc UNUSED_PARAM, char **argv)
-       /* 2: unmask them */
-       sig_unblock(SIGUSR1);
-       sig_unblock(SIGUSR2);
-+#endif
-       /* Revert stdin/out to our controlling tty
-        * (or die if we have none) */
-       xmove_fd(xopen(CURRENT_TTY, O_RDWR), STDIN_FILENO);
-       xdup2(STDIN_FILENO, STDOUT_FILENO);
-+#ifdef __linux__
-       xioctl(STDIN_FILENO, VT_GETMODE, &vtm);
-       ovtm = vtm;
-       /* "console switches are controlled by us, not kernel!" */
-@@ -75,6 +83,7 @@ int vlock_main(int argc UNUSED_PARAM, char **argv)
-       vtm.relsig = SIGUSR1;
-       vtm.acqsig = SIGUSR2;
-       ioctl(STDIN_FILENO, VT_SETMODE, &vtm);
-+#endif
-       tcgetattr(STDIN_FILENO, &oterm);
-       term = oterm;
-@@ -95,7 +104,9 @@ int vlock_main(int argc UNUSED_PARAM, char **argv)
-               puts("Password incorrect");
-       } while (1);
-+#ifdef __linux__
-       ioctl(STDIN_FILENO, VT_SETMODE, &ovtm);
-+#endif
-       tcsetattr_stdin_TCSANOW(&oterm);
-       fflush_stdout_and_exit(EXIT_SUCCESS);
- }
--- 
-1.7.1
-
diff --git a/debian/rules b/debian/rules
deleted file mode 100755 (executable)
index 10aeec8..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-#!/usr/bin/make -f
-
-SHELL := sh -e
-DEB_HOST_ARCH := $(shell dpkg-architecture -qDEB_HOST_ARCH)
-DEB_HOST_ARCH_OS := $(shell dpkg-architecture -qDEB_HOST_ARCH_OS)
-DEB_BUILD_ARCH := $(shell dpkg-architecture -qDEB_BUILD_ARCH)
-SOURCE := $(shell dpkg-parsechangelog | sed -ne 's,^Source: *\(.*\)$$,\1,p')
-VERSION_DEBIAN := $(shell dpkg-parsechangelog | sed -ne 's,^Version: *\(.*\)$$,\1,p')
-VERSION := $(shell echo "$(VERSION_DEBIAN)" | sed -e 's,^[^:]*:,,' -e 's,-[^-]*$$,,')
-
-BUILD_DIR = debian/build
-STAMPS_DIR = debian/stamps
-
-ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
-       CONFIG_DEBUG = y
-endif
-ifneq (,$(findstring thumb,$(DEB_BUILD_OPTIONS)))
-THUMB := -mthumb
-endif
-
-source: $(STAMPS_DIR)/source
-patch: $(STAMPS_DIR)/patch
-
-$(STAMPS_DIR)/patch:
-       dh_testdir
-       @mkdir -p $(STAMPS_DIR)
-       QUILT_PATCHES=$(CURDIR)/debian/patches quilt --quiltrc /dev/null push -a || test $$? = 2
-       touch $@
-
-#setup: $(STAMPS_DIR)/setup_deb $(STAMPS_DIR)/setup_static $(STAMPS_DIR)/setup_udeb
-setup: $(STAMPS_DIR)/setup_slp
-
-$(STAMPS_DIR)/setup_%: SOURCE_FILES = $(filter-out debian, $(wildcard *))
-$(STAMPS_DIR)/setup_%: DIR=$(BUILD_DIR)/build_$*
-$(STAMPS_DIR)/setup_%: $(STAMPS_DIR)/patch
-       dh_testdir
-       rm -rf '$(DIR)'
-       mkdir -p '$(DIR)'
-       cp -a -l $(SOURCE_FILES) '$(DIR)'
-       cat debian/config/os/$(DEB_HOST_ARCH_OS) debian/config/pkg/$* > '$(DIR)'/.config
-       $(MAKE) -C '$(DIR)' oldconfig
-       touch $@
-
-#build: $(STAMPS_DIR)/build_deb $(STAMPS_DIR)/build_static $(STAMPS_DIR)/build_udeb
-build: $(STAMPS_DIR)/build_slp
-
-$(STAMPS_DIR)/build_slp : debian/sfdisk/sfdisk
-$(STAMPS_DIR)/build_%: DIR=$(BUILD_DIR)/build_$*
-$(STAMPS_DIR)/build_%: $(STAMPS_DIR)/setup_%
-       dh_testdir
-       $(MAKE) -C '$(DIR)' THUMB=$(THUMB) busybox docs/busybox.1 BB_EXTRA_VERSION="SLP $(VERSION_DEBIAN)"
-       $(MAKE) -C '$(DIR)' busybox.links
-       touch $@
-
-$(STAMPS_DIR)/indepbuild_%: DIR=$(BUILD_DIR)/indepbuild_$*
-$(STAMPS_DIR)/indepbuild_%:
-       dh_testdir
-       touch $@
-
-debian/sfdisk/sfdisk:
-       $(MAKE) -C debian/sfdisk
-
-$(BUILD_DIR) $(STAMPS_DIR):
-       @[ -d $@ ] || mkdir $@
-
-maintainerclean:
-       rm -rf $(filter-out .svn debian, $(wildcard * .[^.]*))
-
-clean:
-       dh_testdir
-       rm -rf $(BUILD_DIR) $(STAMPS_DIR)
-       $(MAKE) -C debian/sfdisk clean
-       dh_clean
-
-#binary-indep: binary-indep_busybox-syslogd binary-indep_udhcpc binary-indep_udhcpd
-binary-indep: binary-indep_systemd
-
-#binary-arch: binary-arch_busybox binary-arch_busybox-static binary-arch_busybox-udeb
-binary-arch: binary-arch_slp
-
-binary-arch_slp: DIR = $(BUILD_DIR)/build_slp
-binary-arch_slp: $(STAMPS_DIR)/build_slp
-       dh_testdir
-       dh_testroot
-       dh_clean -k -d
-       # verify that packages links files are in sync with BB generated one
-       # some other time
-       #debian/scripts/check-links.py $(DIR)/busybox.links debian/*.links
-       # verify that Busybox provides these packages
-       grep util-linux debian/control > /dev/null
-       install -D -m755 debian/sfdisk/sfdisk debian/busybox/sbin/sfdisk
-       #grep debianutils debian/control > /dev/null
-       #install -D -m755 debian/local/tempfile debian/busybox/bin/tempfile
-       install -D -m755 $(DIR)/busybox debian/busybox/bin/busybox
-
-       $(MAKE) -f debian/rules binary-arch_all
-
-binary-arch_busybox: DIR = $(BUILD_DIR)/build_deb
-binary-arch_busybox: export DH_OPTIONS = -pbusybox
-binary-arch_busybox: $(STAMPS_DIR)/build_deb
-       dh_testdir
-       dh_testroot
-       dh_clean -k
-       dh_install -X.svn --sourcedir=$(DIR)
-       @$(MAKE) -f debian/rules binary-arch_all
-
-binary-arch_busybox-static: DIR = $(BUILD_DIR)/build_static
-binary-arch_busybox-static: export DH_OPTIONS = -pbusybox-static
-binary-arch_busybox-static: $(STAMPS_DIR)/build_static
-       dh_testdir
-       dh_testroot
-       dh_clean -k
-       dh_install -X.svn --sourcedir=$(DIR)
-       @$(MAKE) -f debian/rules binary-arch_all
-
-binary-arch_busybox-udeb: DIR = $(BUILD_DIR)/build_udeb
-binary-arch_busybox-udeb: export DH_OPTIONS = -pbusybox-udeb
-binary-arch_busybox-udeb: $(STAMPS_DIR)/build_udeb
-       dh_testdir
-       dh_testroot
-       dh_clean -k
-       # Remove init link, but init support is still compiled in to be
-       # used.
-       rm -f $(DIR)/_install/sbin/init
-       dh_install -X.svn --sourcedir=$(DIR)
-       @$(MAKE) -f debian/rules binary-arch_all
-
-binary-arch_all: export DH_OPTIONS = -Nbusybox-systemd-klogd -Nbusybox-systemd-sysklogd
-binary-arch_all:
-       dh_installdirs
-       # add docs & debug only for busybox package itself
-       dh_installdocs -pbusybox
-       dh_installchangelogs -pbusybox
-       dh_strip -pbusybox --dbg-package=busybox-dbg
-       dh_compress -pbusybox
-       # packaging
-       dh_link
-       dh_fixperms
-       dh_installdeb
-       # only busybox package has binaries
-       dh_shlibdeps -pbusybox
-       dh_gencontrol
-       dh_md5sums
-       dh_builddeb
-
-binary-indep_busybox-syslogd: export DH_OPTIONS = -pbusybox-syslogd
-binary-indep_busybox-syslogd:
-       dh_testdir
-       dh_testroot
-       dh_clean -k
-       dh_link
-       dh_installinit -u"defaults 10 90"
-       dh_installinit -u"defaults 11 89" --name=busybox-klogd
-       $(MAKE) -f debian/rules binary-indep_all
-
-binary-indep_udhcpc: export DH_OPTIONS = -pudhcpc
-binary-indep_udhcpc:
-       dh_testdir
-       dh_testroot
-       dh_clean -k
-       dh_link
-       dh_install -X.svn
-       $(MAKE) -f debian/rules binary-indep_all
-
-binary-indep_udhcpd: export DH_OPTIONS = -pudhcpd
-binary-indep_udhcpd:
-       dh_testdir
-       dh_testroot
-       dh_clean -k
-       dh_link
-       dh_installinit --onlyscripts
-       dh_install -X.svn
-       $(MAKE) -f debian/rules binary-indep_all
-
-binary-indep_systemd: export DH_OPTIONS = -pbusybox-systemd-klogd -pbusybox-systemd-sysklogd
-binary-indep_systemd:
-       dh_testdir
-       dh_testroot
-       dh_clean -k
-       dh_link
-       dh_install -X.svn
-       $(MAKE) -f debian/rules binary-indep_all
-
-binary-indep_all:
-       dh_installdirs
-       dh_installdocs
-       dh_installchangelogs
-       dh_compress
-       dh_fixperms
-       dh_installdeb
-       dh_gencontrol
-       dh_md5sums
-       dh_builddeb
-
-binary: binary-indep binary-arch
-
-DIR_ORIG = ../orig/$(SOURCE)-$(VERSION)
-TAR_ORIG_NAME = $(SOURCE)_$(VERSION).orig.tar.bz2
-TAR_ORIG = $(firstword $(wildcard ../$(TAR_ORIG_NAME)) $(wildcard ../orig/$(TAR_ORIG_NAME)))
-
-orig: $(DIR_ORIG)
-       rsync --delete --exclude debian --exclude .git --link-dest=$(DIR_ORIG)/ -a $(DIR_ORIG)/ .
-
-$(DIR_ORIG):
-ifeq ($(TAR_ORIG),)
-       $(error Cannot find orig tarball $(TAR_ORIG_NAME))
-else
-       mkdir -p ../orig
-       tar -C ../orig -xjf $(TAR_ORIG)
-endif
-
diff --git a/debian/scripts/README b/debian/scripts/README
deleted file mode 100644 (file)
index fda9d2f..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-
-BusyBox Debian packaging Howto for SLP
-========================================
-
-This is a howto for creating optimal match between BusyBox
-Debian package configuration and the upstream Debian packages
-which BusyBox replaces.
-
-Files:
-
-- README -- this file
-- create-control.py -- script to create BusyBox packaging files
-- check-links.py -- script for checking at package build whether
-                    the packaging files match BusyBox created link list
-- debian-mappings.txt -- mappings between BusyBox and Debian packages,
-                         needed by the script
-- busybox-notes.txt -- some extra notes about differences vs. Debian
-                       BusyBox
-- *.dirs -- extra packaging files needed in addition to ones
-            created by the script
-
-
-What updates are needed and when/where:
-
-When Packaging info needs to be changed (maintainer etc):
--> Updating control file content excerpts in the beginning
-   of create-control.py
-
-When syncing to newer upstream Debian version:
--> Updating debian-mappings.txt according to changes in Debian (if any)
-
-When BusyBox configuration needs to be changed (after corresponding
-architecture ticket is accepted):
--> Iterate through the steps below.  Replace the earlier package
-   control, *.links, *.postinst and *.prerm files with ones created
-   by create-control.py
-
-
-BusyBox configuration update steps:
-
-1. Enable/disable relevant things from BusyBox config
-       vi debian/config/pkg/slp
-
-2. Create initial control, postinst & links files for BusyBox
-   packages based on this config file:
-       cd debian/scripts
-       ./create-control.py -c ../config/pkg/slp debian-mappings.txt
-
-3. If any errors were reported, add the missing BB binaries
-   to Debian mappings and repeat from 1).  May be needed if later
-   (>1.11) BusyBox versions have new binaries that are enabled
-
-4. Build BusyBox:
-       cd ../..
-       dpkg-buildpackage -rfakeroot -b -uc
-
-5. Create updated control, postinst & links files for BusyBox
-   packages based on the BusyBox links file:
-       cd debian/scripts
-       ./create-control.py -l ../build/build_slp/busybox.links debian-mappings.txt
-
-6. Update BusyBox config (or mappings file) to fix the relevant
-   issues and repeat from 4) until no relevant warnings are given
-
-7. Overwrite previous packagaging with the new one:
-       cp *.dirs ../; mv control *.{links,postinst,prerm} ../
-
-8. Cross-check from debian/rules file that everything is up to date.
-   Manually you can do it like this:
-       cd ..
-       scripts/check-links.py build/build_slp/busybox.links *.links
-
-NOTE: The script cannot deduce all the symlinks based on BusyBox
-configuration at step 2), that's why BB config is used only for
-initial configuration and rest of the checks at step 5) are done
-based on the links file created by the BusyBox build.
diff --git a/debian/scripts/busybox-notes.txt b/debian/scripts/busybox-notes.txt
deleted file mode 100644 (file)
index d3e8d99..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-
-Changes between Diablo and Fremantle Maemo BusyBox configurations
------------------------------------------------------------------
-
-- sysvinit: in Diablo, real package, in Fremantle, provided by BB
-  - In Fremantle "/sbin/init" comes from "upstart", not sysvinit though
-  - Note: In Debian Lenny sysvinit was split to sysvinit & sysvinit-tools
-    and the debian-mapping.txt follows that.  Diablo sysvinit still
-    contains binaries for both of these packages like Debian Etch did
-- module-init-tools: in Diablo, provided by BB, in Fremantle, real package
-
-
-Comparison between Maemo BusyBox symlinks and Debian (Etch-Sid) binaries
-========================================================================
-
-Two ways for listing applets:
-- ./busybox|grep -A99 addgroup|tr ',' '\n'|awk '/[^ ]/{print $1}'|sort
-- make busybox.links
-  python -c "import os;\
-  print ''.join([os.path.basename(x) for x in open('busybox.links')])"
-
-
-Name conflicts
---------------
-
-pscan: BusyBox binary scans ports, Debian C-source
-ftpput: BusyBox binary has regular FTP, Debian camera additions
-
-
-Renames/replacements
---------------------
-
-It doesn't make sense to rename these to use same
-name as Debian alternative before somebody checks
-that they're compatible enough with the Debian ones.
-
-Debian:   BusyBox:
-lzcat     -> lzmacat
-makedev   -> makedevs
-udev      -> mdev
-microcom  -> minicom
-splash    -> fbsplash
-cron      -> crond
-dhttpd    -> httpd
-in.tftpd  -> tftpd
-cat -v    -> catv
-ip addr   -> ipaddr
-ip link   -> iplink
-ip route  -> iproute
-ip tunnel -> iptunnel
-ip rule   -> iprule
-
-For some reason, in Debian 'dnsd' is in (dietlibc)-dev package
-and located in /usr/lib/diet/bin/dnsd.
-
-
-Extras
-------
-
-[[ -> symlink to [
-
-
-Special cases
--------------
-
-'linuxrc' file in upstream source, not included into our package.
-
-Following ones are in our BusyBox package, but not in upstream or
-Debian.
-
-util-linux:
-/sbin/sfdisk (binary)
-
-debianutils:
-/bin/tempfile (script)
-
-module-init-tools:
-/sbin/update-modules (empty script)
-
-(no corresponding Debian package):
-/bin/fsync (BusyBox applet)
-
-
-Related files
--------------
-
-ifupdown (dirs):
-/etc/network/if-down.d/
-/etc/network/if-post-down.d/
-/etc/network/if-pre-up.d/
-/etc/network/if-up.d/
diff --git a/debian/scripts/busybox-symlinks-ifupdown.dirs b/debian/scripts/busybox-symlinks-ifupdown.dirs
deleted file mode 100644 (file)
index d7fd273..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-etc/network/if-post-down.d
-etc/network/if-pre-up.d
-etc/network/if-up.d
-etc/network/if-down.d
diff --git a/debian/scripts/busybox.dirs b/debian/scripts/busybox.dirs
deleted file mode 100644 (file)
index d653148..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-bin
-sbin
-usr/share/man/man1
diff --git a/debian/scripts/check-links.py b/debian/scripts/check-links.py
deleted file mode 100755 (executable)
index 1ba8c7b..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/python
-#
-# Check that the binaries for links BusyBox outputs and the ones
-# in the packages *.links files match.
-#
-# Copyright (C) 2008 by Nokia Corporation
-#
-# Contact: Eero Tamminen <eero.tamminen@nokia.com>
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# version 2 as published by the Free Software Foundation.
-#
-# This program 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
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-
-import os, sys
-
-def add_links_from(filename, olditems):
-    "read busybox.links file and return a list of binary names"
-    newitems = {}
-    existing = []
-    print "-", filename
-    for line in open(filename):
-        line = line.strip()
-        if not line:
-            continue
-        name = os.path.basename(line.split()[-1])
-        if name in olditems and name not in newitems:
-            # same package may symlink same name to different
-            # places, but other packages may not
-            existing.append(name)
-        else:
-            olditems[name] = filename
-            newitems[name] = 1
-    
-    if existing:
-        print "ERROR: following items were already in (some) previous links file:"
-        for name in existing:
-            print "-", name
-        print "Re-run of create-control.py needed?"
-        sys.exit(1)
-
-
-def process_args(argv):
-    links = {}
-    bblinks = {}
-    print "Checking:"
-    add_links_from(argv[1], bblinks)
-    for filename in argv[2:]:
-        add_links_from(filename, links)
-    
-    missing = []
-    for link in bblinks.keys():
-        if link in links:
-            del(links[link])
-        else:
-            missing.append(link)
-    
-    if missing:
-        print "WARNING: links files for packages are missing following BB links:"
-        for link in missing:
-            print "-", link
-        print "Are all these installed as alternatives?"
-    
-    if links:
-        print "ERROR: BB links file doesn't contain following packages links:"
-        for link in links.keys():
-            print "- %s (in '%s')" % (link, links[link])
-        print "Re-run of create-control.py needed?"
-        sys.exit(1)
-
-
-if __name__ == "__main__":
-    if len(sys.argv) > 2:
-        process_args(sys.argv)
-    else:
-       print """
-Script to check that binary names in packages link files
-match the list of binary names in the BB link file.
-
-usage: %s <BB links file> <packages link files>
-""" % os.path.basename(sys.argv[0])
-       sys.exit(1)
-
diff --git a/debian/scripts/create-control.py b/debian/scripts/create-control.py
deleted file mode 100755 (executable)
index dff4e55..0000000
+++ /dev/null
@@ -1,499 +0,0 @@
-#!/usr/bin/python
-#
-# Output BusyBox source package debian/control file and
-# relevant *.links & *.postist files for the binary packages,
-# based on the Busybox configuration and Debian package
-# mappings file.
-#
-# Copyright (C) 2008 by Nokia Corporation
-#
-# Contact: Eero Tamminen <eero.tamminen@nokia.com>
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# version 2 as published by the Free Software Foundation.
-#
-# This program 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
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-#
-# Notes:
-# - If some package provide only alternatives, Busybox package
-#   control file is set up to provide those and its postinst to
-#   install the alternatives.  As they cannot conflict, there's
-#   no need to put them into separate symlink packages
-
-# source package, busybox and debug package descriptions
-basepackages_info = """Source: busybox
-Priority: optional
-Section: utils
-Maintainer: Rafal Krypa <r.krypa@samsung.com>
-Uploaders: Karol Lewandowski <k.lewandowsk@samsung.com>
-X-Maemo-Maintainer: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
-X-Original-Maintainer: Debian Install System Team <debian-boot@lists.debian.org>
-X-Original-Uploaders: Bastian Blank <waldi@debian.org>
-Build-Depends: debhelper (>> 5), python, quilt
-Standards-Version: 3.7.3
-
-Package: busybox
-Priority: required
-Essential: yes
-Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}
-Provides: %(provides)s
-Replaces: %(replaces)s
-Conflicts: %(conflicts)s
-Description: Tiny utilities for small and embedded systems
- BusyBox combines tiny versions of many common UNIX utilities into a single
- small executable. It provides minimalist replacements for the most common
- utilities you would usually find on your desktop system (i.e., ls, cp, mv,
- mount, tar, etc.). The utilities in BusyBox generally have fewer options than
- their full-featured GNU cousins; however, the options that are included
- provide the expected functionality and behave very much like their GNU
- counterparts.
- .
- This package installs:
- - the BusyBox binary
- - symlinks for tools included into it that correspond to binaries
-   in essential Debian packages
- - alternatives from packages that don't have anything else
- .
- Symlinks to other tools included into BusyBox which correspond to binaries
- in non-essential Debian packages are provided in separate symlink packages.
- Those package are split and named according to the corresponding Debian
- packages.
-
-Package: busybox-dbg
-Architecture: any
-Depends: busybox (= ${binary:Version})
-Section: devel
-Description: Debug symbols for BusyBox
- Debug symbol file for BusyBox. BusyBox provides tiny utilities for small
- and embedded systems.
-"""
-
-# description for the busybox specific tools symlinks package
-symlinksbusybox_info = """
-Package: busybox-symlinks-busybox
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Description: BusyBox specific symlinks
- BusyBox symlinks for utilities without counterparts in Debian.
- These are in separate package because they aren't essentials
- (e.g. needed for system package upgrades).
-"""
-
-# descriptions for the other busybox symlinks packages
-symlinksother_info = """
-Package: busybox-symlinks-%s
-Architecture: all
-Depends: busybox (= ${binary:Version})
-Provides: %s
-Replaces: %s
-Conflicts: %s
-Description: BusyBox symlinks to provide '%s'
- BusyBox symlinks for utilities corresponding to '%s' package.
-"""
-
-# List of packages that are in Debian Lenny marked as essential:
-# awk '/Package:/{pkg=$2} /Essential: yes/{printf("\"%s\",\n", pkg)}' < /var/lib/dpkg/status|sort
-#
-# Because we don't have "bash", but BusyBox provides "ash", I've set
-# that as Essential (instead of Bash) and specifically divert "/bin/sh"
-# in code below if that is included into Busybox links (in Ubuntu
-# "dash" does same kind of divert).
-#
-# Some essential packages in Debian "provide" packages that in earlier
-# Debian versions were in separate packages.  These aren't listed here
-# because they're unnecessary:
-# 
-# * coreutils provides: fileutils, shellutils, textutils
-#   -> no explicit dependencies for these in Lenny.  In Etch "latex2html"
-#      requires "fileutils" without having "coreutils" as an alternative,
-#      but that's a versioned dependency that wouldn't work with
-#      BusyBox anyway
-# 
-# * util-linux provides: linux32, schedutils
-#   -> these were separate packages still in Etch, but as nothing explicitly
-#      depends on them in Etch or Lenny and Maemo BusyBox configuration
-#      doesn't include binaries from Etch linux32 or schedutils, they
-#      don't need to be declared
-# 
-# * grep provides: rgrep
-#   -> there are no dependencies to "rgrep" in Etch or Lenny and as it
-#      cannot even be enabled in BusyBox, it doesn't need to be declared
-essentialpackages = (
-    'ash',          # Maemo special case, see above
-    'base-files',
-    'base-passwd',
-    'bash',
-    'bsdutils',
-    'coreutils',
-    'debianutils',
-    'diffutils',
-    'dpkg',
-    'e2fsprogs',
-    'findutils',
-    'grep',
-    'gzip',
-    'hostname',
-    'login',
-    'mount',
-    'ncurses-base',
-    'ncurses-bin',
-    'perl-base',
-    'sed',
-    'sysvinit',
-    'sysvinit-utils',
-    'tar',
-    'util-linux'
-)
-
-
-import os, sys
-stderr = sys.stderr.write
-
-def error_exit(msg):
-    "exit with given error message"
-    sys.stderr.write("\nERROR: %s\n\n" % msg)
-    sys.exit(1)
-
-def check_file(filename, filetype):
-    "exit if given filename doesn't exist or isn't a file"
-    if os.path.isfile(filename):
-        return
-    error_exit("%s file '%s' doesn't exist!" % (filetype, filename))
-
-
-def read_mappings(filename):
-    """read Debian mappings file and return dicts for
-    - package = [list of binaries]
-    - binary = (package, [paths/alternative info])
-    """
-    check_file(filename, "Debian mappings")
-    binaries = {}
-    packages = {}
-    for line in open(filename):
-        line = line.strip()
-        if (not line) or (line[0] == '#'):
-            continue
-        package, data = line.split(":")
-       paths = data.strip().split()
-       binary = os.path.basename(paths[0].strip())
-       binaries[binary] = (package, paths)
-        if package not in packages:
-            packages[package] = []
-        packages[package].append(binary)
-    return packages, binaries
-
-
-def read_links(filename):
-    "read busybox.links file and return a list of binary names"
-    check_file(filename, "links")
-    items = []
-    for line in open(filename):
-        line = line.strip()
-        if not line:
-            continue
-        items.append(os.path.basename(line))
-    items.sort()
-    return items
-
-
-def read_config(filename):
-    "read BusyBox config and return list of enabled applets"
-    check_file(filename, "BusyBox config")
-    applets = []
-    for line in open(filename):
-        if not line.startswith("CONFIG_"):
-            continue
-       if line.startswith("CONFIG_LFS"):
-           continue
-        # stuff after CONFIG_
-        config = line[7:].split('=')[0]
-        if '_' in config:
-            # BB applet names don't have '_'
-            continue
-        applets.append(config.lower())
-    
-    # remove busybox config crap
-    if "debug" in applets:
-        applets.remove("debug")
-    if "prefix" in applets:
-        applets.remove("prefix")
-
-    # some applets provide multiple binary symlinks, fix
-    if "swaponoff" in applets:
-        applets.remove("swaponoff")
-        applets.append("swapoff")
-        applets.append("swapon")
-    if "ifupdown" in applets:
-        applets.remove("ifupdown")
-        applets.append("ifdown")
-        applets.append("ifup")
-    
-    applets.sort()
-    return applets
-
-
-def tools2packages(tools, binaries):
-    """match BB tools to Debian binaries and return list of
-       corresponding packages.  Assert that each binary is
-       associated to some package ("sh" is special case)."""
-    missing = []
-    packages = {}
-    for tool in tools:
-        if tool in binaries:
-            package = binaries[tool][0]
-            if package not in packages:
-                packages[package] = []
-            packages[package].append(tool)
-        # /bin/sh is special case
-        elif tool != "sh":
-            missing.append(tool)
-    if missing:
-        stderr("\nERROR: following tools didn't belong to any mapped package:\n")
-        for name in missing:
-            stderr("- %s\n" % name)
-        stderr("Update either %s::read_config() code or Debian mappings file!\n\n" % os.path.basename(sys.argv[0]))
-        sys.exit(1)
-    return packages
-
-
-def check_packages(packages, bbpackages):
-    "warn if packages provided by BB are missing tools that BB could include"
-    missing = {}
-    for package in bbpackages.keys():
-        if package == "busybox":
-            # ignore busybox specific tools
-            continue
-        for binary in packages[package]:
-            if binary not in bbpackages[package]:
-                if package not in missing:
-                    missing[package] = []
-                missing[package].append(binary)
-    for package in missing.keys():
-        stderr("\nWARNING: '%s' package could include also:\n" % package)
-        for binary in missing[package]:
-            stderr("  - %s\n" % binary)
-
-
-def collect_packages(bbpackages, tools, binaries):
-    "return lists of packages BB conflicts with, provides and symlinks"
-    base = {} # directly to Busybox package
-    extras = {} # alternatives for Busybox package
-    symlinks = {} # symlinks/alteratives for symlink packages
-
-    for package, tools in bbpackages.items():
-        # package is essential -> goes to busybox itself
-        if package in essentialpackages:
-            base[package] = tools
-            continue
-
-        allalternatives = True
-        # check whether all tools from given pkg are alternatives
-        for tool in tools:
-            toolinfo = binaries[tool][1]
-            if len(toolinfo) < 2 or toolinfo[1] != "alternative":
-                allalternatives = False
-
-        if allalternatives:
-            # only alternatives -> can be provided by busybox itself
-            extras[package] = tools
-        else:
-            # otherwise a separate symlinks package
-            symlinks[package] = tools
-     
-    return base, extras, symlinks
-
-
-def collect_links_alternatives(tools, binaries, links, postinst):
-    "go through given packages and set/update links & postinst contents"
-    for tool in tools:
-        toolinfo = binaries[tool][1]
-        count = len(toolinfo)
-        if count > 1 and toolinfo[1] == "alternative":
-            target = "/bin/busybox"
-            link = toolinfo[0]
-            if count > 2:
-                priority = int(toolinfo[2])
-                if count > 3:
-                    # alternative having different name than BB symlink
-                    # means that alternative needs to point to BB symlink
-                    # instead of directly to BB so that BB knows which
-                    # applet to use for the alternative (e.g. pager):
-                    #  tool -> altlink -> target -> busybox
-                    links.append(link)
-                    target = '/' + link
-                    link = toolinfo[3]
-                    tool = os.path.basename(link)
-            else:
-                priority = 1
-            postinst.append(('/' + link, tool, target, priority))
-        else:
-            for link in toolinfo:
-                links.append(link)
-
-
-def write_links(package, links):
-    "write given package links file"
-    if not links:
-        return
-    name = "%s.links" % package
-    print "-", name
-    linksfile = open(name, "w")
-    if not linksfile:
-        error_exit("opening/writing package '%s' links file failed")
-    for link in links:
-        linksfile.write("bin/busybox %s\n" % link)
-    linksfile.close()
-
-
-def write_alternatives(package, alternatives, diverts=[]):
-    "write given alternatives updates for given package postinst/prerm files"
-    if not (alternatives or diverts):
-        return
-    
-    name = "%s.postinst" % package
-    print "-", name
-    postinst = open(name, "w")
-    if not postinst:
-        error_exit("opening/writing package '%s' postinst file failed")
-    
-    postinst.write("#!/bin/sh\nset -e\n")
-    for link,tool,target,priority in alternatives:
-        postinst.write("update-alternatives --install %s %s %s %d\n" % (link, tool, target, priority))
-    for divert in diverts:
-        # Note: as dpkg-divert --rename doesn't work as described
-        # in the manual page, it's not used here
-        postinst.write("dpkg-divert --package %s --add %s\n" % (package, divert))
-    postinst.close()
-    
-    name = "%s.prerm" % package
-    print "-", name
-    prerm = open(name, "w")
-    if not prerm:
-        error_exit("opening/writing package '%s' prerm file failed")
-    
-    prerm.write("#!/bin/sh\nset -e\n")
-    for link,tool,target,priority in alternatives:
-        prerm.write("update-alternatives --remove %s %s\n" % (tool, target))
-    for divert in diverts:
-        prerm.write("dpkg-divert --package %s --remove %s\n" % (package, divert))
-    prerm.close()
-
-
-def create_links_alternatives(bbpackages, bblinks, binaries):
-    """generate .postinst files for tools needing alternatives,
-    and .links files for required symlinks. If package would include
-    only alternatives, just add them to busybox package as provides
-    (without conflicts/replaces).
-    
-    Return lists of packages going to busybox package with which
-    it needs to conflict with, ones it can just provide (as they're
-    handled as alternatives), and packages that would contain
-    separate non-essential symlink packages."""
-
-    base, extras, symlinks = collect_packages(bbpackages, bblinks, binaries)
-    
-    links = []
-    alternatives = []
-    for package,tools in base.items():
-        collect_links_alternatives(tools, binaries, links, alternatives)
-    # add extra alternatives to alternatives
-    for package,tools in extras.items():
-        collect_links_alternatives(tools, binaries, links, alternatives)
-    if "bin/sh" in links:
-        # /bin/sh is special case as in Maemo, BusyBox should be /bin/sh.
-        # In Debian it's linked by Bash.  In Ubuntu /bin/sh is diverted
-        # by Dash (by default), so let's do divert here too.
-        divert = ["/bin/sh"]
-    else:
-        divert = []
-    write_alternatives("busybox", alternatives, divert)
-    links.sort() # nicer to read/verify
-    write_links("busybox", links)
-
-    for package,tools in symlinks.items():
-        links = []
-        alternatives = []
-        collect_links_alternatives(tools, binaries, links, alternatives)
-        pkgname = "busybox-symlinks-%s" % package
-        write_alternatives(pkgname, alternatives)
-        write_links(pkgname, links)
-
-    return base.keys(), extras.keys(), symlinks.keys()
-
-
-def create_control(base, extraprovides, symlinks):
-    "generate busybox control file based on given packages lists"
-    # add same list of packages for provides, replaces & conflicts
-    args = {}
-    base.sort()
-    args['conflicts'] = ", ".join(base)
-    args['replaces'] = args['conflicts']
-    if extraprovides:
-        # busybox provides some extra packages with alternatives
-        provides = base + extraprovides
-        provides.sort()
-        args['provides'] = ", ".join(provides)
-    else:
-        args['provides'] = args['conflicts']
-    
-    print "- control"
-    control = open("control", "w")
-    if not control:
-        error_exit("opening/writing package 'control' file failed")
-    control.write(basepackages_info % args)
-
-    # special case package with it's own description
-    if "busybox" in symlinks:
-        symlinks.remove("busybox")
-        control.write(symlinksbusybox_info)
-
-    # other symlink packages descriptions
-    symlinks.sort()
-    for package in symlinks:
-        args = 6*(package,)
-        control.write(symlinksother_info % args)
-    control.close()
-
-
-def process_args(argv):
-    if len(sys.argv) == 4 and sys.argv[1] in ("-c", "-l"):
-        print "\nPARSING..."
-        if sys.argv[1] == "-l":
-            tools = read_links(sys.argv[2])
-        else:
-            tools = read_config(sys.argv[2])
-       packages, binaries = read_mappings(sys.argv[3])
-        bbpackages = tools2packages(tools, binaries)
-        check_packages(packages, bbpackages)
-        print '\n', 66*'-', "\n\nWRITING:"
-        base, extras, symlinks = create_links_alternatives(bbpackages, tools, binaries)
-        create_control(base, extras, symlinks)
-        print
-    else:
-       script = os.path.basename(sys.argv[0])
-       print """
-Outputs BusyBox Debian package control/links/postinst/prerm
-files based on the input files.
-
-usage: %s <-c bb.config|-l bb.links> <mappings file>
-
-Example of first invocation using BusyBox config file:
-       %s -c config.maemo debian-mappings.txt
-
-Example of later iterations using the BusyBox generated symlinks file:
-       %s -l busybox.links debian-mappings.txt
-""" % (script, script, script)
-       sys.exit(1)
-
-if __name__ == "__main__":
-    process_args(sys.argv)
diff --git a/debian/scripts/debian-mappings.txt b/debian/scripts/debian-mappings.txt
deleted file mode 100644 (file)
index d1e4c29..0000000
+++ /dev/null
@@ -1,521 +0,0 @@
-#
-# Mapping between Busybox binary name and in which package
-# that binary is in Debian.  The format is following:
-#   <package-name>: <path/to/tool> [<path/to/tool2> [<path/to/tool3>...]]
-# or:
-#   <package-name>: <path/to/tool> alternative [priority [altname(s)]]
-#
-# For example:
-# - normal case:
-#      coreutils: bin/cp
-# - multiple symlinks for given tool:
-#      iproute: bin/ip sbin/ip
-#      debianutils: bin/which usr/bin/which
-# - tool is a Debian alternative (by default has priority of 1):
-#      awk: usr/bin/awk alternative
-# - tool has regular symlink and alternative(s):
-#      util-linux: usr/bin/more alternative 10 usr/bin/pager
-#      less: usr/bin/less alternative 20 usr/bin/pager
-#
-# Lines starting with '#' are comments, empty lines are ignored
-#
-# Updating this file:
-# - If create-control.py complains that this is out of date,
-#   check with the http://packages.debian.org/ file search form
-#   in which package and with which path the given binary is
-#   in Debian.
-# - Install that package and check e.g. from the binary manual
-#   page and BusyBox commands references that the BB utility
-#   is "compatible enough" before listing it here. If it's not
-#   compatible, the BB utility should be renamed:
-#      http://busybox.net/downloads/BusyBox.html
-# 
-# Notes:
-# - In Debian "bash" provides /bin/sh.  In Ubuntu "dash" diverts
-#   bin/sh for itself.  For BusyBox I've set "ash" to provide /bin/sh.
-#   create-control.py sets "ash" to be Essential (unlike on Debian)
-#   to include its symlinks to busybox package, it also does the
-#   the required divert.
-# - The smaller BusyBox shells: lash, hush, msh, aren't really
-#   Bourne shell compatible, please use ash (it's stripped down
-#   version of Debian dash which is improved version of BSD Ash).
-#   http://lists.debian.org/debian-boot/2001/07/msg00104.html
-# - All paths are relative as that's what the Debian dh_link eats
-#
-# Hints:
-# - To get the list of package names listed here, use:
-#   awk '/#/{next}/:/{print $1}' debian-mappings.txt|tr -d :|sort -u
-# - To get list of alternatives:
-#   grep alternative debian-mappings.txt|grep -v '^#'
-# - If a package listed below contains only Debian alternatives,
-#   corresponding virtual package name is used as the package name here:
-#   http://www.debian.org/doc/packaging-manuals/virtual-package-names-list.txt
-#   See "awk", "man-browser" and "editor"
-#
-# TODO:
-# - Get Busybox to rename tools that conflict with unrelated
-#   Debian tools (see things commented as "Conflicts")
-# - How to specify that inet services (like ftp, telnet)
-#   should be registered for inetd (in postinst)?
-# - How to specify that services (like crond, inetd) should
-#   be registered for Upstart (in postinst)?
-# 
-# Changelog:
-# - 2008-12-09: link targets are relative (removed first '/').
-#   comment updates
-# - 2008-12-04: support alternatives and multiple symlinks
-#   and many other updates
-# - 2008-07-11: updated against Etch & packages.debian.org
-#   and Busybox 1.11.0
-
-
-# These busybox symlinks don't have a matching binary in Debian
-# and are therefore the same as in busybox.links.
-# 
-# Some may provides same functionality as something else in Debian,
-# but the compatibility should be checked before renaming.
-#
-# For rest of the Debian package mappings the binary path is same
-# as in Debian instead of what's in busybox.links file generated
-# by the Busybox build.
-
-# same as "["
-busybox: usr/bin/[[
-# Same as "cat -v"
-busybox: usr/bin/catv
-# does this correspond to Debian cron?
-busybox: usr/sbin/crond
-busybox: usr/sbin/dhcprelay
-busybox: usr/sbin/dnsd
-busybox: bin/dumpkmap
-busybox: usr/bin/ether-wake
-busybox: usr/sbin/fakeidentd
-# does this correspond to Debian splash?
-busybox: sbin/fbsplash
-# maemo addition
-busybox: bin/fsync
-busybox: usr/bin/ftpget
-# Conflicts: camstream
-# (Busybox binary has regular FTP, Debian one camera additions)
-busybox: usr/bin/ftpput
-# Provides: httpd?
-busybox: usr/sbin/httpd
-busybox: sbin/ifenslave
-busybox: sbin/inotifyd
-# same as "ip addr"
-busybox: bin/ipaddr
-# same as " ip link"
-busybox: bin/iplink
-# same as "ip route"
-busybox: bin/iproute
-# same as "ip rule"
-busybox: bin/iprule
-busybox: usr/bin/length
-busybox: linuxrc
-busybox: usr/bin/loadfont
-busybox: sbin/loadkmap
-busybox: sbin/logread
-# does this correspond to Debian makedev?
-busybox: sbin/makedevs
-# does this correspond to Debian udev?
-busybox: sbin/mdev
-# does this correspond to Debian minicom?
-busybox: usr/bin/microcom
-busybox: usr/bin/nmeter
-busybox: bin/pipe_progress
-# Conflicts: pscan (Busybox binary scans ports, Debian one C-source)?
-busybox: usr/bin/pscan
-busybox: sbin/raidautorun
-busybox: usr/bin/readahead
-busybox: sbin/setconsole
-# Provides: ftp-server?
-busybox: usr/sbin/tftpd
-busybox: usr/bin/ttysize
-busybox: bin/usleep
-busybox: bin/volumeid
-# Conflicts: whois
-busybox: usr/bin/mkpasswd
-# Conflicts: eject
-busybox: usr/bin/volname
-
-adduser: usr/sbin/addgroup
-adduser: usr/sbin/adduser
-adduser: usr/sbin/delgroup
-adduser: usr/sbin/deluser
-
-adjtimex: usr/bin/adjtimex
-
-# /bin/sh is special case, see Notes
-ash: bin/ash
-ash: bin/sh
-#bash: bin/sh
-
-# awk is a virtual package name, this is OK for alternatives
-awk: usr/bin/awk alternative
-
-binutils: usr/bin/ar
-binutils: usr/bin/strings
-
-bridge-utils: usr/bin/brctl
-
-bsdmainutils: usr/bin/cal
-bsdmainutils: usr/bin/hd
-bsdmainutils: usr/bin/hexdump
-
-bsdutils: usr/bin/logger
-bsdutils: usr/bin/renice
-bsdutils: usr/bin/script
-bsdutils: usr/bin/scriptreplay
-bsdutils: usr/bin/wall
-
-bzip2: bin/bunzip2
-bzip2: bin/bzcat
-bzip2: bin/bzip2
-
-console-tools: bin/fgconsole
-console-tools: usr/bin/chvt
-console-tools: usr/bin/deallocvt
-console-tools: usr/bin/kbd_mode
-console-tools: usr/bin/openvt
-console-tools: usr/bin/setkeycodes
-console-tools: usr/bin/setlogcons
-console-tools: usr/bin/showkey
-
-coreutils: bin/cat
-coreutils: bin/chgrp
-coreutils: bin/chmod
-coreutils: bin/chown
-coreutils: bin/cp
-coreutils: bin/date
-coreutils: bin/dd
-coreutils: bin/df
-coreutils: bin/echo
-coreutils: bin/false
-coreutils: bin/ln
-coreutils: bin/ls
-coreutils: bin/mkdir
-coreutils: bin/mknod
-coreutils: bin/mktemp
-coreutils: bin/mv
-coreutils: bin/pwd
-coreutils: bin/readlink
-coreutils: bin/rm
-coreutils: bin/rmdir
-coreutils: bin/sleep
-coreutils: bin/stty
-coreutils: bin/sync
-coreutils: bin/touch
-coreutils: bin/true
-coreutils: bin/uname
-coreutils: usr/bin/[
-coreutils: usr/bin/basename
-coreutils: usr/bin/chcon
-coreutils: usr/bin/cksum
-coreutils: usr/bin/comm
-coreutils: usr/bin/cut
-coreutils: usr/bin/dirname
-coreutils: usr/bin/du
-coreutils: usr/bin/env
-coreutils: usr/bin/expand
-coreutils: usr/bin/expr
-coreutils: usr/bin/fold
-coreutils: usr/bin/head
-coreutils: usr/bin/hostid
-coreutils: usr/bin/id
-coreutils: usr/bin/install
-coreutils: usr/bin/logname
-coreutils: usr/bin/md5sum
-coreutils: usr/bin/mkfifo
-coreutils: usr/bin/nice
-coreutils: usr/bin/nohup
-coreutils: usr/bin/od
-coreutils: usr/bin/printenv
-coreutils: usr/bin/printf
-coreutils: usr/bin/runcon
-coreutils: usr/bin/seq
-coreutils: usr/bin/sha1sum
-coreutils: usr/bin/sha256sum
-coreutils: usr/bin/sha512sum
-coreutils: usr/bin/sort
-coreutils: usr/bin/split
-coreutils: usr/bin/stat
-coreutils: usr/bin/sum
-coreutils: usr/bin/tac
-coreutils: usr/bin/tail
-coreutils: usr/bin/tee
-coreutils: usr/bin/test
-coreutils: usr/bin/timeout
-coreutils: usr/bin/tr
-coreutils: usr/bin/tty
-coreutils: usr/bin/unexpand
-coreutils: usr/bin/uniq
-coreutils: usr/bin/wc
-coreutils: usr/bin/who
-coreutils: usr/bin/whoami
-coreutils: usr/bin/yes
-coreutils: usr/sbin/chroot
-
-cpio: bin/cpio
-cpio: bin/mt alternative
-
-cron: usr/bin/crontab
-
-# sid/later
-daemontools: usr/bin/envdir
-daemontools: usr/bin/envuidgid
-daemontools: usr/bin/setuidgid
-daemontools: usr/bin/softlimit
-
-dc: usr/bin/dc
-
-debianutils: bin/run-parts
-debianutils: bin/which usr/bin/which
-
-diffutils: usr/bin/cmp
-diffutils: usr/bin/diff
-
-dnsutils: usr/bin/nslookup
-
-dosfstools: sbin/mkdosfs
-dosfstools: sbin/mkfs.vfat
-
-dpkg: sbin/start-stop-daemon
-
-e2fsprogs: sbin/findfs
-e2fsprogs: sbin/fsck
-e2fsprogs: sbin/mke2fs
-e2fsprogs: sbin/mkfs.ext2
-e2fsprogs: sbin/tune2fs
-e2fsprogs: usr/bin/chattr
-e2fsprogs: usr/bin/lsattr
-
-ed: bin/ed
-
-eject: usr/bin/eject
-
-ssmtp: usr/sbin/sendmail
-
-fbset: bin/fbset
-
-fdflush: bin/fdflush
-
-fetchmail: usr/bin/fetchmail
-
-findutils: usr/bin/find
-findutils: usr/bin/xargs
-
-grep: bin/egrep
-grep: bin/fgrep
-grep: bin/grep
-
-gzip: bin/gunzip
-gzip: bin/gzip
-gzip: bin/uncompress
-gzip: bin/zcat
-
-hdparm: sbin/hdparm
-
-hostname: bin/hostname
-hostname: bin/dnsdomainname
-
-ifupdown: sbin/ifdown
-ifupdown: sbin/ifup
-
-initscripts: bin/mountpoint
-
-ipcalc: usr/bin/ipcalc
-
-iproute: bin/ip sbin/ip
-
-ipsvd: usr/bin/tcpsvd
-ipsvd: usr/bin/udpsvd
-
-iputils-arping: usr/bin/arping
-
-iputils-ping: bin/ping
-iputils-ping: bin/ping6
-
-klogd: sbin/klogd
-
-less: usr/bin/less alternative 20 usr/bin/pager
-
-loadlin: usr/bin/freeramdisk
-
-login: bin/login
-login: bin/su
-
-lpr: usr/bin/lpd
-lpr: usr/bin/lpq
-lpr: usr/bin/lpr
-
-lrzsz: usr/bin/rx
-
-lzma: usr/bin/lzcat
-lzma: usr/bin/lzma
-lzma: usr/bin/unlzma
-
-lzop: usr/bin/lzop
-lzop: usr/bin/lzopcat
-lzop: usr/bin/unlzop
-
-# man-browser is a virtual package name, this is OK for alternatives
-man-browser: usr/bin/man alternative
-
-module-init-tools: sbin/depmod
-module-init-tools: sbin/insmod
-module-init-tools: sbin/lsmod
-module-init-tools: sbin/modprobe
-module-init-tools: sbin/modinfo
-module-init-tools: sbin/rmmod
-
-mount: bin/mount
-mount: bin/umount
-mount: sbin/losetup
-mount: sbin/swapoff
-mount: sbin/swapon
-
-mtd-utils: usr/sbin/flashcp
-mtd-utils: usr/sbin/flash_eraseall
-mtd-utils: usr/sbin/flash_lock
-mtd-utils: usr/sbin/flash_unlock
-mtd-utils: usr/sbin/ubiattach
-mtd-utils: usr/sbin/ubidetach
-
-ncurses-bin: usr/bin/clear
-ncurses-bin: usr/bin/reset
-
-# starting from lenny, netcat is a virtual package (for upgrade reasons)
-netcat: bin/nc alternative
-
-net-tools: bin/netstat
-net-tools: sbin/ifconfig
-net-tools: sbin/iptunnel
-net-tools: sbin/nameif
-net-tools: sbin/route
-net-tools: sbin/slattach
-net-tools: usr/sbin/arp
-
-openbsd-inetd: usr/sbin/inetd
-
-passwd: usr/bin/passwd
-passwd: usr/sbin/chpasswd
-
-patch: usr/bin/patch
-
-ppp: usr/sbin/chat
-
-procps: bin/kill
-procps: bin/ps
-procps: sbin/sysctl
-procps: usr/bin/free
-procps: usr/bin/pgrep
-procps: usr/bin/pkill
-procps: usr/bin/top
-procps: usr/bin/uptime
-procps: usr/bin/watch
-
-psmisc: bin/fuser
-psmisc: usr/bin/killall
-
-rdate: usr/sbin/rdate
-
-realpath: usr/bin/realpath
-
-rpm: usr/bin/rpm
-rpm: usr/bin/rpm2cpio
-
-runit: usr/bin/chpst
-runit: usr/bin/runsv
-runit: usr/bin/runsvdir
-runit: usr/bin/sv
-runit: usr/bin/svlogd
-
-sed: bin/sed
-
-sharutils: usr/bin/uudecode
-sharutils: usr/bin/uuencode
-
-sysklogd: sbin/syslogd
-
-sysvinit: sbin/halt
-sysvinit: sbin/init
-sysvinit: sbin/poweroff
-sysvinit: sbin/reboot
-sysvinit: sbin/runlevel
-
-sysvinit-utils: bin/pidof
-sysvinit-utils: sbin/killall5
-sysvinit-utils: sbin/sulogin
-sysvinit-utils: usr/bin/last
-sysvinit-utils: usr/bin/mesg
-
-tar: bin/tar
-
-telnetd: usr/sbin/telnetd
-
-# telnet-client is a virtual package name, this is OK for alternatives
-telnet-client: usr/bin/telnet alternative
-
-tftp: usr/bin/tftp
-
-time: usr/bin/time
-
-tofrodos: usr/bin/dos2unix
-tofrodos: usr/bin/unix2dos
-
-traceroute: usr/bin/traceroute alternative
-
-udhcpc: usr/bin/udhcpc
-
-udhcpd: usr/bin/dumpleases
-udhcpd: usr/sbin/udhcpd
-
-unzip: usr/bin/unzip
-
-util-linux: bin/dmesg
-util-linux: bin/more alternative 10 usr/bin/pager
-util-linux: sbin/blkid
-util-linux: sbin/fdisk
-util-linux: sbin/fsck.minix
-util-linux: sbin/getty
-util-linux: sbin/hwclock
-util-linux: sbin/mkfs.minix
-util-linux: sbin/mkswap
-util-linux: sbin/pivot_root
-util-linux: sbin/switch_root
-util-linux: usr/bin/flock
-util-linux: usr/bin/rev
-util-linux: usr/bin/rtcwake
-util-linux: usr/sbin/rdev
-# before lenny this binary was in schedutils package
-util-linux: usr/bin/chrt
-util-linux: usr/bin/fdformat
-util-linux: usr/bin/getopt
-util-linux: usr/bin/ionice
-util-linux: usr/bin/ipcrm
-util-linux: usr/bin/ipcs
-# before lenny these two were in linux32 package
-util-linux: usr/bin/linux32
-util-linux: usr/bin/linux64
-# this didn't exist before lenny
-util-linux: usr/bin/setarch
-util-linux: usr/bin/setsid
-# before lenny this binary was in schedutils package
-util-linux: usr/bin/taskset
-util-linux: usr/sbin/readprofile
-util-linux: sbin/blockdev
-
-# editor is (an obsolete) virtual package name, this is OK for alternatives
-editor: usr/bin/vi alternative
-
-vlan: sbin/vconfig
-
-vlock: usr/bin/vlock
-
-watchdog: usr/sbin/watchdog
-
-wget: usr/bin/wget
-
-xterm: usr/bin/resize
-
-zcip: usr/bin/zcip
diff --git a/debian/sfdisk/Makefile b/debian/sfdisk/Makefile
deleted file mode 100644 (file)
index 36ad891..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-
-OBJS = disksize.o i386_sys_types.o partname.o sfdisk.o 
-
-#OBJS = sfdisk.o
-
-CFLAGS ?= -fomit-frame-pointer -Os
-
-all: sfdisk
-
-
-sfdisk: $(OBJS)
-       $(CC) -o $@ $(OBJS)
-
-clean:
-       rm -f *.o *~ sfdisk
diff --git a/debian/sfdisk/common.h b/debian/sfdisk/common.h
deleted file mode 100644 (file)
index 7c1c3cd..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/* common stuff for fdisk, cfdisk, sfdisk */
-
-/* including <linux/fs.h> fails */
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#define BLKRRPART    _IO(0x12,95)    /* re-read partition table */
-#define BLKGETSIZE   _IO(0x12,96)    /* return device size */
-#define BLKFLSBUF    _IO(0x12,97)    /* flush buffer cache */
-#define BLKSSZGET    _IO(0x12,104)   /* get block device sector size */
-#define BLKGETSIZE64 _IOR(0x12,114,size_t)     /* size in bytes */
-
-/* including <linux/hdreg.h> also fails */
-struct hd_geometry {
-      unsigned char heads;
-      unsigned char sectors;
-      unsigned short cylinders;
-      unsigned long start;
-};
-
-#define HDIO_GETGEO            0x0301  /* get device geometry */
-
-
-struct systypes {
-       unsigned char type;
-       char *name;
-};
-
-extern struct systypes i386_sys_types[];
-
-extern char *partname(char *dev, int pno, int lth);
-
-int disksize(int fd, unsigned long long *sectors);
diff --git a/debian/sfdisk/defines.h b/debian/sfdisk/defines.h
deleted file mode 100644 (file)
index 9aa106d..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#define UTIL_LINUX_VERSION "2.12r"
-#define util_linux_version "util-linux-2.12r"
-
diff --git a/debian/sfdisk/disksize.c b/debian/sfdisk/disksize.c
deleted file mode 100644 (file)
index f5687d2..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#include "common.h"
-
-int disksize(int fd, unsigned long long *sectors) {
-       int err;
-       long sz;
-       long long b;
-
-       err = ioctl(fd, BLKGETSIZE, &sz);
-       if (err)
-               return err;
-       err = ioctl(fd, BLKGETSIZE64, &b);
-       if (err || b == 0 || b == sz)
-               *sectors = sz;
-       else
-               *sectors = (b >> 9);
-       return 0;
-}
diff --git a/debian/sfdisk/i386_sys_types.c b/debian/sfdisk/i386_sys_types.c
deleted file mode 100644 (file)
index e08a9b2..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/* DOS partition types */
-#include "common.h"
-#include "nls.h"
-
-struct systypes i386_sys_types[] = {
-       {0x00, N_("Empty")},
-       {0x01, N_("FAT12")},
-       {0x02, N_("XENIX root")},
-       {0x03, N_("XENIX usr")},
-       {0x04, N_("FAT16 <32M")},
-       {0x05, N_("Extended")},         /* DOS 3.3+ extended partition */
-       {0x06, N_("FAT16")},            /* DOS 16-bit >=32M */
-       {0x07, N_("HPFS/NTFS")},        /* OS/2 IFS, eg, HPFS or NTFS or QNX */
-       {0x08, N_("AIX")},              /* AIX boot (AIX -- PS/2 port) or SplitDrive */
-       {0x09, N_("AIX bootable")},     /* AIX data or Coherent */
-       {0x0a, N_("OS/2 Boot Manager")},/* OS/2 Boot Manager */
-       {0x0b, N_("W95 FAT32")},
-       {0x0c, N_("W95 FAT32 (LBA)")},/* LBA really is `Extended Int 13h' */
-       {0x0e, N_("W95 FAT16 (LBA)")},
-       {0x0f, N_("W95 Ext'd (LBA)")},
-       {0x10, N_("OPUS")},
-       {0x11, N_("Hidden FAT12")},
-       {0x12, N_("Compaq diagnostics")},
-       {0x14, N_("Hidden FAT16 <32M")},
-       {0x16, N_("Hidden FAT16")},
-       {0x17, N_("Hidden HPFS/NTFS")},
-       {0x18, N_("AST SmartSleep")},
-       {0x1b, N_("Hidden W95 FAT32")},
-       {0x1c, N_("Hidden W95 FAT32 (LBA)")},
-       {0x1e, N_("Hidden W95 FAT16 (LBA)")},
-       {0x24, N_("NEC DOS")},
-       {0x39, N_("Plan 9")},
-       {0x3c, N_("PartitionMagic recovery")},
-       {0x40, N_("Venix 80286")},
-       {0x41, N_("PPC PReP Boot")},
-       {0x42, N_("SFS")},
-       {0x4d, N_("QNX4.x")},
-       {0x4e, N_("QNX4.x 2nd part")},
-       {0x4f, N_("QNX4.x 3rd part")},
-       {0x50, N_("OnTrack DM")},
-       {0x51, N_("OnTrack DM6 Aux1")}, /* (or Novell) */
-       {0x52, N_("CP/M")},             /* CP/M or Microport SysV/AT */
-       {0x53, N_("OnTrack DM6 Aux3")},
-       {0x54, N_("OnTrackDM6")},
-       {0x55, N_("EZ-Drive")},
-       {0x56, N_("Golden Bow")},
-       {0x5c, N_("Priam Edisk")},
-       {0x61, N_("SpeedStor")},
-       {0x63, N_("GNU HURD or SysV")}, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
-       {0x64, N_("Novell Netware 286")},
-       {0x65, N_("Novell Netware 386")},
-       {0x70, N_("DiskSecure Multi-Boot")},
-       {0x75, N_("PC/IX")},
-       {0x80, N_("Old Minix")},        /* Minix 1.4a and earlier */
-       {0x81, N_("Minix / old Linux")},/* Minix 1.4b and later */
-       {0x82, N_("Linux swap / Solaris")},
-       {0x83, N_("Linux")},
-       {0x84, N_("OS/2 hidden C: drive")},
-       {0x85, N_("Linux extended")},
-       {0x86, N_("NTFS volume set")},
-       {0x87, N_("NTFS volume set")},
-       {0x88, N_("Linux plaintext")},
-       {0x8e, N_("Linux LVM")},
-       {0x93, N_("Amoeba")},
-       {0x94, N_("Amoeba BBT")},       /* (bad block table) */
-       {0x9f, N_("BSD/OS")},           /* BSDI */
-       {0xa0, N_("IBM Thinkpad hibernation")},
-       {0xa5, N_("FreeBSD")},          /* various BSD flavours */
-       {0xa6, N_("OpenBSD")},
-       {0xa7, N_("NeXTSTEP")},
-       {0xa8, N_("Darwin UFS")},
-       {0xa9, N_("NetBSD")},
-       {0xab, N_("Darwin boot")},
-       {0xb7, N_("BSDI fs")},
-       {0xb8, N_("BSDI swap")},
-       {0xbb, N_("Boot Wizard hidden")},
-       {0xbe, N_("Solaris boot")},
-       {0xbf, N_("Solaris")},
-       {0xc1, N_("DRDOS/sec (FAT-12)")},
-       {0xc4, N_("DRDOS/sec (FAT-16 < 32M)")},
-       {0xc6, N_("DRDOS/sec (FAT-16)")},
-       {0xc7, N_("Syrinx")},
-       {0xda, N_("Non-FS data")},
-       {0xdb, N_("CP/M / CTOS / ...")},/* CP/M or Concurrent CP/M or
-                                          Concurrent DOS or CTOS */
-       {0xde, N_("Dell Utility")},     /* Dell PowerEdge Server utilities */
-       {0xdf, N_("BootIt")},           /* BootIt EMBRM */
-       {0xe1, N_("DOS access")},       /* DOS access or SpeedStor 12-bit FAT
-                                          extended partition */
-       {0xe3, N_("DOS R/O")},          /* DOS R/O or SpeedStor */
-       {0xe4, N_("SpeedStor")},        /* SpeedStor 16-bit FAT extended
-                                          partition < 1024 cyl. */
-       {0xeb, N_("BeOS fs")},
-       {0xee, N_("EFI GPT")},          /* Intel EFI GUID Partition Table */
-       {0xef, N_("EFI (FAT-12/16/32)")},/* Intel EFI System Partition */
-       {0xf0, N_("Linux/PA-RISC boot")},/* Linux/PA-RISC boot loader */
-       {0xf1, N_("SpeedStor")},
-       {0xf4, N_("SpeedStor")},        /* SpeedStor large partition */
-       {0xf2, N_("DOS secondary")},    /* DOS 3.3+ secondary */
-       {0xfd, N_("Linux raid autodetect")},/* New (2.2.x) raid partition with
-                                              autodetect using persistent
-                                              superblock */
-       {0xfe, N_("LANstep")},          /* SpeedStor >1024 cyl. or LANstep */
-       {0xff, N_("BBT")},              /* Xenix Bad Block Table */
-       { 0, 0 }
-};
diff --git a/debian/sfdisk/nls.h b/debian/sfdisk/nls.h
deleted file mode 100644 (file)
index 99458d6..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-int main(int argc, char *argv[]);
-
-#include "defines.h"           /* for HAVE_locale_h */
-
-#ifndef PACKAGE
-#define PACKAGE        "util-linux"
-#endif
-
-#ifndef LOCALEDIR
-#define LOCALEDIR "/usr/share/locale"
-#endif
-
-#ifdef HAVE_locale_h
-# include <locale.h>
-#endif
-
-#if defined MAY_ENABLE_NLS && !defined DISABLE_NLS
-# include <libintl.h>
-# define _(Text) gettext (Text)
-# ifdef gettext_noop
-#  define N_(String) gettext_noop (String)
-# else
-#  define N_(String) (String)
-# endif
-#else
-# undef bindtextdomain
-# define bindtextdomain(Domain, Directory) /* empty */
-# undef textdomain
-# define textdomain(Domain) /* empty */
-# define _(Text) (Text)
-# define N_(Text) (Text)
-#endif
-
-
diff --git a/debian/sfdisk/partname.c b/debian/sfdisk/partname.c
deleted file mode 100644 (file)
index e4bc42b..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#include <ctype.h>
-#include <stdio.h>
-#include <string.h>
-#include "common.h"
-
-/*
- * return partition name - uses static storage unless buf is supplied
- */
-static char *
-partnamebf(char *dev, int pno, int lth, int bufsiz, char *bufp) {
-       static char buffer[80];
-       char *p;
-       int w, wp;
-
-       if (!bufp) {
-               bufp = buffer;
-               bufsiz = sizeof(buffer);
-       }
-
-       w = strlen(dev);
-       p = "";
-
-       if (isdigit(dev[w-1]))
-               p = "p";
-
-       /* devfs kludge - note: fdisk partition names are not supposed
-          to equal kernel names, so there is no reason to do this */
-       if (strcmp (dev + w - 4, "disc") == 0) {
-               w -= 4;
-               p = "part";
-       }
-
-       wp = strlen(p);
-               
-       if (lth) {
-               snprintf(bufp, bufsiz, "%*.*s%s%-2u",
-                        lth-wp-2, w, dev, p, pno);
-       } else {
-               snprintf(bufp, bufsiz, "%.*s%s%-2u", w, dev, p, pno);
-       }
-       return bufp;
-}
-
-char *
-partname(char *dev, int pno, int lth) {
-       return partnamebf(dev, pno, lth, 0, NULL);
-}
diff --git a/debian/sfdisk/sfdisk.c b/debian/sfdisk/sfdisk.c
deleted file mode 100644 (file)
index 7af841d..0000000
+++ /dev/null
@@ -1,3105 +0,0 @@
-/*
- * sfdisk version 3.0 - aeb - 950813
- *
- * Copyright (C) 1995  Andries E. Brouwer (aeb@cwi.nl)
- *
- * This program is free software. You can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation: either Version 1
- * or (at your option) any later version.
- *
- * A.V. Le Blanc (LeBlanc@mcc.ac.uk) wrote Linux fdisk 1992-1994,
- * patched by various people (faith@cs.unc.edu, martin@cs.unc.edu,
- * leisner@sdsp.mc.xerox.com, esr@snark.thyrsus.com, aeb@cwi.nl)
- * 1993-1995, with version numbers (as far as I have seen) 0.93 - 2.0e.
- * This program had (head,sector,cylinder) as basic unit, and was
- * (therefore) broken in several ways for the use on larger disks -
- * for example, my last patch (from 2.0d to 2.0e) was required
- * to allow a partition to cross cylinder 8064, and to write an
- * extended partition past the 4GB mark.
- *
- * The current program is a rewrite from scratch, and I started a
- * version numbering at 3.0.
- *     Andries Brouwer, aeb@cwi.nl, 950813
- *
- * Well, a good user interface is still lacking. On the other hand,
- * many configurations cannot be handled by any other fdisk.
- * I changed the name to sfdisk to prevent confusion. - aeb, 970501
- *
- * Changes:
- * 19990319 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> - i18n
- * 20040428 - Jeroen Dobbelaere <jeroen.dobbelaere@acunia.com> - added PACKED
- * 20040824 - David A. Wheeler <dwheeler@dwheeler.com> - warnings to stderr
- */
-
-#define PROGNAME "sfdisk"
-#define VERSION "3.08"
-#define DATE "040824"
-
-#include <stdio.h>
-#include <stdlib.h>            /* atoi, free */
-#include <stdarg.h>            /* varargs */
-#include <unistd.h>            /* read, write */
-#include <fcntl.h>             /* O_RDWR */
-#include <errno.h>             /* ERANGE */
-#include <string.h>            /* index() */
-#include <ctype.h>
-#include <getopt.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/utsname.h>
-#include <linux/unistd.h>      /* _syscall */
-#include "nls.h"
-#include "common.h"
-
-#define SIZE(a)        (sizeof(a)/sizeof(a[0]))
-
-/*
- * Table of contents:
- *  A. About seeking
- *  B. About sectors
- *  C. About heads, sectors and cylinders
- *  D. About system Ids
- *  E. About partitions
- *  F. The standard input
- *  G. The command line
- *  H. Listing the current situation
- *  I. Writing the new situation
- */
-int exit_status = 0;
-
-int force = 0;         /* 1: do what I say, even if it is stupid ... */
-int quiet = 0;         /* 1: suppress all warnings */
-/* IA-64 gcc spec file currently does -DLinux... */
-#undef Linux
-int Linux = 0;         /* 1: suppress warnings irrelevant for Linux */
-int DOS = 0;           /* 1: shift extended partitions by #sectors, not 1 */
-int DOS_extended = 0;  /* 1: use starting cylinder boundary of extd partn */
-int dump = 0;           /* 1: list in a format suitable for later input */
-int verify = 0;         /* 1: check that listed partition is reasonable */
-int no_write = 0;      /* 1: do not actually write to disk */
-int no_reread = 0;     /* 1: skip the BLKRRPART ioctl test at startup */
-int leave_last = 0;    /* 1: don't allocate the last cylinder */
-int opt_list = 0;
-char *save_sector_file = NULL;
-char *restore_sector_file = NULL;
-
-static void
-do_warn(char *s, ...) {
-    va_list p;
-
-    va_start(p, s);
-    fflush(stdout);
-    vfprintf(stderr, s, p);
-    fflush(stderr);
-    va_end(p);
-}
-
-static void
-warn(char *s, ...) {
-    va_list p;
-
-    va_start(p, s);
-    if (!quiet) {
-       fflush(stdout);
-       vfprintf(stderr, s, p);
-       fflush(stderr);
-    }
-    va_end(p);
-}
-
-static void
-error(char *s, ...) {
-    va_list p;
-
-    va_start(p, s);
-    fflush(stdout);
-    fprintf(stderr, "\n" PROGNAME ": ");
-    vfprintf(stderr, s, p);
-    fflush(stderr);
-    va_end(p);
-}
-
-static void
-fatal(char *s, ...) {
-    va_list p;
-
-    va_start(p, s);
-    fflush(stdout);
-    fprintf(stderr, "\n" PROGNAME ": ");
-    vfprintf(stderr, s, p);
-    fflush(stderr);
-    va_end(p);
-    exit(1);
-}
-
-/*
- * GCC nonsense - needed for GCC 3.4.x with -O2
- *
- * Maybe just test with #if (__GNUC__ >= 3) && (__GNUC_MINOR__ >= 4) ?
- */
-#ifndef __GNUC_PREREQ
-#define __GNUC_PREREQ(x,y)     0
-#endif
-#if __GNUC_PREREQ(3,4)
-#define __attribute__used __attribute__ ((used))
-#else
-#define __attribute__used
-#endif
-
-/*
- * arm needs PACKED - use it everywhere?
- */
-#if defined(__GNUC__) && (defined(__arm__) || defined(__alpha__))
-# define PACKED __attribute__ ((packed))
-#else
-# define PACKED
-#endif
-
-
-/*
- *  A. About seeking
- */
-
-/*
- * sseek: seek to specified sector - return 0 on failure
- *
- * For >4GB disks lseek needs a > 32bit arg, and we have to use llseek.
- * On the other hand, a 32 bit sector number is OK until 2TB.
- * The routines _llseek and sseek below are the only ones that
- * know about the loff_t type.
- *
- * Note: we use 512-byte sectors here, irrespective of the hardware ss.
- */
-#undef use_lseek
-#if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (__s390x__)
-#define use_lseek
-#endif
-
-#ifndef use_lseek
-static __attribute__used
-int _llseek (unsigned int fd, ulong hi, ulong lo,
-       loff_t *res, unsigned int wh) {
-       return syscall (__NR__llseek, fd, hi, lo, res, wh);
-}
-#endif
-
-static int
-sseek(char *dev, unsigned int fd, unsigned long s) {
-    loff_t in, out;
-    in = ((loff_t) s << 9);
-    out = 1;
-
-#ifndef use_lseek
-    if (_llseek (fd, in>>32, in & 0xffffffff, &out, SEEK_SET) != 0) {
-#else
-    if ((out = lseek(fd, in, SEEK_SET)) != in) {
-#endif
-       perror("llseek");
-       error(_("seek error on %s - cannot seek to %lu\n"), dev, s);
-       return 0;
-    }
-
-    if (in != out) {
-       error(_("seek error: wanted 0x%08x%08x, got 0x%08x%08x\n"),
-              (unsigned int)(in>>32), (unsigned int)(in & 0xffffffff),
-              (unsigned int)(out>>32), (unsigned int)(out & 0xffffffff));
-       return 0;
-    }
-    return 1;
-}
-
-/*
- *  B. About sectors
- */
-
-/*
- * We preserve all sectors read in a chain - some of these will
- * have to be modified and written back.
- */
-struct sector {
-    struct sector *next;
-    unsigned long sectornumber;
-    int to_be_written;
-    char data[512];
-} *sectorhead;
-
-static void
-free_sectors(void) {
-    struct sector *s;
-
-    while (sectorhead) {
-       s = sectorhead;
-       sectorhead = s->next;
-       free(s);
-    }
-}
-
-static struct sector *
-get_sector(char *dev, int fd, unsigned long sno) {
-    struct sector *s;
-
-    for(s = sectorhead; s; s = s->next)
-       if (s->sectornumber == sno)
-           return s;
-
-    if (!sseek(dev, fd, sno))
-       return 0;
-
-    if (!(s = (struct sector *) malloc(sizeof(struct sector))))
-       fatal(_("out of memory - giving up\n"));
-
-    if (read(fd, s->data, sizeof(s->data)) != sizeof(s->data)) {
-       if (errno)              /* 0 in case we read past end-of-disk */
-           perror("read");
-       error(_("read error on %s - cannot read sector %lu\n"), dev, sno);
-       free(s);
-       return 0;
-    }
-
-    s->next = sectorhead;
-    sectorhead = s;
-    s->sectornumber = sno;
-    s->to_be_written = 0;
-
-    return s;
-}
-
-static int
-msdos_signature (struct sector *s) {
-    unsigned char *data = s->data;
-    if (data[510] == 0x55 && data[511] == 0xaa)
-           return 1;
-    error(_("ERROR: sector %lu does not have an msdos signature\n"),
-         s->sectornumber);
-    return 0;
-}
-
-static int
-write_sectors(char *dev, int fd) {
-    struct sector *s;
-
-    for (s = sectorhead; s; s = s->next)
-       if (s->to_be_written) {
-           if (!sseek(dev, fd, s->sectornumber))
-               return 0;
-           if (write(fd, s->data, sizeof(s->data)) != sizeof(s->data)) {
-               perror("write");
-               error(_("write error on %s - cannot write sector %lu\n"),
-                      dev, s->sectornumber);
-               return 0;
-           }
-           s->to_be_written = 0;
-       }
-    return 1;
-}
-
-static void
-ulong_to_chars(unsigned long u, char *uu) {
-    int i;
-
-    for(i=0; i<4; i++) {
-       uu[i] = (u & 0xff);
-       u >>= 8;
-    }
-}
-
-static unsigned long
-chars_to_ulong(unsigned char *uu) {
-    int i;
-    unsigned long u = 0;
-
-    for(i=3; i>=0; i--)
-       u = (u << 8) | uu[i];
-    return u;
-}
-
-static int
-save_sectors(char *dev, int fdin) {
-    struct sector *s;
-    char ss[516];
-    int fdout;
-
-    fdout = open(save_sector_file, O_WRONLY | O_CREAT, 0444);
-    if (fdout < 0) {
-       perror(save_sector_file);
-       error(_("cannot open partition sector save file (%s)\n"),
-              save_sector_file);
-       return 0;
-    }
-
-    for (s = sectorhead; s; s = s->next)
-       if (s->to_be_written) {
-           ulong_to_chars(s->sectornumber, ss);
-           if (!sseek(dev, fdin, s->sectornumber))
-               return 0;
-           if (read(fdin, ss+4, 512) != 512) {
-               perror("read");
-               error(_("read error on %s - cannot read sector %lu\n"),
-                      dev, s->sectornumber);
-               return 0;
-           }
-           if (write(fdout, ss, sizeof(ss)) != sizeof(ss)) {
-               perror("write");
-               error(_("write error on %s\n"), save_sector_file);
-               return 0;
-           }
-       }
-    return 1;
-}
-
-static void reread_disk_partition(char *dev, int fd);
-
-static int
-restore_sectors(char *dev) {
-    int fdin, fdout, ct;
-    struct stat statbuf;
-    char *ss0, *ss;
-    unsigned long sno;
-
-    if (stat(restore_sector_file, &statbuf) < 0) {
-       perror(restore_sector_file);
-       error(_("cannot stat partition restore file (%s)\n"),
-              restore_sector_file);
-       return 0;
-    }
-    if (statbuf.st_size % 516) {
-       error(_("partition restore file has wrong size - not restoring\n"));
-       return 0;
-    }
-    if (!(ss = (char *) malloc(statbuf.st_size))) {
-       error(_("out of memory?\n"));
-       return 0;
-    }
-    fdin = open(restore_sector_file, O_RDONLY);
-    if (fdin < 0) {
-       perror(restore_sector_file);
-       error(_("cannot open partition restore file (%s)\n"),
-              restore_sector_file);
-       return 0;
-    }
-    if (read(fdin, ss, statbuf.st_size) != statbuf.st_size) {
-       perror("read");
-       error(_("error reading %s\n"), restore_sector_file);
-       return 0;
-    }
-
-    fdout = open(dev, O_WRONLY);
-    if (fdout < 0) {
-       perror(dev);
-       error(_("cannot open device %s for writing\n"), dev);
-       return 0;
-    }
-
-    ss0 = ss;
-    ct = statbuf.st_size/516;
-    while(ct--) {
-       sno = chars_to_ulong(ss);
-       if (!sseek(dev, fdout, sno))
-         return 0;
-       if (write(fdout, ss+4, 512) != 512) {
-           perror(dev);
-           error(_("error writing sector %lu on %s\n"), sno, dev);
-           return 0;
-       }
-       ss += 516;
-    }
-    free(ss0);
-
-    reread_disk_partition(dev, fdout);
-
-    return 1;
-}
-
-/*
- *  C. About heads, sectors and cylinders
- */
-
-/*
- * <linux/hdreg.h> defines HDIO_GETGEO and
- * struct hd_geometry {
- *      unsigned char heads;
- *      unsigned char sectors;
- *      unsigned short cylinders;
- *      unsigned long start;
- * };
- *
- * For large disks g.cylinders is truncated, so we use BLKGETSIZE.
- */
-
-/*
- * We consider several geometries for a disk:
- * B - the BIOS geometry, gotten from the kernel via HDIO_GETGEO
- * F - the fdisk geometry
- * U - the user-specified geometry
- *
- * 0 means unspecified / unknown
- */
-struct geometry {
-       unsigned long long total_size;          /* in sectors */
-       unsigned long cylindersize;             /* in sectors */
-       unsigned long heads, sectors, cylinders;
-       unsigned long start;
-} B, F, U;
-
-static struct geometry
-get_geometry(char *dev, int fd, int silent) {
-    struct hd_geometry g;
-    unsigned long cyls;
-    unsigned long long sectors;
-    struct geometry R;
-
-    if (ioctl(fd, HDIO_GETGEO, &g)) {
-       g.heads = g.sectors = g.cylinders = g.start = 0;
-       if (!silent)
-           do_warn(_("Disk %s: cannot get geometry\n"), dev);
-    }
-
-    R.start = g.start;
-    R.heads = g.heads;
-    R.sectors = g.sectors;
-    R.cylindersize = R.heads * R.sectors;
-    R.cylinders = 0;
-    R.total_size = 0;
-
-    if (disksize(fd, &sectors)) {
-       /* maybe an ordinary file */
-       struct stat s;
-
-       if (fstat(fd, &s) == 0 && S_ISREG(s.st_mode))
-           R.total_size = (s.st_size >> 9);
-       else if (!silent)
-           do_warn(_("Disk %s: cannot get size\n"), dev);
-    } else
-           R.total_size = sectors;
-
-    if (R.cylindersize && R.total_size) {
-           sectors /= R.cylindersize;
-           cyls = sectors;
-           if (cyls != sectors)
-                   cyls = ~0;
-           R.cylinders = cyls;
-    }
-
-    return R;
-}
-
-static void
-get_cylindersize(char *dev, int fd, int silent) {
-    struct geometry R;
-
-    R = get_geometry(dev, fd, silent);
-
-    B.heads = (U.heads ? U.heads : R.heads);
-    B.sectors = (U.sectors ? U.sectors : R.sectors);
-    B.cylinders = (U.cylinders ? U.cylinders : R.cylinders);
-
-    B.cylindersize = B.heads * B.sectors;
-    B.total_size = R.total_size;
-
-    if (B.cylinders == 0 && B.cylindersize != 0)
-           B.cylinders = B.total_size / B.cylindersize;
-
-    if (R.start && !force) {
-       warn(
-           _("Warning: start=%lu - this looks like a partition rather than\n"
-             "the entire disk. Using fdisk on it is probably meaningless.\n"
-             "[Use the --force option if you really want this]\n"), R.start);
-       exit(1);
-    }
-#if 0
-    if (R.heads && B.heads != R.heads)
-       warn(_("Warning: HDIO_GETGEO says that there are %lu heads\n"),
-            R.heads);
-    if (R.sectors && B.sectors != R.sectors)
-       warn(_("Warning: HDIO_GETGEO says that there are %lu sectors\n"),
-            R.sectors);
-    if (R.cylinders && B.cylinders != R.cylinders
-           && B.cylinders < 65536 && R.cylinders < 65536)
-       warn(_("Warning: BLKGETSIZE/HDIO_GETGEO says that there are %lu cylinders\n"),
-            R.cylinders);
-#endif
-
-    if (B.sectors > 63)
-      warn(_("Warning: unlikely number of sectors (%lu) - usually at most 63\n"
-          "This will give problems with all software that uses C/H/S addressing.\n"),
-          B.sectors);
-    if (!silent)
-      printf(_("\nDisk %s: %lu cylinders, %lu heads, %lu sectors/track\n"),
-            dev, B.cylinders, B.heads, B.sectors);
-}
-
-typedef struct { unsigned char h,s,c; } PACKED chs; /* has some c bits in s */
-chs zero_chs = { 0,0,0 };
-
-typedef struct { unsigned long h,s,c; } longchs;
-longchs zero_longchs;
-
-static chs
-longchs_to_chs (longchs aa, struct geometry G) {
-    chs a;
-
-    if (aa.h < 256 && aa.s < 64 && aa.c < 1024) {
-       a.h = aa.h;
-       a.s = aa.s | ((aa.c >> 2) & 0xc0);
-       a.c = (aa.c & 0xff);
-    } else if (G.heads && G.sectors) {
-       a.h = G.heads - 1;
-       a.s = G.sectors | 0xc0;
-       a.c = 0xff;
-    } else
-        a = zero_chs;
-    return a;
-}
-
-static longchs
-chs_to_longchs (chs a) {
-    longchs aa;
-
-    aa.h = a.h;
-    aa.s = (a.s & 0x3f);
-    aa.c = (a.s & 0xc0);
-    aa.c = (aa.c << 2) + a.c;
-    return aa;
-}
-
-static longchs
-ulong_to_longchs (unsigned long sno, struct geometry G) {
-    longchs aa;
-
-    if (G.heads && G.sectors && G.cylindersize) {
-       aa.s = 1 + sno % G.sectors;
-       aa.h = (sno / G.sectors) % G.heads;
-       aa.c = sno / G.cylindersize;
-       return aa;
-    } else {
-       return zero_longchs;
-    }
-}
-
-static chs
-ulong_to_chs (unsigned long sno, struct geometry G) {
-    return longchs_to_chs(ulong_to_longchs(sno, G), G);
-}
-
-#if 0
-static unsigned long
-longchs_to_ulong (longchs aa, struct geometry G) {
-    return (aa.c*G.cylindersize + aa.h*G.sectors + aa.s - 1);
-}
-
-static unsigned long
-chs_to_ulong (chs a, struct geometry G) {
-    return longchs_to_ulong(chs_to_longchs(a), G);
-}
-#endif
-
-static int
-is_equal_chs (chs a, chs b) {
-    return (a.h == b.h && a.s == b.s && a.c == b.c);
-}
-
-static int
-chs_ok (chs a, char *v, char *w) {
-    longchs aa = chs_to_longchs(a);
-    int ret = 1;
-
-    if (is_equal_chs(a, zero_chs))
-      return 1;
-    if (B.heads && aa.h >= B.heads) {
-       warn(_("%s of partition %s has impossible value for head: "
-            "%lu (should be in 0-%lu)\n"), w, v, aa.h, B.heads-1);
-       ret = 0;
-    }
-    if (B.sectors && (aa.s == 0 || aa.s > B.sectors)) {
-       warn(_("%s of partition %s has impossible value for sector: "
-            "%lu (should be in 1-%lu)\n"), w, v, aa.s, B.sectors);
-       ret = 0;
-    }
-    if (B.cylinders && aa.c >= B.cylinders) {
-       warn(_("%s of partition %s has impossible value for cylinders: "
-            "%lu (should be in 0-%lu)\n"), w, v, aa.c, B.cylinders-1);
-       ret = 0;
-    }
-    return ret;
-}
-
-/*
- *  D. About system Ids
- */
-
-#define EMPTY_PARTITION                0
-#define EXTENDED_PARTITION     5
-#define WIN98_EXTENDED         0x0f
-#define DM6_AUX1PARTITION      0x51
-#define DM6_AUX3PARTITION      0x53
-#define DM6_PARTITION          0x54
-#define EZD_PARTITION          0x55
-#define LINUX_SWAP              0x82
-#define LINUX_NATIVE           0x83
-#define LINUX_EXTENDED         0x85
-#define BSD_PARTITION          0xa5
-#define NETBSD_PARTITION       0xa9
-
-/* List of partition types now in i386_sys_types.c */
-
-static const char *
-sysname(unsigned char type) {
-    struct systypes *s;
-
-    for (s = i386_sys_types; s->name; s++)
-      if (s->type == type)
-       return _(s->name);
-    return _("Unknown");
-}
-
-static void
-list_types(void) {
-    struct systypes *s;
-
-    printf(_("Id  Name\n\n"));
-    for (s = i386_sys_types; s->name; s++)
-      printf("%2x  %s\n", s->type, _(s->name));
-}
-
-static int
-is_extended(unsigned char type) {
-       return (type == EXTENDED_PARTITION
-               || type == LINUX_EXTENDED
-               || type == WIN98_EXTENDED);
-}
-
-static int
-is_bsd(unsigned char type) {
-       return (type == BSD_PARTITION || type == NETBSD_PARTITION);
-}
-
-/*
- *  E. About partitions
- */
-
-/* MS/DOS partition */
-
-struct partition {
-    unsigned char bootable;            /* 0 or 0x80 */
-    chs begin_chs;
-    unsigned char sys_type;
-    chs end_chs;
-    unsigned int start_sect;   /* starting sector counting from 0 */
-    unsigned int nr_sects;     /* nr of sectors in partition */
-} PACKED;
-
-/* Unfortunately, partitions are not aligned, and non-Intel machines
-   are unhappy with non-aligned integers. So, we need a copy by hand. */
-static int
-copy_to_int(unsigned char *cp) {
-    unsigned int m;
-
-    m = *cp++;
-    m += (*cp++ << 8);
-    m += (*cp++ << 16);
-    m += (*cp++ << 24);
-    return m;
-}
-
-static void
-copy_from_int(int m, char *cp) {
-    *cp++ = (m & 0xff); m >>= 8;
-    *cp++ = (m & 0xff); m >>= 8;
-    *cp++ = (m & 0xff); m >>= 8;
-    *cp++ = (m & 0xff);
-}
-
-static void
-copy_to_part(char *cp, struct partition *p) {
-    p->bootable = *cp++;
-    p->begin_chs.h = *cp++;
-    p->begin_chs.s = *cp++;
-    p->begin_chs.c = *cp++;
-    p->sys_type = *cp++;
-    p->end_chs.h = *cp++;
-    p->end_chs.s = *cp++;
-    p->end_chs.c = *cp++;
-    p->start_sect = copy_to_int(cp);
-    p->nr_sects = copy_to_int(cp+4);
-}
-
-static void
-copy_from_part(struct partition *p, char *cp) {
-    *cp++ = p->bootable;
-    *cp++ = p->begin_chs.h;
-    *cp++ = p->begin_chs.s;
-    *cp++ = p->begin_chs.c;
-    *cp++ = p->sys_type;
-    *cp++ = p->end_chs.h;
-    *cp++ = p->end_chs.s;
-    *cp++ = p->end_chs.c;
-    copy_from_int(p->start_sect, cp);
-    copy_from_int(p->nr_sects, cp+4);
-}
-
-/* Roughly speaking, Linux doesn't use any of the above fields except
-   for partition type, start sector and number of sectors. (However,
-   see also linux/drivers/scsi/fdomain.c.)
-   The only way partition type is used (in the kernel) is the comparison
-   for equality with EXTENDED_PARTITION (and these Disk Manager types). */
-
-struct part_desc {
-    unsigned long start;
-    unsigned long size;
-    unsigned long sector, offset; /* disk location of this info */
-    struct partition p;
-    struct part_desc *ep;        /* extended partition containing this one */
-    int ptype;
-#define DOS_TYPE       0
-#define BSD_TYPE       1
-} zero_part_desc;
-
-static struct part_desc *
-outer_extended_partition(struct part_desc *p) {
-    while (p->ep)
-      p = p->ep;
-    return p;
-}
-
-static int
-is_parent(struct part_desc *pp, struct part_desc *p) {
-    while (p) {
-       if (pp == p)
-         return 1;
-       p = p->ep;
-    }
-    return 0;
-}
-
-struct disk_desc {
-    struct part_desc partitions[512];
-    int partno;
-} oldp, newp;
-
-/* determine where on the disk this information goes */
-static void
-add_sector_and_offset(struct disk_desc *z) {
-    int pno;
-    struct part_desc *p;
-
-    for (pno = 0; pno < z->partno; pno++) {
-       p = &(z->partitions[pno]);
-       p->offset = 0x1be + (pno%4)*sizeof(struct partition);
-       p->sector = (p->ep ? p->ep->start : 0);
-    }
-}
-
-/* tell the kernel to reread the partition tables */
-static int
-reread_ioctl(int fd) {
-    if (ioctl(fd, BLKRRPART)) {
-       perror("BLKRRPART");
-
-       /* 2.6.8 returns EIO for a zero table */
-       if (errno == EBUSY)
-               return -1;
-    }
-    return 0;
-}
-
-static int
-is_blockdev(int fd) {
-    struct stat statbuf;
-
-    return(fstat(fd, &statbuf) == 0 && S_ISBLK(statbuf.st_mode));
-}
-
-/* reread after writing */
-static void
-reread_disk_partition(char *dev, int fd) {
-    printf(_("Re-reading the partition table ...\n"));
-    fflush(stdout);
-    sync();
-
-    if (reread_ioctl(fd) && is_blockdev(fd))
-      do_warn(_("The command to re-read the partition table failed\n"
-            "Reboot your system now, before using mkfs\n"));
-
-    if (close(fd)) {
-       perror(dev);
-       do_warn(_("Error closing %s\n"), dev);
-    }
-    printf("\n");
-}
-
-/* find Linux name of this partition, assuming that it will have a name */
-static int
-index_to_linux(int pno, struct disk_desc *z) {
-    int i, ct = 1;
-    struct part_desc *p = &(z->partitions[0]);
-    for (i=0; i<pno; i++,p++)
-       if (i < 4 || (p->size > 0 && !is_extended(p->p.sys_type)))
-         ct++;
-    return ct;
-}
-
-static int
-linux_to_index(int lpno, struct disk_desc *z) {
-    int i, ct = 0;
-    struct part_desc *p = &(z->partitions[0]);
-    for (i=0; i<z->partno && ct < lpno; i++,p++)
-      if ((i < 4 || (p->size > 0 && !is_extended(p->p.sys_type)))
-        && ++ct == lpno)
-       return i;
-    return -1;
-}
-
-static int
-asc_to_index(char *pnam, struct disk_desc *z) {
-    int pnum, pno;
-
-    if (*pnam == '#') {
-       pno = atoi(pnam+1);
-    } else {
-       pnum = atoi(pnam);
-       pno = linux_to_index(pnum, z);
-    }
-    if (!(pno >= 0 && pno < z->partno))
-      fatal(_("%s: no such partition\n"), pnam);
-    return pno;
-}
-
-/*
- * List partitions - in terms of sectors, blocks or cylinders
- */
-#define F_SECTOR   1
-#define F_BLOCK    2
-#define F_CYLINDER 3
-#define F_MEGABYTE 4
-
-int default_format = F_MEGABYTE;
-int specified_format = 0;
-int show_extended = 0;
-int one_only = 0;
-int one_only_pno;
-int increment = 0;
-
-static void
-set_format(char c) {
-    switch(c) {
-      default:
-       do_warn(_("unrecognized format - using sectors\n"));
-      case 'S': specified_format = F_SECTOR; break;
-      case 'B': specified_format = F_BLOCK; break;
-      case 'C': specified_format = F_CYLINDER; break;
-      case 'M': specified_format = F_MEGABYTE; break;
-    }
-}
-
-static unsigned long
-unitsize(int format) {
-    default_format = (B.cylindersize ? F_CYLINDER : F_MEGABYTE);
-    if (!format && !(format = specified_format))
-      format = default_format;
-
-    switch(format) {
-      default:
-      case F_CYLINDER:
-       if (B.cylindersize)
-         return B.cylindersize;
-      case F_SECTOR:
-       return 1;
-      case F_BLOCK:
-       return 2;
-      case F_MEGABYTE:
-       return 2048;
-    }
-}
-
-static unsigned long
-get_disksize(int format) {
-    unsigned long cs = B.cylinders;
-    if (cs && leave_last)
-      cs--;
-    return (cs * B.cylindersize) / unitsize(format);
-}
-
-static void
-out_partition_header(char *dev, int format, struct geometry G) {
-    if (dump) {
-       printf(_("# partition table of %s\n"), dev);
-       printf("unit: sectors\n\n");
-       return;
-    }
-
-    default_format = (G.cylindersize ? F_CYLINDER : F_MEGABYTE);
-    if (!format && !(format = specified_format))
-      format = default_format;
-
-    switch(format) {
-      default:
-       do_warn(_("unimplemented format - using %s\n"),
-              G.cylindersize ? _("cylinders") : _("sectors"));
-      case F_CYLINDER:
-       if (G.cylindersize) {
-         printf(_("Units = cylinders of %lu bytes, blocks of 1024 bytes"
-                ", counting from %d\n\n"),
-                G.cylindersize<<9, increment);
-           printf(_("   Device Boot Start     End   #cyls    #blocks   Id  System\n"));
-           break;
-       }
-       /* fall through */
-      case F_SECTOR:
-       printf(_("Units = sectors of 512 bytes, counting from %d\n\n"),
-              increment);
-       printf(_("   Device Boot    Start       End   #sectors  Id  System\n"));
-       break;
-      case F_BLOCK:
-       printf(_("Units = blocks of 1024 bytes, counting from %d\n\n"),
-              increment);
-       printf(_("   Device Boot   Start       End    #blocks   Id  System\n"));
-       break;
-      case F_MEGABYTE:
-       printf(_("Units = mebibytes of 1048576 bytes, blocks of 1024 bytes"
-              ", counting from %d\n\n"), increment);
-       printf(_("   Device Boot Start   End    MiB    #blocks   Id  System\n"));
-       break;
-    }
-}
-
-static void
-out_rounddown(int width, unsigned long n, unsigned long unit, int inc) {
-    printf("%*lu", width, inc + n/unit);
-    if (unit != 1)
-      putchar((n % unit) ? '+' : ' ');
-    putchar(' ');
-}
-
-static void
-out_roundup(int width, unsigned long n, unsigned long unit, int inc) {
-    if (n == (unsigned long)(-1))
-      printf("%*s", width, "-");
-    else
-      printf("%*lu", width, inc + n/unit);
-    if (unit != 1)
-      putchar(((n+1) % unit) ? '-' : ' ');
-    putchar(' ');
-}
-
-static void
-out_roundup_size(int width, unsigned long n, unsigned long unit) {
-    printf("%*lu", width, (n+unit-1)/unit);
-    if (unit != 1)
-      putchar((n % unit) ? '-' : ' ');
-    putchar(' ');
-}
-
-static struct geometry
-get_fdisk_geometry_one(struct part_desc *p) {
-    struct geometry G;
-
-    chs b = p->p.end_chs;
-    longchs bb = chs_to_longchs(b);
-    G.heads = bb.h+1;
-    G.sectors = bb.s;
-    G.cylindersize = G.heads*G.sectors;
-    G.cylinders = G.start = 0;
-    return G;
-}
-
-static int
-get_fdisk_geometry(struct disk_desc *z) {
-    struct part_desc *p;
-    int pno, agree;
-    struct geometry G0, G;
-
-    agree = 0;
-    G0.heads = G0.sectors = 0;
-    for (pno=0; pno < z->partno; pno++) {
-       p = &(z->partitions[pno]);
-       if (p->size != 0 && p->p.sys_type != 0) {
-           G = get_fdisk_geometry_one(p);
-           if (!G0.heads) {
-               G0 = G;
-               agree = 1;
-           } else if (G.heads != G0.heads || G.sectors != G0.sectors) {
-               agree = 0;
-               break;
-           }
-       }
-    }
-    F = (agree ? G0 : B);
-    return (F.sectors != B.sectors || F.heads != B.heads);
-}
-
-static void
-out_partition(char *dev, int format, struct part_desc *p,
-             struct disk_desc *z, struct geometry G) {
-    unsigned long start, end, size;
-    int pno, lpno;
-
-    if (!format && !(format = specified_format))
-      format = default_format;
-
-    pno = p - &(z->partitions[0]);     /* our index */
-    lpno = index_to_linux(pno, z);     /* name of next one that has a name */
-    if (pno == linux_to_index(lpno, z))  /* was that us? */
-      printf("%s", partname(dev, lpno, 10));  /* yes */
-    else if (show_extended)
-      printf("    -     ");
-    else
-      return;
-    putchar(dump ? ':' : ' ');
-
-    start = p->start;
-    end = p->start + p->size - 1;
-    size = p->size;
-
-    if (dump) {
-       printf(" start=%9lu", start);
-       printf(", size=%9lu", size);
-       if (p->ptype == DOS_TYPE) {
-           printf(", Id=%2x", p->p.sys_type);
-           if (p->p.bootable == 0x80)
-               printf(", bootable");
-       }
-       printf("\n");
-       return;
-    }
-
-    if (p->ptype != DOS_TYPE || p->p.bootable == 0)
-      printf("   ");
-    else if (p->p.bootable == 0x80)
-      printf(" * ");
-    else
-      printf(" ? ");           /* garbage */
-
-    switch(format) {
-      case F_CYLINDER:
-       if (G.cylindersize) {
-           out_rounddown(6, start, G.cylindersize, increment);
-           out_roundup(6, end, G.cylindersize, increment);
-           out_roundup_size(6, size, G.cylindersize);
-           out_rounddown(9, size, 2, 0);
-           break;
-       }
-       /* fall through */
-      default:
-      case F_SECTOR:
-       out_rounddown(9, start, 1, increment);
-       out_roundup(9, end, 1, increment);
-       out_rounddown(10, size, 1, 0);
-       break;
-      case F_BLOCK:
-#if 0
-       printf("%8lu,%3lu ",
-              p->sector/2, ((p->sector & 1) ? 512 : 0) + p->offset);
-#endif
-       out_rounddown(8, start, 2, increment);
-       out_roundup(8, end, 2, increment);
-       out_rounddown(9, size, 2, 0);
-       break;
-      case F_MEGABYTE:
-       out_rounddown(5, start, 2048, increment);
-       out_roundup(5, end, 2048, increment);
-       out_roundup_size(5, size, 2048);
-       out_rounddown(9, size, 2, 0);
-       break;
-    }
-    if (p->ptype == DOS_TYPE) {
-       printf(" %2x  %s\n",
-          p->p.sys_type, sysname(p->p.sys_type));
-    } else {
-       printf("\n");
-    }
-
-    /* Is chs as we expect? */
-    if (!quiet && p->ptype == DOS_TYPE) {
-       chs a, b;
-       longchs aa, bb;
-       a = (size ? ulong_to_chs(start,G) : zero_chs);
-       b = p->p.begin_chs;
-       aa = chs_to_longchs(a);
-       bb = chs_to_longchs(b);
-       if (a.s && !is_equal_chs(a, b))
-         do_warn(_("\t\tstart: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)\n"),
-                aa.c, aa.h, aa.s, bb.c, bb.h, bb.s);
-       a = (size ? ulong_to_chs(end,G) : zero_chs);
-       b = p->p.end_chs;
-       aa = chs_to_longchs(a);
-       bb = chs_to_longchs(b);
-       if (a.s && !is_equal_chs(a, b))
-         do_warn(_("\t\tend: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)\n"),
-                aa.c, aa.h, aa.s, bb.c, bb.h, bb.s);
-       if (G.cylinders && G.cylinders < 1024 && bb.c > G.cylinders)
-         do_warn(_("partition ends on cylinder %ld, beyond the end of the disk\n"),
-              bb.c);
-    }
-}
-
-static void
-out_partitions(char *dev, struct disk_desc *z) {
-    int pno, format = 0;
-
-    if (z->partno == 0)
-       do_warn(_("No partitions found\n"));
-    else {
-       if (get_fdisk_geometry(z) && !dump) {
-           do_warn(
-          _("Warning: The partition table looks like it was made\n"
-            "  for C/H/S=*/%ld/%ld (instead of %ld/%ld/%ld).\n"
-            "For this listing I'll assume that geometry.\n"),
-          F.heads, F.sectors, B.cylinders, B.heads, B.sectors);
-       }
-
-       out_partition_header(dev, format, F);
-       for(pno=0; pno < z->partno; pno++) {
-           out_partition(dev, format, &(z->partitions[pno]), z, F);
-           if (show_extended && pno%4==3)
-             printf("\n");
-       }
-    }
-}
-
-static int
-disj(struct part_desc *p, struct part_desc *q) {
-    return
-      ((p->start + p->size <= q->start)
-       || (is_extended(p->p.sys_type)
-           && q->start + q->size <= p->start + p->size));
-}
-
-static char *
-pnumber(struct part_desc *p, struct disk_desc *z) {
-    static char buf[20];
-    int this, next;
-    struct part_desc *p0 = &(z->partitions[0]);
-
-    this = index_to_linux(p-p0, z);
-    next = index_to_linux(p-p0+1, z);
-
-    if (next > this)
-      sprintf(buf, "%d", this);
-    else
-      sprintf(buf, "[%d]", this);
-    return buf;
-}
-
-static int
-partitions_ok(struct disk_desc *z) {
-    struct part_desc *partitions = &(z->partitions[0]), *p, *q;
-    int partno = z->partno;
-
-#define PNO(p) pnumber(p, z)
-
-    /* Have at least 4 partitions been defined? */
-    if (partno < 4) {
-        if (!partno)
-             fatal(_("no partition table present.\n"));
-        else
-             fatal(_("strange, only %d partitions defined.\n"), partno);
-        return 0;
-    }
-
-    /* Are the partitions of size 0 marked empty?
-       And do they have start = 0? And bootable = 0? */
-    for (p = partitions; p - partitions < partno; p++)
-      if (p->size == 0) {
-         if (p->p.sys_type != EMPTY_PARTITION)
-           warn(_("Warning: partition %s has size 0 but is not marked Empty\n"),
-                PNO(p));
-         else if (p->p.bootable != 0)
-           warn(_("Warning: partition %s has size 0 and is bootable\n"),
-                PNO(p));
-         else if (p->p.start_sect != 0)
-           warn(_("Warning: partition %s has size 0 and nonzero start\n"),
-                PNO(p));
-         /* all this is probably harmless, no error return */
-      }
-
-    /* Are the logical partitions contained in their extended partitions? */
-    for (p = partitions+4; p < partitions+partno; p++)
-      if (p->ptype == DOS_TYPE)
-      if (p->size && !is_extended(p->p.sys_type)) {
-         q = p->ep;
-         if (p->start < q->start || p->start + p->size > q->start + q->size) {
-             warn(_("Warning: partition %s "), PNO(p));
-             warn(_("is not contained in partition %s\n"), PNO(q));
-             return 0;
-         }
-      }
-
-    /* Are the data partitions mutually disjoint? */
-    for (p = partitions; p < partitions+partno; p++)
-      if (p->size && !is_extended(p->p.sys_type))
-       for (q = p+1; q < partitions+partno; q++)
-         if (q->size && !is_extended(q->p.sys_type))
-           if (!((p->start > q-> start) ? disj(q,p) : disj(p,q))) {
-               warn(_("Warning: partitions %s "), PNO(p));
-               warn(_("and %s overlap\n"), PNO(q));
-               return 0;
-           }
-
-    /* Are the data partitions and the extended partition
-       table sectors disjoint? */
-    for (p = partitions; p < partitions+partno; p++)
-      if (p->size && !is_extended(p->p.sys_type))
-       for (q = partitions; q < partitions+partno; q++)
-         if (is_extended(q->p.sys_type))
-           if (p->start <= q->start && p->start + p->size > q->start) {
-               warn(_("Warning: partition %s contains part of "
-                      "the partition table (sector %lu),\n"
-                      "and will destroy it when filled\n"),
-                    PNO(p), q->start);
-               return 0;
-           }
-
-    /* Do they start past zero and end before end-of-disk? */
-    { unsigned long ds = get_disksize(F_SECTOR);
-    for (p = partitions; p < partitions+partno; p++)
-      if (p->size) {
-         if (p->start == 0) {
-             warn(_("Warning: partition %s starts at sector 0\n"), PNO(p));
-             return 0;
-         }
-         if (p->size && p->start + p->size > ds) {
-             warn(_("Warning: partition %s extends past end of disk\n"),
-                  PNO(p));
-             return 0;
-         }
-      }
-    }
-
-    /* At most one chain of DOS extended partitions ? */
-    /* It seems that the OS/2 fdisk has the additional requirement
-       that the extended partition must be the fourth one */
-    { int ect = 0;
-      for (p = partitions; p < partitions+4; p++)
-       if (p->p.sys_type == EXTENDED_PARTITION)
-         ect++;
-      if (ect > 1 && !Linux) {
-         warn(_("Among the primary partitions, at most one can be extended\n"
-                " (although this is not a problem under Linux)\n"));
-         return 0;
-      }
-    }
-
-    /*
-     * Do all partitions start at a cylinder boundary ?
-     * (this is not required for Linux)
-     * The first partition starts after MBR.
-     * Logical partitions start slightly after the containing extended partn.
-     */
-    if (B.cylindersize) {
-       for(p = partitions; p < partitions+partno; p++)
-         if (p->size) {
-             if (p->start % B.cylindersize != 0
-                && (!p->ep || p->start / B.cylindersize != p->ep->start / B.cylindersize)
-                && (p->p.start_sect >= B.cylindersize)) {
-                 warn(_("Warning: partition %s does not start "
-                      "at a cylinder boundary\n"), PNO(p));
-                 if (!Linux)
-                   return 0;
-             }
-             if ((p->start + p->size) % B.cylindersize) {
-                 warn(_("Warning: partition %s does not end "
-                      "at a cylinder boundary\n"), PNO(p));
-                 if (!Linux)
-                   return 0;
-             }
-         }
-    }
-
-    /* Usually, one can boot only from primary partitions. */
-    /* In fact, from a unique one only. */
-    /* do not warn about bootable extended partitions -
-       often LILO is there */
-    { int pno = -1;
-    for(p = partitions; p < partitions+partno; p++)
-      if (p->p.bootable) {
-         if (pno == -1)
-           pno = p - partitions;
-         else if (p - partitions < 4) {
-             warn(_("Warning: more than one primary partition is marked "
-                  "bootable (active)\n"
-                  "This does not matter for LILO, but the DOS MBR will "
-                  "not boot this disk.\n"));
-             break;
-         }
-         if (p - partitions >= 4) {
-             warn(_("Warning: usually one can boot from primary partitions "
-                  "only\nLILO disregards the `bootable' flag.\n"));
-             break;
-         }
-      }
-      if (pno == -1 || pno >= 4)
-       warn(_("Warning: no primary partition is marked bootable (active)\n"
-            "This does not matter for LILO, but the DOS MBR will "
-            "not boot this disk.\n"));
-    }
-
-    /* Is chs as we expect? */
-    for(p = partitions; p < partitions+partno; p++)
-      if (p->ptype == DOS_TYPE) {
-       chs a, b;
-       longchs aa, bb;
-       a = p->size ? ulong_to_chs(p->start,B) : zero_chs;
-       b = p->p.begin_chs;
-       aa = chs_to_longchs(a);
-       bb = chs_to_longchs(b);
-       if (!chs_ok(b, PNO(p), _("start")))
-         return 0;
-       if (a.s && !is_equal_chs(a, b))
-         warn(_("partition %s: start: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)\n"),
-              PNO(p), aa.c, aa.h, aa.s, bb.c, bb.h, bb.s);
-       a = p->size ? ulong_to_chs(p->start + p->size - 1, B) : zero_chs;
-       b = p->p.end_chs;
-       aa = chs_to_longchs(a);
-       bb = chs_to_longchs(b);
-       if (!chs_ok(b, PNO(p), _("end")))
-         return 0;
-       if (a.s && !is_equal_chs(a, b))
-         warn(_("partition %s: end: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)\n"),
-              PNO(p), aa.c, aa.h, aa.s, bb.c, bb.h, bb.s);
-       if (B.cylinders && B.cylinders < 1024 && bb.c > B.cylinders)
-         warn(_("partition %s ends on cylinder %ld, beyond the end of the disk\n"),
-              PNO(p), bb.c);
-    }
-
-    return 1;
-
-#undef PNO
-}
-
-static void
-extended_partition(char *dev, int fd, struct part_desc *ep, struct disk_desc *z) {
-    char *cp;
-    struct sector *s;
-    unsigned long start, here, next;
-    int i, moretodo = 1;
-    struct partition p;
-    struct part_desc *partitions = &(z->partitions[0]);
-    int pno = z->partno;
-
-    here = start = ep->start;
-
-    if (B.cylindersize && start % B.cylindersize) {
-       /* This is BAD */
-       if (DOS_extended) {
-           here = start -= (start % B.cylindersize);
-           do_warn(_("Warning: shifted start of the extd partition "
-                    "from %ld to %ld\n"
-                    "(For listing purposes only. "
-                    "Do not change its contents.)\n"),
-                  ep->start, start);
-       } else {
-           do_warn(_("Warning: extended partition does not start at a "
-                    "cylinder boundary.\n"
-                    "DOS and Linux will interpret the contents differently.\n"));
-       }
-    }
-
-    while (moretodo) {
-       moretodo = 0;
-
-       if (!(s = get_sector(dev, fd, here)))
-           break;
-
-       if (!msdos_signature(s))
-           break;
-
-       cp = s->data + 0x1be;
-
-       if (pno+4 >= SIZE(z->partitions)) {
-           do_warn(_("too many partitions - ignoring those past nr (%d)\n"),
-                  pno-1);
-           break;
-       }
-
-       next = 0;
-
-       for (i=0; i<4; i++,cp += sizeof(struct partition)) {
-           partitions[pno].sector = here;
-           partitions[pno].offset = cp - s->data;
-           partitions[pno].ep = ep;
-           copy_to_part(cp,&p);
-           if (is_extended(p.sys_type)) {
-               partitions[pno].start = start + p.start_sect;
-               if (next)
-                 do_warn(_("tree of partitions?\n"));
-               else
-                 next = partitions[pno].start;         /* follow `upper' branch */
-               moretodo = 1;
-           } else {
-               partitions[pno].start = here + p.start_sect;
-           }
-           partitions[pno].size = p.nr_sects;
-           partitions[pno].ptype = DOS_TYPE;
-           partitions[pno].p = p;
-           pno++;
-       }
-       here = next;
-    }
-
-    z->partno = pno;
-}
-
-#define BSD_DISKMAGIC   (0x82564557UL)
-#define BSD_MAXPARTITIONS       16
-#define BSD_FS_UNUSED     0
-typedef unsigned char u8;
-typedef unsigned short u16;
-typedef unsigned int u32;
-struct bsd_disklabel {
-       u32     d_magic;
-       char    d_junk1[4];
-       char    d_typename[16];
-       char    d_packname[16];
-       char    d_junk2[92];
-       u32     d_magic2;
-       char    d_junk3[2];
-       u16     d_npartitions;          /* number of partitions in following */
-       char    d_junk4[8];
-     struct  bsd_partition {         /* the partition table */
-                u32   p_size;         /* number of sectors in partition */
-                u32   p_offset;       /* starting sector */
-                u32   p_fsize;        /* filesystem basic fragment size */
-                u8    p_fstype;       /* filesystem type, see below */
-                u8    p_frag;         /* filesystem fragments per block */
-                u16   p_cpg;          /* filesystem cylinders per group */
-     } d_partitions[BSD_MAXPARTITIONS];      /* actually may be more */
-};
-
-static void
-bsd_partition(char *dev, int fd, struct part_desc *ep, struct disk_desc *z) {
-       struct bsd_disklabel *l;
-       struct bsd_partition *bp, *bp0;
-       unsigned long start = ep->start;
-       struct sector *s;
-       struct part_desc *partitions = &(z->partitions[0]);
-       int pno = z->partno;
-
-       if (!(s = get_sector(dev,fd,start+1)))
-               return;
-       l = (struct bsd_disklabel *) (s->data);
-       if (l->d_magic != BSD_DISKMAGIC || l->d_magic2 != BSD_DISKMAGIC)
-               return;
-
-       bp = bp0 = &l->d_partitions[0];
-       while (bp - bp0 < BSD_MAXPARTITIONS && bp - bp0 < l->d_npartitions) {
-               if (pno+1 >= SIZE(z->partitions)) {
-                       do_warn(_("too many partitions - ignoring those "
-                              "past nr (%d)\n"), pno-1);
-                       break;
-               }
-               if (bp->p_fstype != BSD_FS_UNUSED) {
-                       partitions[pno].start = bp->p_offset;
-                       partitions[pno].size = bp->p_size;
-                       partitions[pno].sector = start+1;
-                       partitions[pno].offset = (char *)bp - (char *)bp0;
-                       partitions[pno].ep = 0;
-                       partitions[pno].ptype = BSD_TYPE;
-                       pno++;
-               }
-               bp++;
-       }
-       z->partno = pno;
-}
-
-#define MAKE_VERSION(p,q,r)     (65536*(p) + 256*(q) + (r))
-
-static int
-linux_version_code(void) {
-        struct utsname my_utsname;
-        int p, q, r;
-
-        if (uname(&my_utsname) == 0) {
-                p = atoi(strtok(my_utsname.release, "."));
-                q = atoi(strtok(NULL, "."));
-                r = atoi(strtok(NULL, "."));
-                return MAKE_VERSION(p,q,r);
-        }
-        return 0;
-}
-
-static int
-msdos_partition(char *dev, int fd, unsigned long start, struct disk_desc *z) {
-    int i;
-    char *cp;
-    struct partition pt;
-    struct sector *s;
-    struct part_desc *partitions = &(z->partitions[0]);
-    int pno = z->partno;
-    int bsd_later = (linux_version_code() >= MAKE_VERSION(2,3,40));
-
-    if (!(s = get_sector(dev, fd, start)))
-       return 0;
-
-    if (!msdos_signature(s))
-       return 0;
-
-    cp = s->data + 0x1be;
-    copy_to_part(cp,&pt);
-
-    /* If I am not mistaken, recent kernels will hide this from us,
-          so we will never actually see traces of a Disk Manager */
-    if (pt.sys_type == DM6_PARTITION
-       || pt.sys_type == EZD_PARTITION
-       || pt.sys_type == DM6_AUX1PARTITION
-       || pt.sys_type == DM6_AUX3PARTITION) {
-       do_warn(_("detected Disk Manager - unable to handle that\n"));
-       return 0;
-    }
-    { unsigned int sig = *(unsigned short *)(s->data + 2);
-      if (sig <= 0x1ae
-         && *(unsigned short *)(s->data + sig) == 0x55aa
-         && (1 & *(unsigned char *)(s->data + sig + 2))) {
-         do_warn(_("DM6 signature found - giving up\n"));
-         return 0;
-      }
-    }
-
-    for (pno=0; pno<4; pno++,cp += sizeof(struct partition)) {
-       partitions[pno].sector = start;
-       partitions[pno].offset = cp - s->data;
-       copy_to_part(cp,&pt);
-       partitions[pno].start = start + pt.start_sect;
-       partitions[pno].size = pt.nr_sects;
-       partitions[pno].ep = 0;
-       partitions[pno].p = pt;
-    }
-
-    z->partno = pno;
-
-    for (i=0; i<4; i++) {
-       if (is_extended(partitions[i].p.sys_type)) {
-           if (!partitions[i].size) {
-               do_warn(_("strange..., an extended partition of size 0?\n"));
-               continue;
-           }
-           extended_partition(dev, fd, &partitions[i], z);
-       }
-       if (!bsd_later && is_bsd(partitions[i].p.sys_type)) {
-           if (!partitions[i].size) {
-               do_warn(_("strange..., a BSD partition of size 0?\n"));
-               continue;
-           }
-           bsd_partition(dev, fd, &partitions[i], z);
-       }
-    }
-
-    if (bsd_later) {
-       for (i=0; i<4; i++) {
-           if (is_bsd(partitions[i].p.sys_type)) {
-               if (!partitions[i].size) {
-                   do_warn(_("strange..., a BSD partition of size 0?\n"));
-                   continue;
-               }
-               bsd_partition(dev, fd, &partitions[i], z);
-           }
-       }
-    }
-           
-    return 1;
-}
-
-static int
-osf_partition(char *dev, int fd, unsigned long start, struct disk_desc *z) {
-       return 0;
-}
-
-static int
-sun_partition(char *dev, int fd, unsigned long start, struct disk_desc *z) {
-       return 0;
-}
-
-static int
-amiga_partition(char *dev, int fd, unsigned long start, struct disk_desc *z) {
-       return 0;
-}
-
-static void
-get_partitions(char *dev, int fd, struct disk_desc *z) {
-    z->partno = 0;
-
-    if (!msdos_partition(dev, fd, 0, z)
-       && !osf_partition(dev, fd, 0, z)
-       && !sun_partition(dev, fd, 0, z)
-       && !amiga_partition(dev, fd, 0, z)) {
-       do_warn(_(" %s: unrecognized partition table type\n"), dev);
-       return;
-    }
-}
-
-static int
-write_partitions(char *dev, int fd, struct disk_desc *z) {
-    struct sector *s;
-    struct part_desc *partitions = &(z->partitions[0]), *p;
-    int pno = z->partno;
-
-    if (no_write) {
-       do_warn(_("-n flag was given: Nothing changed\n"));
-       exit(0);
-    }
-
-    for (p = partitions; p < partitions+pno; p++) {
-       s = get_sector(dev, fd, p->sector);
-       if (!s) return 0;
-       s->to_be_written = 1;
-       if (p->ptype == DOS_TYPE) {
-           copy_from_part(&(p->p), s->data + p->offset);
-           s->data[510] = 0x55;
-           s->data[511] = 0xaa;
-       }
-    }
-    if (save_sector_file) {
-       if (!save_sectors(dev, fd)) {
-           fatal(_("Failed saving the old sectors - aborting\n"));
-           return 0;
-       }
-    }
-    if (!write_sectors(dev, fd)) {
-       error(_("Failed writing the partition on %s\n"), dev);
-       return 0;
-    }
-    return 1;
-}
-
-/*
- *  F. The standard input
- */
-
-/*
- * Input format:
- * <start> <size> <type> <bootable> <c,h,s> <c,h,s>
- * Fields are separated by whitespace or comma or semicolon possibly
- * followed by whitespace; initial and trailing whitespace is ignored.
- * Numbers can be octal, decimal or hexadecimal, decimal is default
- * The <c,h,s> parts can (and probably should) be omitted.
- * Bootable is specified as [*|-], with as default not-bootable.
- * Type is given in hex, without the 0x prefix, or is [E|S|L|X], where
- * L (LINUX_NATIVE (83)) is the default, S is LINUX_SWAP (82), and E
- * is EXTENDED_PARTITION (5), X is LINUX_EXTENDED (85).
- * The default value of start is the first nonassigned sector/cylinder/...
- * The default value of size is as much as possible (until next
- * partition or end-of-disk).
- * .: end of chain of extended partitions.
- *
- * On interactive input an empty line means: all defaults.
- * Otherwise empty lines are ignored.
- */
-
-int eof, eob;
-
-struct dumpfld {
-    int fldno;
-    char *fldname;
-    int is_bool;
-} dumpflds[] = {
-    { 0, "start", 0 },
-    { 1, "size", 0 },
-    { 2, "Id", 0 },
-    { 3, "bootable", 1 },
-    { 4, "bh", 0 },
-    { 5, "bs", 0 },
-    { 6, "bc", 0 },
-    { 7, "eh", 0 },
-    { 8, "es", 0 },
-    { 9, "ec", 0 }
-};
-
-/*
- * Read a line, split it into fields
- *
- * (some primitive handwork, but a more elaborate parser seems
- *  unnecessary)
- */
-#define RD_EOF (-1)
-#define RD_CMD (-2)
-
-static int
-read_stdin(unsigned char **fields, unsigned char *line, int fieldssize, int linesize) {
-    unsigned char *lp, *ip;
-    int c, fno;
-
-    /* boolean true and empty string at start */
-    line[0] = '*';
-    line[1] = 0;
-    for (fno=0; fno < fieldssize; fno++)
-      fields[fno] = line + 1;
-    fno = 0;
-
-    /* read a line from stdin */
-    lp = fgets(line+2, linesize, stdin);
-    if (lp == NULL) {
-       eof = 1;
-       return RD_EOF;
-    }
-    if (!(lp = index(lp, '\n')))
-      fatal(_("long or incomplete input line - quitting\n"));
-    *lp = 0;
-
-    /* remove comments, if any */
-    if ((lp = index(line+2, '#')) != 0)
-      *lp = 0;
-
-    /* recognize a few commands - to be expanded */
-    if (!strcmp(line+2, "unit: sectors")) {
-       specified_format = F_SECTOR;
-       return RD_CMD;
-    }
-
-    /* dump style? - then bad input is fatal */
-    if ((ip = index(line+2, ':')) != 0) {
-       struct dumpfld *d;
-
-      nxtfld:
-           ip++;
-           while(isspace(*ip))
-             ip++;
-           if (*ip == 0)
-             return fno;
-           for(d = dumpflds; d-dumpflds < SIZE(dumpflds); d++) {
-               if (!strncmp(ip, d->fldname, strlen(d->fldname))) {
-                   ip += strlen(d->fldname);
-                   while(isspace(*ip))
-                     ip++;
-                   if (d->is_bool)
-                       fields[d->fldno] = line;
-                   else if (*ip == '=') {
-                       while(isspace(*++ip)) ;
-                       fields[d->fldno] = ip;
-                       while(isalnum(*ip))     /* 0x07FF */
-                         ip++;
-                   } else
-                     fatal(_("input error: `=' expected after %s field\n"),
-                           d->fldname);
-                   if (fno <= d->fldno)
-                     fno = d->fldno + 1;
-                   if (*ip == 0)
-                     return fno;
-                   if (*ip != ',' && *ip != ';')
-                     fatal(_("input error: unexpected character %c after %s field\n"),
-                           *ip, d->fldname);
-                   *ip = 0;
-                   goto nxtfld;
-               }
-           }
-           fatal(_("unrecognized input: %s\n"), ip);
-    }
-
-    /* split line into fields */
-    lp = ip = line+2;
-    fields[fno++] = lp;
-    while((c = *ip++) != 0) {
-       if (!lp[-1] && (c == '\t' || c == ' '))
-         ;
-       else if (c == '\t' || c == ' ' || c == ',' || c == ';') {
-           *lp++ = 0;
-           if (fno < fieldssize)
-               fields[fno++] = lp;
-           continue;
-       } else
-         *lp++ = c;
-    }
-
-    if (lp == fields[fno-1])
-      fno--;
-    return fno;
-}
-
-/* read a number, use default if absent */
-/* a sign gives an offset from the default */
-static int
-get_ul(char *u, unsigned long *up, unsigned long def, int base) {
-    char *nu;
-    int sign = 0;
-    unsigned long val;
-
-    if (*u == '+') {
-       sign = 1;
-       u++;
-    } else if (*u == '-') {
-       sign = -1;
-       u++;
-    }
-    if (*u) {
-       errno = 0;
-       val = strtoul(u, &nu, base);
-       if (errno == ERANGE) {
-           do_warn(_("number too big\n"));
-           return -1;
-       }
-       if (*nu) {
-           do_warn(_("trailing junk after number\n"));
-           return -1;
-       }
-       if (sign == 1)
-               val = def + val;
-       else if (sign == -1)
-               val = def - val;
-       *up = val;
-    } else
-      *up = def;
-    return 0;
-}
-
-/* There are two common ways to structure extended partitions:
-   as nested boxes, and as a chain. Sometimes the partitions
-   must be given in order. Sometimes all logical partitions
-   must lie inside the outermost extended partition.
-NESTED: every partition is contained in the surrounding partitions
-   and is disjoint from all others.
-CHAINED: every data partition is contained in the surrounding partitions
-   and disjoint from all others, but extended partitions may lie outside
-   (insofar as allowed by all_logicals_inside_outermost_extended).
-ONESECTOR: all data partitions are mutually disjoint; extended partitions
-   each use one sector only (except perhaps for the outermost one).
-*/
-int partitions_in_order = 0;
-int all_logicals_inside_outermost_extended = 1;
-enum { NESTED, CHAINED, ONESECTOR } boxes = NESTED;
-
-/* find the default value for <start> - assuming entire units */
-static unsigned long
-first_free(int pno, int is_extended, struct part_desc *ep, int format,
-          unsigned long mid, struct disk_desc *z) {
-    unsigned long ff, fff;
-    unsigned long unit = unitsize(format);
-    struct part_desc *partitions = &(z->partitions[0]), *pp = 0;
-
-    /* if containing ep undefined, look at its container */
-    if (ep && ep->p.sys_type == EMPTY_PARTITION)
-      ep = ep->ep;
-
-    if (ep) {
-       if (boxes == NESTED || (boxes == CHAINED && !is_extended))
-         pp = ep;
-       else if (all_logicals_inside_outermost_extended)
-         pp = outer_extended_partition(ep);
-    }
-#if 0
-    ff = pp ? (pp->start + unit - 1) / unit : 0;
-#else
-    /* rounding up wastes almost an entire cylinder - round down
-       and leave it to compute_start_sect() to fix the difference */
-    ff = pp ? pp->start / unit : 0;
-#endif
-    /* MBR and 1st sector of an extended partition are never free */
-    if (unit == 1)
-      ff++;
-
-  again:
-    for(pp = partitions; pp < partitions+pno; pp++) {
-       if (!is_parent(pp, ep) && pp->size > 0) {
-           if ((partitions_in_order || pp->start / unit <= ff
-                                    || (mid && pp->start / unit <= mid))
-               && (fff = (pp->start + pp->size + unit - 1) / unit) > ff) {
-               ff = fff;
-               goto again;
-           }
-       }
-    }
-
-    return ff;
-}
-
-/* find the default value for <size> - assuming entire units */
-static unsigned long
-max_length(int pno, int is_extended, struct part_desc *ep, int format,
-          unsigned long start, struct disk_desc *z) {
-    unsigned long fu;
-    unsigned long unit = unitsize(format);
-    struct part_desc *partitions = &(z->partitions[0]), *pp = 0;
-
-    /* if containing ep undefined, look at its container */
-    if (ep && ep->p.sys_type == EMPTY_PARTITION)
-      ep = ep->ep;
-
-    if (ep) {
-       if (boxes == NESTED || (boxes == CHAINED && !is_extended))
-         pp = ep;
-       else if (all_logicals_inside_outermost_extended)
-         pp = outer_extended_partition(ep);
-    }
-    fu = pp ? (pp->start + pp->size) / unit : get_disksize(format);
-       
-    for(pp = partitions; pp < partitions+pno; pp++)
-      if (!is_parent(pp, ep) && pp->size > 0
-         && pp->start / unit >= start && pp->start / unit < fu)
-       fu = pp->start / unit;
-
-    return (fu > start) ? fu - start : 0;
-}
-
-/* compute starting sector of a partition inside an extended one */
-/* return 0 on failure */
-/* ep is 0 or points to surrounding extended partition */
-static int
-compute_start_sect(struct part_desc *p, struct part_desc *ep) {
-    unsigned long base;
-    int inc = (DOS && B.sectors) ? B.sectors : 1;
-    int delta;
-
-    if (ep && p->start + p->size >= ep->start + 1)
-      delta = p->start - ep->start - inc;
-    else if (p->start == 0 && p->size > 0)
-      delta = -inc;
-    else
-      delta = 0;
-
-    if (delta < 0) {
-       p->start -= delta;
-       p->size += delta;
-       if (is_extended(p->p.sys_type) && boxes == ONESECTOR)
-         p->size = inc;
-       else if ((int)(p->size) <= 0) {
-           warn(_("no room for partition descriptor\n"));
-           return 0;
-       }
-    }
-    base = (!ep ? 0
-               : (is_extended(p->p.sys_type) ?
-                  outer_extended_partition(ep) : ep)->start);
-    p->ep = ep;
-    if (p->p.sys_type == EMPTY_PARTITION && p->size == 0) {
-        p->p.start_sect = 0;
-       p->p.begin_chs = zero_chs;
-       p->p.end_chs = zero_chs;
-    } else {
-        p->p.start_sect = p->start - base;
-       p->p.begin_chs = ulong_to_chs(p->start,B);
-       p->p.end_chs = ulong_to_chs(p->start + p->size - 1,B);
-    }
-    p->p.nr_sects = p->size;
-    return 1;
-}    
-
-/* build the extended partition surrounding a given logical partition */
-static int
-build_surrounding_extended(struct part_desc *p, struct part_desc *ep,
-                          struct disk_desc *z) {
-    int inc = (DOS && B.sectors) ? B.sectors : 1;
-    int format = F_SECTOR;
-    struct part_desc *p0 = &(z->partitions[0]), *eep = ep->ep;
-
-    if (boxes == NESTED) {
-       ep->start = first_free(ep-p0, 1, eep, format, p->start, z);
-       ep->size = max_length(ep-p0, 1, eep, format, ep->start, z);
-       if (ep->start > p->start || ep->start + ep->size < p->start + p->size) {
-           warn(_("cannot build surrounding extended partition\n"));
-           return 0;
-       }
-    } else {
-       ep->start = p->start;
-       if (boxes == CHAINED)
-         ep->size = p->size;
-       else
-         ep->size = inc;
-    }
-
-    ep->p.nr_sects = ep->size;
-    ep->p.bootable = 0;
-    ep->p.sys_type = EXTENDED_PARTITION;
-    if (!compute_start_sect(ep, eep) || !compute_start_sect(p, ep)) {
-       ep->p.sys_type = EMPTY_PARTITION;
-       ep->size = 0;
-       return 0;
-    }
-
-    return 1;
-}
-
-static int
-read_line(int pno, struct part_desc *ep, char *dev, int interactive,
-         struct disk_desc *z) {
-    unsigned char line[1000];
-    unsigned char *fields[11];
-    int fno, pct = pno%4;
-    struct part_desc p, *orig;
-    unsigned long ff, ff1, ul, ml, ml1, def;
-    int format, lpno, is_extd;
-
-    if (eof || eob)
-      return -1;
-
-    lpno = index_to_linux(pno, z);
-
-    if (interactive) {
-       if (pct == 0 && (show_extended || pno == 0))
-         warn("\n");
-       warn("%s:", partname(dev, lpno, 10));
-    }
-
-    /* read input line - skip blank lines when reading from a file */
-    do {
-       fno = read_stdin(fields, line, SIZE(fields), SIZE(line));
-    } while(fno == RD_CMD || (fno == 0 && !interactive));
-    if (fno == RD_EOF) {
-       return -1;
-    } else if (fno > 10 && *(fields[10]) != 0) {
-       do_warn(_("too many input fields\n"));
-       return 0;
-    }
-
-    if (fno == 1 && !strcmp(fields[0], ".")) {
-       eob = 1;
-       return -1;
-    }
-
-    /* use specified format, but round to cylinders if F_MEGABYTE specified */
-    format = 0;
-    if (B.cylindersize && specified_format == F_MEGABYTE)
-      format = F_CYLINDER;
-
-    orig = (one_only ? &(oldp.partitions[pno]) : 0);
-
-    p = zero_part_desc;
-    p.ep = ep;
-
-    /* first read the type - we need to know whether it is extended */
-    /* stop reading when input blank (defaults) and all is full */
-    is_extd = 0;
-    if (fno == 0) {            /* empty line */
-       if (orig && is_extended(orig->p.sys_type))
-         is_extd = 1;
-       ff = first_free(pno, is_extd, ep, format, 0, z);
-       ml = max_length(pno, is_extd, ep, format, ff, z);
-       if (ml == 0 && is_extd == 0) {
-           is_extd = 1;
-           ff = first_free(pno, is_extd, ep, format, 0, z);
-           ml = max_length(pno, is_extd, ep, format, ff, z);
-       }
-       if (ml == 0 && pno >= 4) {
-           /* no free blocks left - don't read any further */
-           warn(_("No room for more\n"));
-           return -1;
-       }
-    }
-    if (fno < 3 || !*(fields[2]))
-      ul = orig ? orig->p.sys_type :
-          (is_extd || (pno > 3 && pct == 1 && show_extended))
-            ? EXTENDED_PARTITION : LINUX_NATIVE;
-    else if (!strcmp(fields[2], "L"))
-      ul = LINUX_NATIVE;
-    else if (!strcmp(fields[2], "S"))
-      ul = LINUX_SWAP;
-    else if (!strcmp(fields[2], "E"))
-      ul = EXTENDED_PARTITION;
-    else if (!strcmp(fields[2], "X"))
-      ul = LINUX_EXTENDED;
-    else if (get_ul(fields[2], &ul, LINUX_NATIVE, 16))
-      return 0;
-    if (ul > 255) {
-       warn(_("Illegal type\n"));
-       return 0;
-    }
-    p.p.sys_type = ul;
-    is_extd = is_extended(ul);
-
-    /* find start */
-    ff = first_free(pno, is_extd, ep, format, 0, z);
-    ff1 = ff * unitsize(format);
-    def = orig ? orig->start : (pno > 4 && pct > 1) ? 0 : ff1;
-    if (fno < 1 || !*(fields[0]))
-      p.start = def;
-    else {
-       if (get_ul(fields[0], &ul, def / unitsize(0), 0))
-         return 0;
-       p.start = ul * unitsize(0);
-       p.start -= (p.start % unitsize(format));
-    }
-
-    /* find length */
-    ml = max_length(pno, is_extd, ep, format, p.start / unitsize(format), z);
-    ml1 = ml * unitsize(format);
-    def = orig ? orig->size : (pno > 4 && pct > 1) ? 0 : ml1;
-    if (fno < 2 || !*(fields[1]))
-      p.size = def;
-    else {
-       if (get_ul(fields[1], &ul, def / unitsize(0), 0))
-         return 0;
-       p.size = ul * unitsize(0) + unitsize(format) - 1;
-       p.size -= (p.size % unitsize(format));
-    }
-    if (p.size > ml1) {
-       warn(_("Warning: given size (%lu) exceeds max allowable size (%lu)\n"),
-            (p.size + unitsize(0) - 1) / unitsize(0), ml1 / unitsize(0));
-       if (!force)
-         return 0;
-    }
-    if (p.size == 0 && pno >= 4 && (fno < 2 || !*(fields[1]))) {
-       warn(_("Warning: empty partition\n"));
-       if (!force)
-         return 0;
-    }
-    p.p.nr_sects = p.size;
-
-    if (p.size == 0 && !orig) {
-       if (fno < 1 || !*(fields[0]))
-         p.start = 0;
-       if (fno < 3 || !*(fields[2]))
-         p.p.sys_type = EMPTY_PARTITION;
-    }
-
-    if (p.start < ff1 && p.size > 0) {
-       warn(_("Warning: bad partition start (earliest %lu)\n"),
-            (ff1 + unitsize(0) - 1) / unitsize(0));
-       if (!force)
-         return 0;
-    }
-
-    if (fno < 4 || !*(fields[3]))
-      ul = (orig ? orig->p.bootable : 0);
-    else if (!strcmp(fields[3], "-"))
-      ul = 0;
-    else if (!strcmp(fields[3], "*") || !strcmp(fields[3], "+"))
-      ul = 0x80;
-    else {
-       warn(_("unrecognized bootable flag - choose - or *\n"));
-       return 0;
-    }
-    p.p.bootable = ul;
-
-    if (ep && ep->p.sys_type == EMPTY_PARTITION) {
-      if (!build_surrounding_extended(&p, ep, z))
-       return 0;
-    } else
-      if (!compute_start_sect(&p, ep))
-       return 0;
-
-    { longchs aa = chs_to_longchs(p.p.begin_chs), bb;
-
-      if (fno < 5) {
-         bb = aa;
-      } else if (fno < 7) {
-         warn(_("partial c,h,s specification?\n"));
-         return 0;
-      } else if (get_ul(fields[4], &bb.c, aa.c, 0) ||
-               get_ul(fields[5], &bb.h, aa.h, 0) ||
-               get_ul(fields[6], &bb.s, aa.s, 0))
-       return 0;
-      p.p.begin_chs = longchs_to_chs(bb,B);
-    }
-    { longchs aa = chs_to_longchs(p.p.end_chs), bb;
-
-      if (fno < 8) {
-         bb = aa;
-      } else if (fno < 10) {
-         warn(_("partial c,h,s specification?\n"));
-         return 0;
-      } else if (get_ul(fields[7], &bb.c, aa.c, 0) ||
-               get_ul(fields[8], &bb.h, aa.h, 0) ||
-               get_ul(fields[9], &bb.s, aa.s, 0))
-       return 0;
-      p.p.end_chs = longchs_to_chs(bb, B);
-    }
-
-    if (pno > 3 && p.size && show_extended && p.p.sys_type != EMPTY_PARTITION
-               && (is_extended(p.p.sys_type) != (pct == 1))) {
-       warn(_("Extended partition not where expected\n"));
-       if (!force)
-         return 0;
-    }
-
-    z->partitions[pno] = p;
-    if (pno >= z->partno)
-      z->partno += 4;          /* reqd for out_partition() */
-
-    if (interactive)
-      out_partition(dev, 0, &(z->partitions[pno]), z, B);
-
-    return 1;
-}
-
-/* ep either points to the extended partition to contain this one,
-   or to the empty partition that may become extended or is 0 */
-static int
-read_partition(char *dev, int interactive, int pno, struct part_desc *ep,
-              struct disk_desc *z) {
-    struct part_desc *p = &(z->partitions[pno]);
-    int i;
-
-    if (one_only) {
-       *p = oldp.partitions[pno];
-       if (one_only_pno != pno)
-         goto ret;
-    } else if (!show_extended && pno > 4 && pno%4)
-         goto ret;
-
-    while (!(i = read_line(pno, ep, dev, interactive, z)))
-      if (!interactive)
-       fatal(_("bad input\n"));
-    if (i < 0) {
-       p->ep = ep;
-       return 0;
-    }
-
-  ret:
-    p->ep = ep;
-    if (pno >= z->partno)
-      z->partno += 4;
-    return 1;
-}
-
-static void
-read_partition_chain(char *dev, int interactive, struct part_desc *ep,
-                    struct disk_desc *z) {
-    int i, base;
-
-    eob = 0;
-    while (1) {
-       base = z->partno;
-       if (base+4 > SIZE(z->partitions)) {
-           do_warn(_("too many partitions\n"));
-           break;
-       }
-       for (i=0; i<4; i++)
-         if (!read_partition(dev, interactive, base+i, ep, z))
-           return;
-       for (i=0; i<4; i++) {
-           ep = &(z->partitions[base+i]);
-           if (is_extended(ep->p.sys_type) && ep->size)
-             break;
-       }
-       if (i == 4) {
-           /* nothing found - maybe an empty partition is going
-              to be extended */
-           if (one_only || show_extended)
-             break;
-           ep = &(z->partitions[base+1]);
-           if (ep->size || ep->p.sys_type != EMPTY_PARTITION)
-             break;
-       }
-    }
-}
-
-static void
-read_input(char *dev, int interactive, struct disk_desc *z) {
-    int i;
-    struct part_desc *partitions = &(z->partitions[0]), *ep;
-
-    for (i=0; i < SIZE(z->partitions); i++)
-      partitions[i] = zero_part_desc;
-    z->partno = 0;
-
-    if (interactive)
-      warn(_("Input in the following format; absent fields get a default value.\n"
-             "<start> <size> <type [E,S,L,X,hex]> <bootable [-,*]> <c,h,s> <c,h,s>\n"
-             "Usually you only need to specify <start> and <size> (and perhaps <type>).\n"));
-    eof = 0;
-
-    for (i=0; i<4; i++)
-      read_partition(dev, interactive, i, 0, z);
-    for (i=0; i<4; i++) {
-       ep = partitions+i;
-       if (is_extended(ep->p.sys_type) && ep->size)
-         read_partition_chain(dev, interactive, ep, z);
-    }
-    add_sector_and_offset(z);
-}
-
-/*
- *  G. The command line
- */
-
-static void version(void) {
-    printf("%s %s %s (aeb@cwi.nl, %s) from util-linux-"
-          UTIL_LINUX_VERSION "\n",
-          PROGNAME, _("version"), VERSION, DATE);
-}
-
-static void
-usage(void) {
-    version();
-    printf(_("Usage: %s [options] device ...\n"), PROGNAME);
-    puts (_("device: something like /dev/hda or /dev/sda"));
-    puts (_("useful options:"));
-    puts (_("    -s [or --show-size]: list size of a partition"));
-    puts (_("    -c [or --id]:        print or change partition Id"));
-    puts (_("    -l [or --list]:      list partitions of each device"));
-    puts (_("    -d [or --dump]:      idem, but in a format suitable for later input"));
-    puts (_("    -i [or --increment]: number cylinders etc. from 1 instead of from 0"));
-    puts (_("    -uS, -uB, -uC, -uM:  accept/report in units of sectors/blocks/cylinders/MB"));
-    puts (_("    -T [or --list-types]:list the known partition types"));
-    puts (_("    -D [or --DOS]:       for DOS-compatibility: waste a little space"));
-    puts (_("    -R [or --re-read]:   make kernel reread partition table"));
-    puts (_("    -N# :                change only the partition with number #"));
-    puts (_("    -n :                 do not actually write to disk"));
-    puts (_("    -O file :            save the sectors that will be overwritten to file"));
-    puts (_("    -I file :            restore these sectors again"));
-    puts (_("    -v [or --version]:   print version"));
-    puts (_("    -? [or --help]:      print this message"));
-    puts (_("dangerous options:"));
-    puts (_("    -g [or --show-geometry]: print the kernel's idea of the geometry"));
-    puts (_("    -G [or --show-pt-geometry]: print geometry guessed from the partition table"));
-    puts (_("    -x [or --show-extended]: also list extended partitions on output\n"
-          "                             or expect descriptors for them on input"));
-    puts (_("    -L  [or --Linux]:      do not complain about things irrelevant for Linux"));
-    puts (_("    -q  [or --quiet]:      suppress warning messages"));
-    puts (_("    You can override the detected geometry using:"));
-    puts (_("    -C# [or --cylinders #]:set the number of cylinders to use"));
-    puts (_("    -H# [or --heads #]:    set the number of heads to use"));
-    puts (_("    -S# [or --sectors #]:  set the number of sectors to use"));
-    puts (_("You can disable all consistency checking with:"));
-    puts (_("    -f  [or --force]:      do what I say, even if it is stupid"));
-    exit(1);
-}
-
-static void
-activate_usage(char *progn) {
-    puts (_("Usage:"));
-    printf(_("%s device                 list active partitions on device\n"), progn);
-    printf(_("%s device n1 n2 ... activate partitions n1 ..., inactivate the rest\n"), progn);
-    printf(_("%s -An device     activate partition n, inactivate the other ones\n"), PROGNAME);
-    exit(1);
-}
-
-static void
-unhide_usage(char *progn) {
-    exit(1);
-}
-
-static char short_opts[] = "cdfgilnqsu:vx?1A::C:DGH:I:LN:O:RS:TU::V";
-
-#define PRINT_ID 0400
-#define CHANGE_ID 01000
-
-static const struct option long_opts[] = {
-    { "change-id",       no_argument, NULL, 'c' + CHANGE_ID },
-    { "print-id",        no_argument, NULL, 'c' + PRINT_ID },
-    { "id",              no_argument, NULL, 'c' },
-    { "dump",             no_argument, NULL, 'd' },
-    { "force",            no_argument, NULL, 'f' },
-    { "show-geometry",   no_argument, NULL, 'g' },
-    { "increment",        no_argument, NULL, 'i' },
-    { "list",             no_argument, NULL, 'l' },
-    { "quiet",            no_argument, NULL, 'q' },
-    { "show-size",        no_argument, NULL, 's' },
-    { "unit",       required_argument, NULL, 'u' },
-    { "version",          no_argument, NULL, 'v' },
-    { "show-extended",    no_argument, NULL, 'x' },
-    { "help",            no_argument, NULL, '?' },
-    { "one-only",         no_argument, NULL, '1' },
-    { "cylinders",  required_argument, NULL, 'C' },
-    { "heads",      required_argument, NULL, 'H' },
-    { "sectors",    required_argument, NULL, 'S' },
-    { "show-pt-geometry", no_argument, NULL, 'G' },
-    { "activate",   optional_argument, NULL, 'A' },
-    { "DOS",              no_argument, NULL, 'D' },
-    { "DOS-extended",    no_argument, NULL, 'E' },
-    { "Linux",            no_argument, NULL, 'L' },
-    { "re-read",          no_argument, NULL, 'R' },
-    { "list-types",       no_argument, NULL, 'T' },
-    { "unhide",     optional_argument, NULL, 'U' },
-    { "no-reread",        no_argument, NULL, 160 },
-    { "IBM",              no_argument, NULL, 161 },
-    { "leave-last",       no_argument, NULL, 161 },
-/* undocumented flags - not all completely implemented */
-    { "in-order",         no_argument, NULL, 128 },
-    { "not-in-order",     no_argument, NULL, 129 },
-    { "inside-outer",     no_argument, NULL, 130 },
-    { "not-inside-outer", no_argument, NULL, 131 },
-    { "nested",           no_argument, NULL, 132 },
-    { "chained",          no_argument, NULL, 133 },
-    { "onesector",        no_argument, NULL, 134 },
-    { NULL, 0, NULL, 0 }
-};
-
-static int
-is_ide_cdrom_or_tape(char *device) {
-       FILE *procf;
-       char buf[100];
-       struct stat statbuf;
-       int is_ide = 0;
-
-       /* No device was given explicitly, and we are trying some
-          likely things.  But opening /dev/hdc may produce errors like
-           "hdc: tray open or drive not ready"
-          if it happens to be a CD-ROM drive. It even happens that
-          the process hangs on the attempt to read a music CD.
-          So try to be careful. This only works since 2.1.73. */
-
-       if (strncmp("/dev/hd", device, 7))
-               return 0;
-
-       snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5);
-       procf = fopen(buf, "r");
-       if (procf != NULL && fgets(buf, sizeof(buf), procf))
-               is_ide = (!strncmp(buf, "cdrom", 5) ||
-                         !strncmp(buf, "tape", 4));
-       else
-               /* Now when this proc file does not exist, skip the
-                  device when it is read-only. */
-               if (stat(device, &statbuf) == 0)
-                       is_ide = ((statbuf.st_mode & 0222) == 0);
-
-       if (procf)
-               fclose(procf);
-       return is_ide;
-}
-
-static int
-is_probably_full_disk(char *name) {
-       struct hd_geometry geometry;
-       int fd, i = 0;
-
-       fd = open(name, O_RDONLY);
-       if (fd >= 0) {
-               i = ioctl(fd, HDIO_GETGEO, &geometry);
-               close(fd);
-       }
-       return (fd >= 0 && i == 0 && geometry.start == 0);
-}
-
-#define PROC_PARTITIONS        "/proc/partitions"
-static FILE *procf = NULL;
-
-static void
-openproc(void) {
-       procf = fopen(PROC_PARTITIONS, "r");
-       if (procf == NULL)
-               fprintf(stderr, _("cannot open %s\n"), PROC_PARTITIONS);
-}
-
-static char *
-nextproc(void) {
-       static char devname[120];
-       char line[100], ptname[100];
-       int ma, mi, sz;
-
-       if (procf == NULL)
-               return NULL;
-       while (fgets(line, sizeof(line), procf) != NULL) {
-               if (sscanf (line, " %d %d %d %[^\n ]",
-                           &ma, &mi, &sz, ptname) != 4)
-                       continue;
-               snprintf(devname, sizeof(devname), "/dev/%s", ptname);
-               if (!is_probably_full_disk(devname))
-                       continue;
-               return devname;
-       }
-
-       fclose(procf);
-       procf = NULL;
-       return NULL;
-}      
-
-static void do_list(char *dev, int silent);
-static void do_size(char *dev, int silent);
-static void do_geom(char *dev, int silent);
-static void do_pt_geom(char *dev, int silent);
-static void do_fdisk(char *dev);
-static void do_reread(char *dev);
-static void do_change_id(char *dev, char *part, char *id);
-static void do_unhide(char **av, int ac, char *arg);
-static void do_activate(char **av, int ac, char *arg);
-
-unsigned long long total_size;
-
-int
-main(int argc, char **argv) {
-    char *progn;
-    int c;
-    char *dev;
-    int opt_size = 0;
-    int opt_out_geom = 0;
-    int opt_out_pt_geom = 0;
-    int opt_reread = 0;
-    int activate = 0;
-    int do_id = 0;
-    int unhide = 0;
-    int fdisk = 0;
-    char *activatearg = 0;
-    char *unhidearg = 0;
-
-/*
-    setlocale(LC_ALL, "");
-    bindtextdomain(PACKAGE, LOCALEDIR);
-    textdomain(PACKAGE);
-*/
-    if (argc < 1)
-      fatal(_("no command?\n"));
-    if ((progn = rindex(argv[0], '/')) == NULL)
-      progn = argv[0];
-    else
-      progn++;
-    if (!strcmp(progn, "activate"))
-      activate = 1;            /* equivalent to `sfdisk -A' */
-#if 0                          /* not important enough to deserve a name */
-    else if (!strcmp(progn, "unhide"))
-      unhide = 1;              /* equivalent to `sfdisk -U' */
-#endif
-    else
-      fdisk = 1;
-
-    while ((c = getopt_long (argc, argv, short_opts, long_opts, NULL)) != -1) {
-       switch (c) {
-         case 'f':
-           force = 1; break;   /* does not imply quiet */
-         case 'g':
-           opt_out_geom = 1; break;
-         case 'G':
-           opt_out_pt_geom = 1; break;
-         case 'i':
-           increment = 1; break;
-         case 'c':
-         case 'c' + PRINT_ID:
-         case 'c' + CHANGE_ID:
-           do_id = c; break;
-         case 'd':
-           dump = 1; /* fall through */
-         case 'l':
-           opt_list = 1; break;
-         case 'n':
-           no_write = 1; break;
-         case 'q':
-           quiet = 1; break;
-         case 's':
-           opt_size = 1; break;
-         case 'u':
-           set_format(*optarg); break;
-         case 'v':
-           version();
-           exit(0);
-         case 'x':
-           show_extended = 1; break;
-         case 'A':
-           activatearg = optarg;
-           activate = 1; break;
-         case 'C':
-           U.cylinders = atoi(optarg); break;
-         case 'D':
-           DOS = 1; break;
-         case 'E':
-           DOS_extended = 1; break;
-         case 'H':
-           U.heads = atoi(optarg); break;
-         case 'L':
-           Linux = 1; break;
-         case 'N':
-           one_only = atoi(optarg); break;
-         case 'I':
-           restore_sector_file = optarg; break;
-         case 'O':
-           save_sector_file = optarg; break;
-         case 'R':
-           opt_reread = 1; break;
-         case 'S':
-           U.sectors = atoi(optarg); break;
-         case 'T':
-           list_types();
-           exit(0);
-         case 'U':
-           unhidearg = optarg;
-           unhide = 1; break;
-         case 'V':
-           verify = 1; break;
-         case '?':
-         default:
-           usage(); break;
-
-         /* undocumented flags */
-         case 128:
-           partitions_in_order = 1; break;
-         case 129:
-           partitions_in_order = 0; break;
-         case 130:
-           all_logicals_inside_outermost_extended = 1; break;
-         case 131:
-           all_logicals_inside_outermost_extended = 0; break;
-         case 132:
-           boxes = NESTED; break;
-         case 133:
-           boxes = CHAINED; break;
-         case 134:
-           boxes = ONESECTOR; break;
-
-         /* more flags */
-         case 160:
-           no_reread = 1; break;
-         case 161:
-           leave_last = 1; break;
-       }
-    }
-
-    if (optind == argc &&
-       (opt_list || opt_out_geom || opt_out_pt_geom || opt_size || verify)) {
-       /* try all known devices */
-       total_size = 0;
-       openproc();
-       while ((dev = nextproc()) != NULL) {
-           if (is_ide_cdrom_or_tape(dev))
-               continue;
-           if (opt_out_geom)
-               do_geom(dev, 1);
-           if (opt_out_pt_geom)
-               do_pt_geom(dev, 1);
-           if (opt_size)
-               do_size(dev, 1);
-           if (opt_list || verify)
-               do_list(dev, 1);
-       }
-
-       if (opt_size)
-         printf(_("total: %llu blocks\n"), total_size);
-
-       exit(exit_status);
-    }
-
-    if (optind == argc) {
-       if (activate)
-         activate_usage(fdisk ? "sfdisk -A" : progn);
-       else if (unhide)
-         unhide_usage(fdisk ? "sfdisk -U" : progn);
-       else
-         usage();
-    }
-
-    if (opt_list || opt_out_geom || opt_out_pt_geom || opt_size || verify) {
-       while (optind < argc) {
-           if (opt_out_geom)
-             do_geom(argv[optind], 0);
-           if (opt_out_pt_geom)
-             do_pt_geom(argv[optind], 0);
-           if (opt_size)
-             do_size(argv[optind], 0);
-           if (opt_list || verify)
-             do_list(argv[optind], 0);
-           optind++;
-       }
-       exit(exit_status);
-    }
-
-    if (activate) {
-       do_activate(argv+optind, argc-optind, activatearg);
-       exit(exit_status);
-    }
-    if (unhide) {
-       do_unhide(argv+optind, argc-optind, unhidearg);
-       exit(exit_status);
-    }
-    if (do_id) {
-        if ((do_id & PRINT_ID) != 0 && optind != argc-2)
-         fatal(_("usage: sfdisk --print-id device partition-number\n"));
-       else if ((do_id & CHANGE_ID) != 0 && optind != argc-3)
-         fatal(_("usage: sfdisk --change-id device partition-number Id\n"));
-       else if (optind != argc-3 && optind != argc-2)
-         fatal(_("usage: sfdisk --id device partition-number [Id]\n"));
-       do_change_id(argv[optind], argv[optind+1],
-                    (optind == argc-2) ? 0 : argv[optind+2]);
-       exit(exit_status);
-    }
-      
-    if (optind != argc-1)
-      fatal(_("can specify only one device (except with -l or -s)\n"));
-    dev = argv[optind];
-
-    if (opt_reread)
-      do_reread(dev);
-    else if (restore_sector_file)
-      restore_sectors(dev);
-    else
-      do_fdisk(dev);
-
-    return 0;
-}
-
-/*
- *  H. Listing the current situation
- */
-
-static int
-my_open (char *dev, int rw, int silent) {
-    int fd, mode;
-
-    mode = (rw ? O_RDWR : O_RDONLY);
-    fd = open(dev, mode);
-    if (fd < 0 && !silent) {
-       perror(dev);
-       if (rw)
-               fatal(_("cannot open %s read-write\n"), dev);
-       else
-               fatal(_("cannot open %s for reading\n"), dev);
-    }
-    return fd;
-}
-
-static void
-do_list (char *dev, int silent) {
-    int fd;
-    struct disk_desc *z;
-
-    fd = my_open(dev, 0, silent);
-    if (fd < 0)
-       return;
-
-    z = &oldp;
-
-    free_sectors();
-    get_cylindersize(dev, fd, dump ? 1 : opt_list ? 0 : 1);
-    get_partitions(dev, fd, z);
-
-    if (opt_list)
-      out_partitions(dev, z);
-
-    if (verify) {
-       if (partitions_ok(z))
-         warn(_("%s: OK\n"), dev);
-       else
-         exit_status = 1;
-    }
-}
-
-static void
-do_geom (char *dev, int silent) {
-    int fd;
-    struct geometry R;
-
-    fd = my_open(dev, 0, silent);
-    if (fd < 0)
-       return;
-
-    R = get_geometry(dev, fd, silent);
-    if (R.cylinders)
-       printf(_("%s: %ld cylinders, %ld heads, %ld sectors/track\n"),
-              dev, R.cylinders, R.heads, R.sectors);
-}
-
-static void
-do_pt_geom (char *dev, int silent) {
-    int fd;
-    struct disk_desc *z;
-    struct geometry R;
-
-    fd = my_open(dev, 0, silent);
-    if (fd < 0)
-       return;
-
-    z = &oldp;
-
-    free_sectors();
-    get_cylindersize(dev, fd, 1);
-    get_partitions(dev, fd, z);
-
-    R = B;
-
-    if (z->partno != 0 && get_fdisk_geometry(z)) {
-           R.heads = F.heads;
-           R.sectors = F.sectors;
-           R.cylindersize = R.heads * R.sectors;
-           R.cylinders = (R.cylindersize == 0) ? 0 :
-                   R.total_size / R.cylindersize;
-    }
-
-    if (R.cylinders)
-       printf(_("%s: %ld cylinders, %ld heads, %ld sectors/track\n"),
-              dev, R.cylinders, R.heads, R.sectors);
-}
-
-/* for compatibility with earlier fdisk: provide option -s */
-static void
-do_size (char *dev, int silent) {
-    int fd;
-    unsigned long long size;
-
-    fd = my_open(dev, 0, silent);
-    if (fd < 0)
-       return;
-
-    if (disksize(fd, &size)) {
-       if (!silent) {
-           perror(dev);
-           fatal(_("Cannot get size of %s\n"), dev);
-       }
-       return;
-    }
-
-    size /= 2;                 /* convert sectors to blocks */
-
-    /* a CDROM drive without mounted CD yields MAXINT */
-    if (silent && size == ((1<<30)-1))
-      return;
-
-    if (silent)
-      printf("%s: %9llu\n", dev, size);
-    else
-      printf("%llu\n", size);
-
-    total_size += size;
-}
-
-/*
- * Activate: usually one wants to have a single primary partition
- * to be active. OS/2 fdisk makes non-bootable logical partitions
- * active - I don't know what that means to OS/2 Boot Manager.
- *
- * Call: activate /dev/hda 2 5 7       make these partitions active
- *                                     and the remaining ones inactive
- * Or:   sfdisk -A /dev/hda 2 5 7
- *
- * If only a single partition must be active, one may also use the form
- *       sfdisk -A2 /dev/hda
- *
- * With "activate /dev/hda" or "sfdisk -A /dev/hda" the active partitions
- * are listed but not changed. To get zero active partitions, use
- * "activate /dev/hda none" or "sfdisk -A /dev/hda none".
- * Use something like `echo ",,,*" | sfdisk -N2 /dev/hda' to only make
- * /dev/hda2 active, without changing other partitions.
- *
- * A warning will be given if after the change not precisely one primary
- * partition is active.
- *
- * The present syntax was chosen to be (somewhat) compatible with the
- * activate from the LILO package.
- */
-static void
-set_active (struct disk_desc *z, char *pnam) {
-    int pno;
-
-    pno = asc_to_index(pnam, z);
-    if (z->partitions[pno].ptype == DOS_TYPE)
-           z->partitions[pno].p.bootable = 0x80;
-}
-
-static void
-do_activate (char **av, int ac, char *arg) {
-    char *dev = av[0];
-    int fd;
-    int rw, i, pno, lpno;
-    struct disk_desc *z;
-
-    z = &oldp;
-
-    rw = (!no_write && (arg || ac > 1));
-    fd = my_open(dev, rw, 0);
-
-    free_sectors();
-    get_cylindersize(dev, fd, 1);
-    get_partitions(dev, fd, z);
-
-    if (!arg && ac == 1) {
-       /* list active partitions */
-       for (pno=0; pno < z->partno; pno++) {
-           if (z->partitions[pno].p.bootable) {
-               lpno = index_to_linux(pno, z);
-               if (pno == linux_to_index(lpno, z))
-                 printf("%s\n", partname(dev, lpno, 0));
-               else
-                 printf("%s#%d\n", dev, pno);
-               if (z->partitions[pno].p.bootable != 0x80)
-                 warn(_("bad active byte: 0x%x instead of 0x80\n"),
-                      z->partitions[pno].p.bootable);
-           }
-       }
-    } else {
-       /* clear `active byte' everywhere */
-       for (pno=0; pno < z->partno; pno++)
-           if (z->partitions[pno].ptype == DOS_TYPE)
-               z->partitions[pno].p.bootable = 0;
-
-       /* then set where desired */
-       if (ac == 1)
-         set_active(z, arg);
-       else for(i=1; i<ac; i++)
-         set_active(z, av[i]);
-
-       /* then write to disk */
-       if (write_partitions(dev, fd, z))
-         warn(_("Done\n\n"));
-       else
-         exit_status = 1;
-    }
-    i = 0;
-    for (pno=0; pno < z->partno && pno < 4; pno++)
-      if (z->partitions[pno].p.bootable)
-       i++;
-    if (i != 1)
-      warn(_("You have %d active primary partitions. This does not matter for LILO,\n"
-          "but the DOS MBR will only boot a disk with 1 active partition.\n"), i);
-}
-
-static void
-set_unhidden (struct disk_desc *z, char *pnam) {
-    int pno;
-    unsigned char id;
-
-    pno = asc_to_index(pnam, z);
-    id = z->partitions[pno].p.sys_type;
-    if (id == 0x11 || id == 0x14 || id == 0x16 || id == 0x17)
-      id -= 0x10;
-    else
-      fatal(_("partition %s has id %x and is not hidden\n"), pnam, id);
-    z->partitions[pno].p.sys_type = id;
-}
-
-/*
- * maybe remove and make part of --change-id
- */
-static void
-do_unhide (char **av, int ac, char *arg) {
-    char *dev = av[0];
-    int fd, rw, i;
-    struct disk_desc *z;
-
-    z = &oldp;
-
-    rw = !no_write;
-    fd = my_open(dev, rw, 0);
-
-    free_sectors();
-    get_cylindersize(dev, fd, 1);
-    get_partitions(dev, fd, z);
-
-    /* unhide where desired */
-    if (ac == 1)
-      set_unhidden(z, arg);
-    else for(i=1; i<ac; i++)
-      set_unhidden(z, av[i]);
-
-    /* then write to disk */
-    if (write_partitions(dev, fd, z))
-      warn(_("Done\n\n"));
-    else
-      exit_status = 1;
-}
-
-static void
-do_change_id(char *dev, char *pnam, char *id) {
-    int fd, rw, pno;
-    struct disk_desc *z;
-    unsigned long i;
-
-    z = &oldp;
-
-    rw = !no_write;
-    fd = my_open(dev, rw, 0);
-
-    free_sectors();
-    get_cylindersize(dev, fd, 1);
-    get_partitions(dev, fd, z);
-
-    pno = asc_to_index(pnam, z);
-    if (id == 0) {
-      printf("%x\n", z->partitions[pno].p.sys_type);
-      return;
-    }
-    i = strtoul(id, NULL, 16);
-    if (i > 255)
-      fatal(_("Bad Id %lx\n"), i);
-    z->partitions[pno].p.sys_type = i;
-
-    if (write_partitions(dev, fd, z))
-      warn(_("Done\n\n"));
-    else
-      exit_status = 1;
-}
-
-static void
-do_reread(char *dev) {
-    int fd;
-
-    fd = my_open(dev, 0, 0);
-    if (reread_ioctl(fd))
-      do_warn(_("This disk is currently in use.\n"));
-}
-
-/*
- *  I. Writing the new situation
- */
-
-static void
-do_fdisk(char *dev){
-    int fd;
-    int c, answer;
-    struct stat statbuf;
-    int interactive = isatty(0);
-    struct disk_desc *z;
-
-    if (stat(dev, &statbuf) < 0) {
-       perror(dev);
-       fatal(_("Fatal error: cannot find %s\n"), dev);
-    }
-    if (!S_ISBLK(statbuf.st_mode)) {
-       do_warn(_("Warning: %s is not a block device\n"), dev);
-       no_reread = 1;
-    }
-    fd = my_open(dev, !no_write, 0);
-
-    if (!no_write && !no_reread) {
-       warn(_("Checking that no-one is using this disk right now ...\n"));
-       if (reread_ioctl(fd)) {
-           do_warn(_("\nThis disk is currently in use - repartitioning is probably a bad idea.\n"
-                  "Umount all file systems, and swapoff all swap partitions on this disk.\n"
-                  "Use the --no-reread flag to suppress this check.\n"));
-           if (!force) {
-               do_warn(_("Use the --force flag to overrule all checks.\n"));
-               exit(1);
-           }
-       } else
-         warn(_("OK\n"));
-    }
-
-    z = &oldp;
-
-    free_sectors();
-    get_cylindersize(dev, fd, 0);
-    get_partitions(dev, fd, z);
-
-    printf(_("Old situation:\n"));
-    out_partitions(dev, z);
-
-    if (one_only && (one_only_pno = linux_to_index(one_only, z)) < 0)
-      fatal(_("Partition %d does not exist, cannot change it\n"), one_only);
-
-    z = &newp;
-
-    while(1) {
-
-       read_input(dev, interactive, z);
-
-       printf(_("New situation:\n"));
-       out_partitions(dev, z);
-
-       if (!partitions_ok(z) && !force) {
-           if (!interactive)
-             fatal(_("I don't like these partitions - nothing changed.\n"
-                     "(If you really want this, use the --force option.)\n"));
-           else
-             do_warn(_("I don't like this - probably you should answer No\n"));
-       }
-      ask:
-       if (interactive) {
-           if (no_write)
-             printf(_("Are you satisfied with this? [ynq] "));
-           else
-             printf(_("Do you want to write this to disk? [ynq] "));
-           answer = c = getchar();
-           while (c != '\n' && c != EOF)
-             c = getchar();
-           if (c == EOF)
-               printf(_("\nsfdisk: premature end of input\n"));
-           if (c == EOF || answer == 'q' || answer == 'Q') {
-               fatal(_("Quitting - nothing changed\n"));
-           } else if (answer == 'n' || answer == 'N') {
-               continue;
-           } else if (answer == 'y' || answer == 'Y') {
-               break;
-           } else {
-               printf(_("Please answer one of y,n,q\n"));
-               goto ask;
-           }
-       } else
-         break;
-    }
-
-    if (write_partitions(dev, fd, z))
-      printf(_("Successfully wrote the new partition table\n\n"));
-    else
-      exit_status = 1;
-
-    reread_disk_partition(dev, fd);
-    
-    warn(_("If you created or changed a DOS partition, /dev/foo7, say, then use dd(1)\n"
-        "to zero the first 512 bytes:  dd if=/dev/zero of=/dev/foo7 bs=512 count=1\n"
-        "(See fdisk(8).)\n"));
-    
-    sync();                    /* superstition */
-    exit(exit_status);
-}
diff --git a/debian/sfdisk/xstrncpy.c b/debian/sfdisk/xstrncpy.c
deleted file mode 100644 (file)
index 7975426..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-/* NUL-terminated version of strncpy() */
-#include <string.h>
-#include "xstrncpy.h"
-
-/* caller guarantees n > 0 */
-void
-xstrncpy(char *dest, const char *src, size_t n) {
-       strncpy(dest, src, n-1);
-       dest[n-1] = 0;
-}
diff --git a/debian/sfdisk/xstrncpy.h b/debian/sfdisk/xstrncpy.h
deleted file mode 100644 (file)
index 05c8fa2..0000000
+++ /dev/null
@@ -1 +0,0 @@
-extern void xstrncpy(char *dest, const char *src, size_t n);
diff --git a/debian/source/format b/debian/source/format
deleted file mode 100644 (file)
index 163aaf8..0000000
+++ /dev/null
@@ -1 +0,0 @@
-3.0 (quilt)
diff --git a/debian/tree/busybox-udeb/usr/share/udhcpc/default.script b/debian/tree/busybox-udeb/usr/share/udhcpc/default.script
deleted file mode 100755 (executable)
index 1284e1f..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/bin/sh -e
-
-# udhcpc client script to be used by Debian Installer
-# Copyright (C) 2009, 2010 Otavio Salvador <otavio@debian.org>
-
-comma_separate() {
-       echo "$1" | sed 's/ /,/g'
-}
-
-do_resolv_conf() {
-       local cfg=/etc/resolv.conf
-
-       if [ -n "$domain" ] || [ -n "$dns" ]; then
-               echo -n > $cfg
-               if [ -n "$domain" ]; then
-                       echo search $domain >> $cfg
-               fi
-
-               for i in $dns ; do
-                       echo nameserver $i >> $cfg
-               done
-       fi
-}
-
-do_hostname() {
-       local current=$(cat /proc/sys/kernel/hostname)
-
-       if [ -z "$current" ] || [ "$current" = "(none)" ]; then
-               echo "$hostname" > /proc/sys/kernel/hostname
-       fi
-}
-
-do_leases() {
-       local file="/var/lib/udhcp/udhcpc.leases"
-
-       mkdir -p /var/lib/udhcp
-       cat >> $file <<EOF
-lease {
-  interface "$interface";
-  fixed-address $ip;
-  filename "$boot_file";
-  option subnet-mask $subnet;
-  option domain-name "$domain";
-  option host-name "$hostname";
-  option domain-name-servers $(comma_separate "$dns");
-  option dhcp-server-identifier $serverid;
-  option dhcp-lease-time $lease;
-  option routers $(comma_separate "$router");
-  option ntp-servers $(comma_separate "$ntpsrv");
-}
-EOF
-}
-
-case "$1" in
-       deconfig)
-               ip link set "$interface" up
-               ip addr flush dev "$interface"
-               ;;
-
-       bound|renew)
-               do_hostname
-               
-               ip addr add "$ip/$subnet" dev "$interface"
-
-               if [ -n "$mtu" ]; then
-                       ip link set "$interface" mtu "$mtu"
-               fi
-
-               for r in "$router"; do
-                       ip route add default via "$r"
-               done
-
-               do_resolv_conf
-
-               # Get the domain name into a file suitable for netcfg to read.
-               printf "$domain" > /tmp/domain_name
-
-               if [ -n "$ntpsrv" ]; then
-                       printf "$ntpsrv" > /tmp/dhcp-ntp-servers
-               fi
-
-               logger -t udhcpc "Got IP $ip (using $interface) and routing through $router"
-
-               do_leases
-               ;;
-       *)
-               echo "udhcpc: has been called with an unknown param: $1"
-               ;;
-esac
-
-exit 0
diff --git a/debian/tree/udhcpc/usr/share/udhcpc/default.script b/debian/tree/udhcpc/usr/share/udhcpc/default.script
deleted file mode 100755 (executable)
index 26b95c1..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/bin/sh
-# Busybox udhcpc dispatcher script. Copyright (C) 2009 by Axel Beckert.
-#
-# Based on the busybox example scripts and the old udhcp source
-# package default.* scripts.
-
-RESOLV_CONF="/etc/resolv.conf"
-
-case $1 in
-    bound|renew)
-       [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
-       [ -n "$subnet" ] && NETMASK="netmask $subnet"
-
-       /sbin/ifconfig $interface $ip $BROADCAST $NETMASK
-
-       if [ -n "$router" ]; then
-           echo "$0: Resetting default routes"
-           while /sbin/route del default gw 0.0.0.0 dev $interface; do :; done
-
-           metric=0
-           for i in $router; do
-               /sbin/route add default gw $i dev $interface metric $metric
-               metric=$(($metric + 1))
-           done
-       fi
-
-       # Update resolver configuration file
-       R=""
-       [ -n "$domain" ] && R="domain $domain
-"
-       for i in $dns; do
-           echo "$0: Adding DNS $i"
-           R="${R}nameserver $i
-"
-       done
-
-       if [ -x /sbin/resolvconf ]; then
-           echo -n "$R" | resolvconf -a "${interface}.udhcpc"
-       else
-           echo -n "$R" > "$RESOLV_CONF"
-       fi
-       ;;
-
-    deconfig)
-       if [ -x /sbin/resolvconf ]; then
-           resolvconf -d "${interface}.udhcpc"
-       fi
-       /sbin/ifconfig $interface 0.0.0.0
-       ;;
-
-    leasefail)
-       echo "$0: Lease failed: $message"
-       ;;
-
-    nak)
-       echo "$0: Received a NAK: $message"
-       ;;
-
-    *)
-       echo "$0: Unknown udhcpc command: $1";
-       exit 1;
-       ;;
-esac
diff --git a/debian/tree/udhcpd/etc/default/udhcpd b/debian/tree/udhcpd/etc/default/udhcpd
deleted file mode 100644 (file)
index 34ef72b..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# Comment the following line to enable
-DHCPD_ENABLED="no"
-
-# Options to pass to busybox' udhcpd.
-#
-# -S    Log to syslog
-# -f    run in foreground
-
-DHCPD_OPTS="-S"
diff --git a/debian/tree/udhcpd/etc/init.d/udhcpd b/debian/tree/udhcpd/etc/init.d/udhcpd
deleted file mode 100644 (file)
index e7f8ddd..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-#! /bin/sh
-#
-# Written by Miquel van Smoorenburg <miquels@cistron.nl>.
-# Modified for Debian GNU/Linux by Ian Murdock <imurdock@gnu.ai.mit.edu>
-# and Axel Beckert <abe@deuxchevaux.org>.
-#
-### BEGIN INIT INFO
-# Provides:          udhcpd
-# Required-Start:    $remote_fs $syslog
-# Required-Stop:     $remote_fs $syslog
-# Default-Start:     2 3 4 5
-# Default-Stop:      0 1 6
-# Short-Description: Start busybox udhcpd at boot time
-### END INIT INFO
-
-PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
-DAEMON=/usr/sbin/udhcpd
-NAME=udhcpd
-DESC="very small Busybox based DHCP server"
-DHCPD_OPTS="-S" # Additional options given to the server
-
-test -x $DAEMON || exit 0
-
-# Include defaults if available
-if [ -f /etc/default/udhcpd ] ; then
-       . /etc/default/udhcpd
-fi
-
-if [ "$DHCPD_ENABLED" = "no" ]; then
-    echo $NAME: Disabled. Edit /etc/default/udhcpd to enable it.
-    exit 0;
-fi
-
-set -e
-
-case "$1" in
-  start)
-       echo -n "Starting $DESC: "
-       start-stop-daemon --start --verbose --pidfile /var/run/$NAME.pid \
-               --oknodo --exec $DAEMON -- $DHCPD_OPTS
-       echo "$NAME."
-       ;;
-  stop)
-       echo -n "Stopping $DESC: "
-       start-stop-daemon --stop --verbose --pidfile /var/run/$NAME.pid \
-               --oknodo --exec $DAEMON
-       echo "$NAME."
-       ;;
-  restart|force-reload)
-       $0 stop
-       sleep 1
-       $0 start        
-       ;;
-  *)
-       N=/etc/init.d/$NAME
-       echo "Usage: $N {start|stop|restart|force-reload}" >&2
-       exit 1
-       ;;
-esac
-
-exit 0
diff --git a/debian/tree/udhcpd/etc/udhcpd.conf b/debian/tree/udhcpd/etc/udhcpd.conf
deleted file mode 100644 (file)
index 672c481..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-# Sample udhcpd configuration file (/etc/udhcpd.conf)
-
-# The start and end of the IP lease block
-
-start          192.168.0.20    #default: 192.168.0.20
-end            192.168.0.254   #default: 192.168.0.254
-
-
-# The interface that udhcpd will use
-
-interface      eth0            #default: eth0
-
-
-# The maximim number of leases (includes addressesd reserved
-# by OFFER's, DECLINE's, and ARP conficts
-
-#max_leases    254             #default: 254
-
-
-# If remaining is true (default), udhcpd will store the time
-# remaining for each lease in the udhcpd leases file. This is
-# for embedded systems that cannot keep time between reboots.
-# If you set remaining to no, the absolute time that the lease
-# expires at will be stored in the dhcpd.leases file.
-
-#remaining     yes             #default: yes
-
-
-# The time period at which udhcpd will write out a dhcpd.leases
-# file. If this is 0, udhcpd will never automatically write a
-# lease file. (specified in seconds)
-
-#auto_time     7200            #default: 7200 (2 hours)
-
-
-# The amount of time that an IP will be reserved (leased) for if a
-# DHCP decline message is received (seconds).
-
-#decline_time  3600            #default: 3600 (1 hour)
-
-
-# The amount of time that an IP will be reserved (leased) for if an
-# ARP conflct occurs. (seconds
-
-#conflict_time 3600            #default: 3600 (1 hour)
-
-
-# How long an offered address is reserved (leased) in seconds
-
-#offer_time    60              #default: 60 (1 minute)
-
-# If a lease to be given is below this value, the full lease time is
-# instead used (seconds).
-
-#min_lease     60              #defult: 60
-
-
-# The location of the leases file
-
-#lease_file    /var/lib/misc/udhcpd.leases     #defualt: /var/lib/misc/udhcpd.leases
-
-# The location of the pid file
-#pidfile       /var/run/udhcpd.pid     #default: /var/run/udhcpd.pid
-
-# Everytime udhcpd writes a leases file, the below script will be called.
-# Useful for writing the lease file to flash every few hours.
-
-#notify_file                           #default: (no script)
-
-#notify_file   dumpleases      # <--- useful for debugging
-
-# The following are bootp specific options, setable by udhcpd.
-
-#siaddr                192.168.0.22            #default: 0.0.0.0
-
-#sname         zorak                   #default: (none)
-
-#boot_file     /var/nfs_root           #default: (none)
-
-# The remainer of options are DHCP options and can be specifed with the
-# keyword 'opt' or 'option'. If an option can take multiple items, such
-# as the dns option, they can be listed on the same line, or multiple
-# lines. The only option with a default is 'lease'.
-
-#Examles
-opt    dns     192.168.10.2 192.168.10.10
-option subnet  255.255.255.0
-opt    router  192.168.10.2
-opt    wins    192.168.10.10
-option dns     129.219.13.81   # appened to above DNS servers for a total of 3
-option domain  local
-option lease   864000          # 10 days of seconds
-
-
-# Currently supported options, for more info, see options.c
-#opt subnet
-#opt timezone
-#opt router
-#opt timesrv
-#opt namesrv
-#opt dns
-#opt logsrv
-#opt cookiesrv
-#opt lprsrv
-#opt bootsize
-#opt domain
-#opt swapsrv
-#opt rootpath
-#opt ipttl
-#opt mtu
-#opt broadcast
-#opt wins
-#opt lease
-#opt ntpsrv
-#opt tftp
-#opt bootfile
-#opt wpad
-
-# Static leases map
-#static_lease 00:60:08:11:CE:4E 192.168.0.54
-#static_lease 00:60:08:11:CE:3E 192.168.0.44
-
-
diff --git a/debian/tree/udhcpd/usr/share/man/man5/udhcpd.conf.5 b/debian/tree/udhcpd/usr/share/man/man5/udhcpd.conf.5
deleted file mode 100644 (file)
index 99e66ca..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-.TH UDHCPD.CONF 5 2001-09-26 GNU/Linux "GNU/Linux Administrator's Manual"
-.SH NAME
-udhcpd.conf \- udhcp server configuration file
-.SH DESCRIPTION
-The file
-.I /etc/udhcpd.conf
-contains configuration information specific to the udhcp server.
-It should contain one configuration keyword per line, followed by
-appropriate configuration information.
-.SH OPTIONS
-.TP
-.BI start\  ADDRESS
-The starting address of the IP lease block is
-.IR ADDRESS .
-The default is
-.BR 192.168.0.20 .
-.TP
-.BI end\  ADDRESS
-The ending address of the IP lease block is
-.IR ADDRESS .
-The default is
-.BR 192.168.0.254 .
-.TP
-.BI interface\  INTERFACE
-The udhcp server should listen on
-.IR INTERFACE .
-The default is
-.BR eth0 .
-.TP
-.BI max_leases\  LEASES
-Offer at most
-.I LEASES
-leases (including those reserved by OFFERs, DECLINEs, and ARP
-conflicts).  The default is
-.BR 254 .
-.TP 
-.BI remaining\  REMAINING
-If
-.I REMAINING
-is
-.BR yes ,
-store the time remaining for each lease.  If it is
-.BR no ,
-store the expiration time for each lease.  The default is
-.BR yes .
-.TP
-.BI auto_time\  SECONDS
-Write the lease information to a file every
-.I SECONDS
-seconds.  The default is
-.BR 7200 .
-.TP
-.BI decline_time\  SECONDS
-Reserve an IP for
-.I SECONDS
-seconds if a DHCP decline message is received.  The default is
-.BR 3600 .
-.TP
-.BI conflict_time\  SECONDS
-Reserve an IP for
-.I SECONDS
-seconds if an ARP conflict occurs.  The default is
-.BR 3600 .
-.TP
-.BI offer_time\  SECONDS
-Reserve an IP for
-.I SECONDS
-seconds if it is offered.  The default is
-.BR 60 .
-.TP
-.BI min_lease\  SECONDS
-Reserve an IP for the full lease time if the lease to be given is less than
-.I SECONDS
-seconds.  The default is
-.BR 60 .
-.TP
-.BI lease_file\  FILE
-Write the lease information to
-.IR FILE .
-The default is
-.BR /var/lib/misc/udhcpd.leases .
-.TP
-.BI pidfile\  FILE
-Write the process ID to
-.IR FILE .
-The default is
-.BR /var/run/udhcpd.pid .
-.TP
-.BI notify_file\  FILE
-Execute
-.I FILE
-after the lease information is written.  By default, no file is executed.
-.TP
-.BI siaddr\  ADDRESS
-BOOTP specific option.  The default is
-.BR 0.0.0.0 .
-.TP
-.BI sname\  NAME
-BOOTP specific option.  There is no default.
-.TP
-.BI boot_file\  FILE
-BOOTP specific option.  There is no default.
-.TP
-.BI option\  OPTION
-DHCP specific option.
-.RS
-.TP
-.BI subnet\  ADDRESS
-.TP
-.BI timezone\  OFFSET
-.TP
-.BI router\  ADDRESS...
-.TP
-.BI timesvr\  ADDRESS...
-.TP
-.BI namesvr\  ADDRESS...
-.TP
-.BI dns\  ADDRESS...
-.TP
-.BI logsvr\  ADDRESS...
-.TP
-.BI cookiesvr\  ADDRESS...
-.TP
-.BI lprsvr\  ADDRESS...
-.TP
-.BI hostname\  HOSTNAME
-.TP
-.BI bootsize\  SIZE
-.TP
-.BI domain\  DOMAIN
-.TP
-.BI swapsvr\  ADDRESS
-.TP
-.BI rootpath\  PATH
-.TP
-.BI ipttl\  TTL
-.TP
-.BI mtu\  MTU
-.TP
-.BI broadcast\  ADDRESS
-.TP
-.BI ntpsrv\  ADDRESS...
-.TP
-.BI wins\  ADDRESS...
-.TP
-.BI requestip\  ADDRESS
-.TP
-.BI lease\  SECONDS
-.TP
-.BI dhcptype\  TYPE
-.TP
-.BI serverid\  ADDRESS
-.TP
-.BI tftp\  FILE
-.TP
-.BI wpad\ URL
-.TP
-.BI bootfile\  FILE
-The default for
-.B lease
-is
-.BR 864000 .
-There are no defaults for the other options.
-.RE
-.SH SEE ALSO
-.BR udhcpd (8).
diff --git a/debian/udhcpc.install b/debian/udhcpc.install
deleted file mode 100644 (file)
index eaf43e3..0000000
+++ /dev/null
@@ -1 +0,0 @@
-debian/tree/udhcpc/* /
diff --git a/debian/udhcpd.install b/debian/udhcpd.install
deleted file mode 100644 (file)
index 0d57ee8..0000000
+++ /dev/null
@@ -1 +0,0 @@
-debian/tree/udhcpd/* /
index 838d8f0..cbc09b5 100644 (file)
@@ -83,4 +83,3 @@ config WHICH
          print out their pathnames.
 
 endmenu
-
index da34e4e..d41b5c8 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
index 2c4e196..983d7a2 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (C) 2000 by Daniel Jacobowitz
  * Written by Daniel Jacobowitz <dan@debian.org>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* Coreutils 6.12 man page says:
  *        -p; else /tmp [deprecated]
  */
 
+//usage:#define mktemp_trivial_usage
+//usage:       "[-dt] [-p DIR] [TEMPLATE]"
+//usage:#define mktemp_full_usage "\n\n"
+//usage:       "Create a temporary file with name based on TEMPLATE and print its name.\n"
+//usage:       "TEMPLATE must end with XXXXXX (e.g. [/dir/]nameXXXXXX).\n"
+//usage:       "Without TEMPLATE, -t tmp.XXXXXX is assumed.\n"
+//usage:     "\n       -d      Make directory, not file"
+//usage:     "\n       -q      Fail silently on errors"
+//usage:     "\n       -t      Prepend base directory name to TEMPLATE"
+//usage:     "\n       -p DIR  Use DIR as a base directory (implies -t)"
+//usage:     "\n       -u      Do not create anything; print a name"
+//usage:     "\n"
+//usage:     "\nBase directory is: -p DIR, else $TMPDIR, else /tmp"
+//usage:
+//usage:#define mktemp_example_usage
+//usage:       "$ mktemp /tmp/temp.XXXXXX\n"
+//usage:       "/tmp/temp.mWiLjM\n"
+//usage:       "$ ls -la /tmp/temp.mWiLjM\n"
+//usage:       "-rw-------    1 andersen andersen        0 Apr 25 17:10 /tmp/temp.mWiLjM\n"
 
 #include "libbb.h"
 
@@ -40,28 +59,55 @@ int mktemp_main(int argc UNUSED_PARAM, char **argv)
        const char *path;
        char *chp;
        unsigned opts;
+       enum {
+               OPT_d = 1 << 0,
+               OPT_q = 1 << 1,
+               OPT_t = 1 << 2,
+               OPT_p = 1 << 3,
+               OPT_u = 1 << 4,
+       };
 
        path = getenv("TMPDIR");
        if (!path || path[0] == '\0')
                path = "/tmp";
 
-       /* -q and -t are ignored */
        opt_complementary = "?1"; /* 1 argument max */
-       opts = getopt32(argv, "dqtp:", &path);
+       opts = getopt32(argv, "dqtp:u", &path);
 
-       chp = argv[optind] ? argv[optind] : xstrdup("tmp.XXXXXX");
-       if (chp[0] != '/' || (opts & 8))
+       chp = argv[optind];
+       if (!chp) {
+               /* GNU coreutils 8.4:
+                * bare "mktemp" -> "mktemp -t tmp.XXXXXX"
+                */
+               chp = xstrdup("tmp.XXXXXX");
+               opts |= OPT_t;
+       }
+#if 0
+       /* Don't allow directory separator in template */
+       if ((opts & OPT_t) && bb_basename(chp) != chp) {
+               errno = EINVAL;
+               goto error;
+       }
+#endif
+       if (opts & (OPT_t|OPT_p))
                chp = concat_path_file(path, chp);
 
-       if (opts & 1) { /* -d */
+       if (opts & OPT_u) {
+               chp = mktemp(chp);
+               if (chp[0] == '\0')
+                       goto error;
+       } else if (opts & OPT_d) {
                if (mkdtemp(chp) == NULL)
-                       return EXIT_FAILURE;
+                       goto error;
        } else {
                if (mkstemp(chp) < 0)
-                       return EXIT_FAILURE;
+                       goto error;
        }
-
        puts(chp);
-
        return EXIT_SUCCESS;
+ error:
+       if (opts & OPT_q)
+               return EXIT_FAILURE;
+       /* don't use chp as it gets mangled in case of error */
+       bb_perror_nomsg_and_die();
 }
index 6adefac..2c7444f 100644 (file)
@@ -4,8 +4,12 @@
  *
  * Copyright (C) 2003 by Rob Landley <rob@landley.net>, Joey Hess
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define pipe_progress_trivial_usage NOUSAGE_STR
+//usage:#define pipe_progress_full_usage ""
+
 #include "libbb.h"
 
 #define PIPE_PROGRESS_SIZE 4096
index ba05897..527fae2 100644 (file)
@@ -4,60 +4,81 @@
  *
  * Copyright (C) 2007 Bernhard Reutner-Fischer
  *
- * Based on a older version that was in busybox which was 1k big..
+ * Based on a older version that was in busybox which was 1k big.
  *   Copyright (C) 2001 by Emanuele Aina <emanuele.aina@tiscali.it>
  *
  * Based on the Debian run-parts program, version 1.15
  *   Copyright (C) 1996 Jeff Noxon <jeff@router.patch.net>,
  *   Copyright (C) 1996-1999 Guy Maor <maor@debian.org>
  *
- *
- * Licensed under GPL v2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* This is my first attempt to write a program in C (well, this is my first
  * attempt to write a program! :-) . */
 
 /* This piece of code is heavily based on the original version of run-parts,
- * taken from debian-utils. I've only removed the long options and the
+ * taken from debian-utils. I've only removed the long options and the
  * report mode. As the original run-parts support only long options, I've
  * broken compatibility because the BusyBox policy doesn't allow them.
- * The supported options are:
- * -t           test. Print the name of the files to be executed, without
- *              execute them.
- * -a ARG       argument. Pass ARG as an argument the program executed. It can
- *              be repeated to pass multiple arguments.
- * -u MASK      umask. Set the umask of the program executed to MASK.
  */
 
+//usage:#define run_parts_trivial_usage
+//usage:       "[-a ARG]... [-u UMASK] "
+//usage:       IF_FEATURE_RUN_PARTS_LONG_OPTIONS("[--reverse] [--test] [--exit-on-error] "IF_FEATURE_RUN_PARTS_FANCY("[--list] "))
+//usage:       "DIRECTORY"
+//usage:#define run_parts_full_usage "\n\n"
+//usage:       "Run a bunch of scripts in DIRECTORY\n"
+//usage:     "\n       -a ARG          Pass ARG as argument to scripts"
+//usage:     "\n       -u UMASK        Set UMASK before running scripts"
+//usage:       IF_FEATURE_RUN_PARTS_LONG_OPTIONS(
+//usage:     "\n       --reverse       Reverse execution order"
+//usage:     "\n       --test          Dry run"
+//usage:     "\n       --exit-on-error Exit if a script exits with non-zero"
+//usage:       IF_FEATURE_RUN_PARTS_FANCY(
+//usage:     "\n       --list          Print names of matching files even if they are not executable"
+//usage:       )
+//usage:       )
+//usage:
+//usage:#define run_parts_example_usage
+//usage:       "$ run-parts -a start /etc/init.d\n"
+//usage:       "$ run-parts -a stop=now /etc/init.d\n\n"
+//usage:       "Let's assume you have a script foo/dosomething:\n"
+//usage:       "#!/bin/sh\n"
+//usage:       "for i in $*; do eval $i; done; unset i\n"
+//usage:       "case \"$1\" in\n"
+//usage:       "start*) echo starting something;;\n"
+//usage:       "stop*) set -x; shutdown -h $stop;;\n"
+//usage:       "esac\n\n"
+//usage:       "Running this yields:\n"
+//usage:       "$run-parts -a stop=+4m foo/\n"
+//usage:       "+ shutdown -h +4m"
+
 #include "libbb.h"
 
 struct globals {
        char **names;
        int    cur;
-       char  *cmd[1];
+       char  *cmd[2 /* using 1 provokes compiler warning */];
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
 #define names (G.names)
 #define cur   (G.cur  )
 #define cmd   (G.cmd  )
+#define INIT_G() do { } while (0)
 
 enum { NUM_CMD = (COMMON_BUFSIZE - sizeof(G)) / sizeof(cmd[0]) - 1 };
 
 enum {
-       OPT_r = (1 << 0),
-       OPT_a = (1 << 1),
-       OPT_u = (1 << 2),
-       OPT_t = (1 << 3),
-       OPT_l = (1 << 4) * ENABLE_FEATURE_RUN_PARTS_FANCY,
+       OPT_a = (1 << 0),
+       OPT_u = (1 << 1),
+       OPT_r = (1 << 2) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS,
+       OPT_t = (1 << 3) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS,
+       OPT_e = (1 << 4) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS,
+       OPT_l = (1 << 5) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS
+                       * ENABLE_FEATURE_RUN_PARTS_FANCY,
 };
 
-#if ENABLE_FEATURE_RUN_PARTS_FANCY
-#define list_mode (option_mask32 & OPT_l)
-#else
-#define list_mode 0
-#endif
-
 /* Is this a valid filename (upper/lower alpha, digits,
  * underscores, and hyphens only?)
  */
@@ -85,7 +106,7 @@ static int FAST_FUNC act(const char *file, struct stat *statbuf, void *args UNUS
        if (depth == 2
         && (  !(statbuf->st_mode & (S_IFREG | S_IFLNK))
            || invalid_name(file)
-           || (!list_mode && access(file, X_OK) != 0))
+           || (!(option_mask32 & OPT_l) && access(file, X_OK) != 0))
        ) {
                return SKIP;
        }
@@ -101,11 +122,12 @@ static int FAST_FUNC act(const char *file, struct stat *statbuf, void *args UNUS
 static const char runparts_longopts[] ALIGN1 =
        "arg\0"     Required_argument "a"
        "umask\0"   Required_argument "u"
-       "test\0"    No_argument       "t"
-#if ENABLE_FEATURE_RUN_PARTS_FANCY
-       "list\0"    No_argument       "l"
-       "reverse\0" No_argument       "r"
 //TODO: "verbose\0" No_argument       "v"
+       "reverse\0" No_argument       "\xf0"
+       "test\0"    No_argument       "\xf1"
+       "exit-on-error\0" No_argument "\xf2"
+#if ENABLE_FEATURE_RUN_PARTS_FANCY
+       "list\0"    No_argument       "\xf3"
 #endif
        ;
 #endif
@@ -118,12 +140,14 @@ int run_parts_main(int argc UNUSED_PARAM, char **argv)
        unsigned n;
        int ret;
 
+       INIT_G();
+
 #if ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS
        applet_long_options = runparts_longopts;
 #endif
        /* We require exactly one argument: the directory name */
        opt_complementary = "=1:a::";
-       getopt32(argv, "ra:u:t"IF_FEATURE_RUN_PARTS_FANCY("l"), &arg_list, &umask_p);
+       getopt32(argv, "a:u:", &arg_list, &umask_p);
 
        umask(xstrtou_range(umask_p, 8, 0, 07777));
 
@@ -166,6 +190,9 @@ int run_parts_main(int argc UNUSED_PARAM, char **argv)
                        bb_perror_msg("can't execute '%s'", name);
                else /* ret > 0 */
                        bb_error_msg("%s exited with code %d", name, ret & 0xff);
+
+               if (option_mask32 & OPT_e)
+                       xfunc_die();
        }
 
        return n;
index 665f38f..7dadc3c 100644 (file)
@@ -5,7 +5,7 @@
  * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
  * Adapted for busybox David Kimdon <dwhedon@gordian.com>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*
@@ -31,7 +31,8 @@ Options controlling process matching
 [TODO: can PROCESS_NAME be a full pathname? Should we require full match then
 with /proc/$PID/exe or argv[0] (comm can't be matched, it never contains path)]
         -x,--exec EXECUTABLE    Look for processes that were started with this
-                                command in /proc/$PID/cmdline.
+                                command in /proc/$PID/exe and /proc/$PID/cmdline
+                                (/proc/$PID/cmdline is a bbox extension)
                                 Unlike -n, we match against the full path:
                                 "ntpd" != "./ntpd" != "/path/to/ntpd"
         -p,--pidfile PID_FILE   Look for processes with PID from this file
@@ -56,6 +57,69 @@ Misc options:
         -v,--verbose            Verbose
 */
 
+//usage:#define start_stop_daemon_trivial_usage
+//usage:       "[OPTIONS] [-S|-K] ... [-- ARGS...]"
+//usage:#define start_stop_daemon_full_usage "\n\n"
+//usage:       "Search for matching processes, and then\n"
+//usage:       "-K: stop all matching processes.\n"
+//usage:       "-S: start a process unless a matching process is found.\n"
+//usage:       IF_FEATURE_START_STOP_DAEMON_LONG_OPTIONS(
+//usage:     "\nProcess matching:"
+//usage:     "\n       -u,--user USERNAME|UID  Match only this user's processes"
+//usage:     "\n       -n,--name NAME          Match processes with NAME"
+//usage:     "\n                               in comm field in /proc/PID/stat"
+//usage:     "\n       -x,--exec EXECUTABLE    Match processes with this command"
+//usage:     "\n                               in /proc/PID/{exe,cmdline}"
+//usage:     "\n       -p,--pidfile FILE       Match a process with PID from the file"
+//usage:     "\n       All specified conditions must match"
+//usage:     "\n-S only:"
+//usage:     "\n       -x,--exec EXECUTABLE    Program to run"
+//usage:     "\n       -a,--startas NAME       Zeroth argument"
+//usage:     "\n       -b,--background         Background"
+//usage:       IF_FEATURE_START_STOP_DAEMON_FANCY(
+//usage:     "\n       -N,--nicelevel N        Change nice level"
+//usage:       )
+//usage:     "\n       -c,--chuid USER[:[GRP]] Change to user/group"
+//usage:     "\n       -m,--make-pidfile       Write PID to the pidfile specified by -p"
+//usage:     "\n-K only:"
+//usage:     "\n       -s,--signal SIG         Signal to send"
+//usage:     "\n       -t,--test               Match only, exit with 0 if a process is found"
+//usage:     "\nOther:"
+//usage:       IF_FEATURE_START_STOP_DAEMON_FANCY(
+//usage:     "\n       -o,--oknodo             Exit with status 0 if nothing is done"
+//usage:     "\n       -v,--verbose            Verbose"
+//usage:       )
+//usage:     "\n       -q,--quiet              Quiet"
+//usage:       )
+//usage:       IF_NOT_FEATURE_START_STOP_DAEMON_LONG_OPTIONS(
+//usage:     "\nProcess matching:"
+//usage:     "\n       -u USERNAME|UID Match only this user's processes"
+//usage:     "\n       -n NAME         Match processes with NAME"
+//usage:     "\n                       in comm field in /proc/PID/stat"
+//usage:     "\n       -x EXECUTABLE   Match processes with this command"
+//usage:     "\n                       command in /proc/PID/cmdline"
+//usage:     "\n       -p FILE         Match a process with PID from the file"
+//usage:     "\n       All specified conditions must match"
+//usage:     "\n-S only:"
+//usage:     "\n       -x EXECUTABLE   Program to run"
+//usage:     "\n       -a NAME         Zeroth argument"
+//usage:     "\n       -b              Background"
+//usage:       IF_FEATURE_START_STOP_DAEMON_FANCY(
+//usage:     "\n       -N N            Change nice level"
+//usage:       )
+//usage:     "\n       -c USER[:[GRP]] Change to user/group"
+//usage:     "\n       -m              Write PID to the pidfile specified by -p"
+//usage:     "\n-K only:"
+//usage:     "\n       -s SIG          Signal to send"
+//usage:     "\n       -t              Match only, exit with 0 if a process is found"
+//usage:     "\nOther:"
+//usage:       IF_FEATURE_START_STOP_DAEMON_FANCY(
+//usage:     "\n       -o              Exit with status 0 if nothing is done"
+//usage:     "\n       -v              Verbose"
+//usage:       )
+//usage:     "\n       -q              Quiet"
+//usage:       )
+
 #include <sys/resource.h>
 
 /* Override ENABLE_FEATURE_PIDFILE */
@@ -135,8 +199,18 @@ static int pid_is_exec(pid_t pid)
 {
        ssize_t bytes;
        char buf[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
+       char *procname, *exelink;
+       int match;
+
+       procname = buf + sprintf(buf, "/proc/%u/exe", (unsigned)pid) - 3;
+
+       exelink = xmalloc_readlink(buf);
+       match = (exelink && strcmp(execname, exelink) == 0);
+       free(exelink);
+       if (match)
+               return match;
 
-       sprintf(buf, "/proc/%u/cmdline", (unsigned)pid);
+       strcpy(procname, "cmdline");
        bytes = open_read_close(buf, G.execname_cmpbuf, G.execname_sizeof);
        if (bytes > 0) {
                G.execname_cmpbuf[bytes] = '\0';
@@ -274,11 +348,17 @@ static int do_stop(void)
                goto ret;
        }
        for (p = G.found_procs; p; p = p->next) {
-               if (TEST || kill(p->pid, signal_nr) == 0) {
+               if (kill(p->pid, TEST ? 0 : signal_nr) == 0) {
                        killed++;
                } else {
-                       p->pid = 0;
                        bb_perror_msg("warning: killing process %u", (unsigned)p->pid);
+                       p->pid = 0;
+                       if (TEST) {
+                               /* Example: -K --test --pidfile PIDFILE detected
+                                * that PIDFILE's pid doesn't exist */
+                               killed = -1;
+                               goto ret;
+                       }
                }
        }
        if (!QUIET && killed) {
@@ -373,7 +453,7 @@ int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv)
 
 //     IF_FEATURE_START_STOP_DAEMON_FANCY(
 //             if (retry_arg)
-//                     retries = xatoi_u(retry_arg);
+//                     retries = xatoi_positive(retry_arg);
 //     )
        //argc -= optind;
        argv += optind;
@@ -405,7 +485,7 @@ int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv)
        *--argv = startas;
        if (opt & OPT_BACKGROUND) {
 #if BB_MMU
-               bb_daemonize(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS);
+               bb_daemonize(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS + DAEMON_DOUBLE_FORK);
                /* DAEMON_DEVNULL_STDIO is superfluous -
                 * it's always done by bb_daemonize() */
 #else
@@ -433,8 +513,16 @@ int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv)
        if (opt & OPT_c) {
                struct bb_uidgid_t ugid = { -1, -1 };
                parse_chown_usergroup_or_die(&ugid, chuid);
-               if (ugid.gid != (gid_t) -1) xsetgid(ugid.gid);
-               if (ugid.uid != (uid_t) -1) xsetuid(ugid.uid);
+               if (ugid.uid != (uid_t) -1) {
+                       struct passwd *pw = xgetpwuid(ugid.uid);
+                       if (ugid.gid != (gid_t) -1)
+                               pw->pw_gid = ugid.gid;
+                       /* initgroups, setgid, setuid: */
+                       change_identity(pw);
+               } else if (ugid.gid != (gid_t) -1) {
+                       xsetgid(ugid.gid);
+                       setgroups(1, &ugid.gid);
+               }
        }
 #if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
        if (opt & OPT_NICELEVEL) {
index 1558e5c..15fd598 100644 (file)
@@ -5,11 +5,20 @@
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  * Copyright (C) 2006 Gabriel Somlo <somlo at cmu.edu>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Based on which from debianutils
  */
 
+//usage:#define which_trivial_usage
+//usage:       "[COMMAND]..."
+//usage:#define which_full_usage "\n\n"
+//usage:       "Locate a COMMAND"
+//usage:
+//usage:#define which_example_usage
+//usage:       "$ which login\n"
+//usage:       "/bin/login\n"
+
 #include "libbb.h"
 
 int which_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
index 9d1b7c2..3d1c5bd 100644 (file)
@@ -1,4 +1,5 @@
-/BusyBox.1
+/busybox.1
 /BusyBox.html
+/busybox.net
 /BusyBox.txt
 /busybox.pod
index 5ed9379..c346c73 100644 (file)
@@ -252,5 +252,34 @@ Tito Ragusa <farmatito@tiscali.it>
 
     devfsd and size optimizations in strings, openvt and deallocvt.
 
-=cut
+=for html <br>
+
+Paul Fox <pgf@foxharp.boston.ma.us>
+
+    vi editing mode for ash, various other patches/fixes
+
+=for html <br>
+
+Roberto A. Foglietta <me@roberto.foglietta.name>
+
+    port: dnsd
+
+=for html <br>
+
+Bernhard Reutner-Fischer <rep.dot.nop@gmail.com>
 
+    misc
+
+=for html <br>
+
+Mike Frysinger <vapier@gentoo.org>
+
+    initial e2fsprogs, printenv, setarch, sum, misc
+
+=for html <br>
+
+Jie Zhang <jie.zhang@analog.com>
+
+    fixed two bugs in msh and hush (exitcode of killed processes)
+
+=cut
index 2a99636..85a173e 100644 (file)
@@ -80,4 +80,3 @@ been enabled, more detailed usage information will also be available.
 =head1 COMMANDS
 
 Currently available applets include:
-
index 5779d62..4f8faae 100644 (file)
@@ -43,4 +43,4 @@ CGI - Common Gateway Interface
 </p><address><a href="http://hoohoo.ncsa.uiuc.edu/cgi/mailtocgi.html">cgi@ncsa.uiuc.edu</a></address>
 
 
-</body></html>
\ No newline at end of file
+</body></html>
index 924026b..b83c750 100644 (file)
@@ -146,4 +146,4 @@ interface specification</a> <p>
 CGI - Common Gateway Interface
 </p><address><a href="http://hoohoo.ncsa.uiuc.edu/cgi/mailtocgi.html">cgi@ncsa.uiuc.edu</a></address>
 
-</body></html>
\ No newline at end of file
+</body></html>
index 679306a..7ee5fe6 100644 (file)
@@ -30,4 +30,4 @@ interface specification</a> <p>
 CGI - Common Gateway Interface
 </p><address><a href="http://hoohoo.ncsa.uiuc.edu/cgi/mailtocgi.html">cgi@ncsa.uiuc.edu</a></address>
 
-</body></html>
\ No newline at end of file
+</body></html>
index ea73ce3..0be016b 100644 (file)
@@ -26,4 +26,4 @@ the following is a hotlink to graphic detail.</p><p>
 
 CGI - Common Gateway Interface
 </p><address><a href="http://hoohoo.ncsa.uiuc.edu/cgi/mailtocgi.html">cgi@ncsa.uiuc.edu</a></address>
-</body></html>
\ No newline at end of file
+</body></html>
index 2203ee5..5266985 100644 (file)
@@ -123,4 +123,4 @@ interface specification</a> <p>
 
 CGI - Common Gateway Interface
 </p><address><a href="http://hoohoo.ncsa.uiuc.edu/cgi/mailtocgi.html">cgi@ncsa.uiuc.edu</a></address>
-</body></html>
\ No newline at end of file
+</body></html>
index d06e4a2..e3289fd 100644 (file)
@@ -229,8 +229,11 @@ Here are some guidelines on how to submit a patch to Busybox.
 Making A Patch
 ~~~~~~~~~~~~~~
 
-If you've got anonymous CVS access set up, making a patch is simple. Just make
-sure you're in the busybox/ directory and type 'cvs diff -bwu > mychanges.patch'.
+If you've got anonymous Git access set up, making a patch is simple. Just make
+sure you're in the busybox/ directory and type:
+
+       git diff -b -w > mychanges.patch
+
 You can send the resulting .patch file to the mailing list with a description
 of what it does. (But not before you test it! See the next section for some
 guidelines.) It is preferred that patches be sent as attachments, but it is
@@ -238,8 +241,12 @@ not required.
 
 Also, feel free to help test other people's patches and reply to them with
 comments. You can apply a patch by saving it into your busybox/ directory and
-typing 'patch < mychanges.patch'. Then you can recompile, see if it runs, test
-if it works as advertised, and post your findings to the mailing list.
+typing:
+
+       patch -p1 < mychanges.patch
+
+Then you can recompile, see if it runs, test if it works as advertised, and
+post your findings to the mailing list.
 
 NOTE: Please do not include extraneous or irrelevant changes in your patches.
 Please do not try to "bundle" two patches together into one. Make single,
@@ -252,7 +259,7 @@ Testing Guidelines
 ~~~~~~~~~~~~~~~~~~
 
 It's considered good form to test your new feature before you submit a patch
-to the mailing list, and especially before you commit a change to CVS. Here
+to the mailing list, and especially before you push a change to Git. Here
 are some guidelines on how to test your changes.
 
  - Always test Busybox applets against GNU counterparts and make sure the
@@ -348,7 +355,7 @@ responses from queries to applet maintainer or positive responses from folks
 on the mailing list.
 
 We've made strident efforts to put a useful "collaboration" infrastructure in
-place in the form of mailing lists, the bug tracking system, and CVS. Please
+place in the form of mailing lists, the bug tracking system, and Git. Please
 use these resources.
 
 
@@ -373,39 +380,43 @@ opposite effect.
 
 
 
-Committing Changes to CVS
--------------------------
+Pushing Changes to Git
+----------------------
 
 If you submit several patches that demonstrate that you are a skilled and wise
-coder, you may be invited to become a committer, thus enabling you to commit
-changes directly to CVS. This is nice because you don't have to wait for
-someone else to commit your change for you, you can just do it yourself.
+coder, you may be invited to become a committer, thus enabling you to push
+changes directly to Git. This is nice because you don't have to wait for
+someone else to push your change for you, you can just do it yourself.
 
 But note that this is a privilege that comes with some responsibilities. You
-should test your changes before you commit them. You should also talk to an
+should test your changes before you push them. You should also talk to an
 applet maintainer before you make any kind of sweeping changes to somebody
 else's code. Big changes should still go to the mailing list first. Remember,
 being wise, polite, and discreet is more important than being clever.
 
+For more information on Git push access, see:
+
+       http://busybox.net/developer.html
 
-When To Commit
-~~~~~~~~~~~~~~
 
-Generally, you should feel free to commit a change if:
+When To Push
+~~~~~~~~~~~~
+
+Generally, you should feel free to push a change if:
 
  - Your changes are small and don't touch many files
  - You are fixing a bug
  - Somebody has told you that it's okay
  - It's obviously the Right Thing
 
-The more of the above are true, the better it is to just commit a change
-directly to CVS.
+The more of the above are true, the better it is to just push a change
+directly to Git.
 
 
-When Not To Commit
-~~~~~~~~~~~~~~~~~~
+When Not To Push
+~~~~~~~~~~~~~~~~
 
-Even if you have commit rights, you should probably still post a patch to the
+Even if you have push access, you should probably still post a patch to the
 mailing list if:
 
  - Your changes are broad and touch many different files
@@ -414,7 +425,7 @@ mailing list if:
  - You are not the maintainer and your changes make the maintainer cringe
 
 The more of the above are true, the better it is to post a patch to the
-mailing list instead of committing.
+mailing list instead of pushing.
 
 
 
@@ -426,5 +437,3 @@ you're having difficulty following some of the steps outlined in this
 document don't worry, the folks on the Busybox mailing list are a fairly
 good-natured bunch and will work with you to help get your patches into shape
 or help you make contributions.
-
-
index 8f466cd..3cb2dd2 100644 (file)
@@ -9,6 +9,8 @@
 
 <p>Before looking at the Linux implementation, first a general Unix
 description of threads, processes, process groups and sessions.
+</p><p>
+(See also <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html">General Terminal Interface</a>)
 </p><p>A session contains a number of process groups, and a process group
 contains a number of processes, and a process contains a number
 of threads.
@@ -277,6 +279,7 @@ and inspect it by
 Again, if TOSTOP is set but the background process ignores or blocks
 the SIGTTOU signal, or if its process group is orphaned (see below),
 then the write() returns an EIO error, and no signal is sent.
+[vda: correction. SUS says that if SIGTTOU is blocked/ignored, write succeeds. ]
 <p>
 </p><h3>Orphaned process groups</h3>
 
index 8008e45..8ab4e51 100644 (file)
@@ -10,27 +10,27 @@ for that.
 We are doomed to have problems with ifup/ifdown. Just look as this code:
 
 static const struct dhcp_client_t ext_dhcp_clients[] = {
-       { "dhcpcd", "<up cmd>", "<down cmd>" },
-       { "dhclient", ........ },
-       { "pump", ........ },
-       { "udhcpc", ........ },
+       { "dhcpcd", "<up cmd>", "<down cmd>" },
+       { "dhclient", ........ },
+       { "pump", ........ },
+       { "udhcpc", ........ },
 };
 
 static int dhcp_down(struct interface_defn_t *ifd, execfn *exec)
 {
 #if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
-       int i ;
-       for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
-               if (exists_execable(ext_dhcp_clients[i].name))
-                       return execute(ext_dhcp_clients[i].stopcmd, ifd, exec);
-       }
-       bb_error_msg("no dhcp clients found, using static interface shutdown");
-       return static_down(ifd, exec);
+       int i ;
+       for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
+               if (exists_execable(ext_dhcp_clients[i].name))
+                       return execute(ext_dhcp_clients[i].stopcmd, ifd, exec);
+       }
+       bb_error_msg("no dhcp clients found, using static interface shutdown");
+       return static_down(ifd, exec);
 #elif ENABLE_UDHCPC
-       return execute("kill "
-                      "`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
+       return execute("kill "
+                       "`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
 #else
-       return 0; /* no dhcp support */
+       return 0; /* no dhcp support */
 #endif
 }
 
index 01c0d3c..9fc7996 100644 (file)
@@ -59,7 +59,7 @@ wait
                Example 1
 
 One example how to reduce global data usage is in
-archival/libunarchive/decompress_unzip.c:
+archival/libarchive/decompress_unzip.c:
 
 /* This is somewhat complex-looking arrangement, but it allows
  * to place decompressor state either in bss or in
@@ -138,13 +138,6 @@ less readable, use #defines:
 #define sector (G.sector)
 
 
-               Word of caution
-
-If applet doesn't use much of global data, converting it to use
-one of above methods is not worth the resulting code obfuscation.
-If you have less than ~300 bytes of global data - don't bother.
-
-
                Finding non-shared duplicated strings
 
 strings busybox | sort | uniq -c | sort -nr
@@ -224,6 +217,14 @@ Result (non-static busybox built against glibc):
 
                Keeping code small
 
+Use scripts/bloat-o-meter to check whether introduced changes
+didn't generate unnecessary bloat. This script needs unstripped binaries
+to generate a detailed report. To automate this, just use
+"make bloatcheck". It requires busybox_old binary to be present,
+use "make baseline" to generate it from unmodified source, or
+copy busybox_unstripped to busybox_old before modifying sources
+and rebuilding.
+
 Set CONFIG_EXTRA_CFLAGS="-fno-inline-functions-called-once",
 produce "make bloatcheck", see the biggest auto-inlined functions.
 Now, set CONFIG_EXTRA_CFLAGS back to "", but add NOINLINE
index 2d03bd8..b24025f 100644 (file)
@@ -51,19 +51,25 @@ device nodes if your system needs something more than the default root/root
 660 permissions.
 
 The file has the format:
-    <device regex>       <uid>:<gid> <permissions>
- or @<maj[,min1[-min2]]> <uid>:<gid> <permissions>
+       [-][envmatch]<device regex>     <uid>:<gid> <permissions>
+or
+       [envmatch]@<maj[,min1[-min2]]>  <uid>:<gid> <permissions>
+or
+       $envvar=<regex>         <uid>:<gid> <permissions>
 
 For example:
-    hd[a-z][0-9]* 0:3 660
+       hd[a-z][0-9]* 0:3 660
 
 The config file parsing stops at the first matching line.  If no line is
 matched, then the default of 0:0 660 is used.  To set your own default, simply
 create your own total match like so:
+
        .* 1:1 777
 
 You can rename/move device nodes by using the next optional field.
+
        <device regex> <uid>:<gid> <permissions> [=path]
+
 So if you want to place the device node into a subdirectory, make sure the path
 has a trailing /.  If you want to rename the device node, just place the name.
        hda 0:3 660 =drives/
index 0646e72..6a8054d 100644 (file)
@@ -8,7 +8,7 @@ Matt Kraai - initial writeup
 Mark Whitley - the remix
 Thomas Lundquist - Trying to keep it updated.
 
-When doing this you should consider using the latest svn trunk.
+When doing this you should consider using the latest git HEAD.
 This is a good thing if you plan to getting it committed into mainline.
 
 Initial Write
@@ -19,8 +19,7 @@ such as who you stole the code from and so forth. Also include the mini-GPL
 boilerplate. Be sure to name the main function <applet>_main instead of main.
 And be sure to put it in <applet>.c. Usage does not have to be taken care of by
 your applet.
-Make sure to #include "libbb.h" as the first include file in your applet so
-the bb_config.h and appropriate platform specific files are included properly.
+Make sure to #include "libbb.h" as the first include file in your applet.
 
 For a new applet mu, here is the code that would go in mu.c:
 
@@ -36,7 +35,7 @@ For a new applet mu, here is the code that would go in mu.c:
  *
  * Copyright (C) [YEAR] by [YOUR NAME] <YOUR EMAIL>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -99,14 +98,14 @@ int function(char *a)
 ----end example code------
 
 Add <function_name>.o in the right alphabetically sorted place
-in libbb/Kbuild. You should look at the conditional part of
-libbb/Kbuild aswell.
+in libbb/Kbuild.src. You should look at the conditional part of
+libbb/Kbuild.src as well.
 
 You should also try to find a suitable place in include/libbb.h for
 the function declaration. If not, add it somewhere anyway, with or without
 ifdefs to include or not.
 
-You can look at libbb/Config.in and try to find out if the function is
+You can look at libbb/Config.src and try to find out if the function is
 tunable and add it there if it is.
 
 
@@ -118,11 +117,11 @@ Find the appropriate directory for your new applet.
 Make sure you find the appropriate places in the files, the applets are
 sorted alphabetically.
 
-Add the applet to Kbuild in the chosen directory:
+Add the applet to Kbuild.src in the chosen directory:
 
 lib-$(CONFIG_MU)               += mu.o
 
-Add the applet to Config.in in the chosen directory:
+Add the applet to Config.src in the chosen directory:
 
 config MU
        bool "MU"
@@ -134,7 +133,7 @@ config MU
 Usage String(s)
 ---------------
 
-Next, add usage information for you applet to include/usage.h.
+Next, add usage information for you applet to include/usage.src.h.
 This should look like the following:
 
        #define mu_trivial_usage \
@@ -149,32 +148,33 @@ This should look like the following:
 If your program supports flags, the flags should be mentioned on the first
 line (-[abcde]) and a detailed description of each flag should go in the
 mu_full_usage section, one flag per line. (Numerous examples of this
-currently exist in usage.h.)
+currently exist in usage.src.h.)
 
 
 Header Files
 ------------
 
-Next, add an entry to include/applets.h.  Be *sure* to keep the list
+Next, add an entry to include/applets.src.h.  Be *sure* to keep the list
 in alphabetical order, or else it will break the binary-search lookup
 algorithm in busybox.c and the Gods of BusyBox smite you. Yea, verily:
 
-Be sure to read the top of applets.h before adding your applet.
+Be sure to read the top of applets.src.h before adding your applet.
 
        /* all programs above here are alphabetically "less than" 'mu' */
-       IF_MU(APPLET(mu, _BB_DIR_USR_BIN, _BB_SUID_DROP))
+       IF_MU(APPLET(mu, BB_DIR_USR_BIN, BB_SUID_DROP))
        /* all programs below here are alphabetically "greater than" 'mu' */
 
 
 The Grand Announcement
 ----------------------
 
-Then create a diff by adding the new files with svn (remember your libbb files)
-       svn add <where you put it>/mu.c
+Then create a diff by adding the new files to git (remember your libbb files)
+       git add <where you put it>/mu.c
 eventually also:
-       svn add libbb/function.c
+       git add libbb/function.c
 then
-       svn diff
+       git commit
+       git format-patch HEAD^
 and send it to the mailing list:
        busybox@busybox.net
        http://busybox.net/mailman/listinfo/busybox
index 06c789a..c58f5a8 100644 (file)
@@ -44,9 +44,11 @@ NOEXEC trick is disabled for NOMMU build.
        NOFORK
 
 NOFORK applet should work correctly if another applet simply runs
-<applet>_main(argc,argv) and then continues with its business (xargs,
-find, shells can do it). This poses much more serious limitations
-on what applet can/cannot do:
+<applet>_main(argc,argv) and then continues with its business.
+xargs, find, shells do it (grep for "spawn_and_wait" and
+"run_nofork_applet" to find more users).
+
+This poses much more serious limitations on what applet can do:
 
 * all NOEXEC limitations apply.
 * do not ever exit() or exec().
@@ -56,7 +58,7 @@ on what applet can/cannot do:
     is taken from xfunc_error_retval.
   - fflush_stdout_and_exit(n) is ok to use.
 * do not use shared global data, or save/restore shared global data
-  prior to returning. (e.g. bb_common_bufsiz1 is off-limits).
+  (e.g. bb_common_bufsiz1) prior to returning.
   - getopt32() is ok to use. You do not need to save/restore option_mask32,
     it is already done by core code.
 * if you allocate memory, you can use xmalloc() only on the very first
@@ -77,3 +79,20 @@ script loops. Applets which mess with signal handlers, termios etc
 are probably not worth the effort.
 
 Any NOFORK applet is also a NOEXEC applet.
+
+
+       Relevant CONFIG options
+
+FEATURE_PREFER_APPLETS
+  BB_EXECVP(cmd, argv) will try to exec /proc/self/exe
+    if command's name matches some applet name
+  applet tables will contain NOFORK/NOEXEC bits
+  spawn_and_wait(argv) will do NOFORK/NOEXEC tricks
+
+FEATURE_SH_STANDALONE (needs FEATURE_PREFER_APPLETS=y)
+  shells will try to exec /proc/self/exe if command's name matches
+    some applet name
+  shells will do NOEXEC trick on NOEXEC applets
+
+FEATURE_SH_NOFORK (needs FEATURE_PREFER_APPLETS=y)
+  shells will do NOFORK trick on NOFORK applets
index d9fa116..5b616d7 100644 (file)
@@ -739,4 +739,3 @@ xargs Busybox specific options:
 
 zcat POSIX options: None
 zcat Busybox specific options: None
-
diff --git a/docs/smallint.txt b/docs/smallint.txt
new file mode 100644 (file)
index 0000000..b57dfd7
--- /dev/null
@@ -0,0 +1,39 @@
+        smalluint i = index_in_str_array(params, name) + 1;
+        if (i == 0)
+                return 0;
+        if (!(i == 4 || i == 5))
+                i |= 0x80;
+
+        return i;
+
+I think that this optimization is wrong.
+index_in_str_array returns int. At best, compiler will use it as-is.
+At worst, compiler will try to make sure that it is properly cast
+into a byte, which probably results in "n = n & 0xff" on many architectures.
+
+You save nothing on space here because i is not stored on-stack,
+gcc will keep it in register. And even if it *is* stored,
+it is *stack* storage, which is cheap (unlike data/bss).
+
+small[u]ints are useful _mostly_ for:
+
+(a) flag variables
+    (a1) global flag variables - make data/bss smaller
+    (a2) local flag variables - "a = 5", "a |= 0x40" are smaller
+         for bytes than for full integers.
+            Example:
+            on i386, there is no widening constant store instruction
+            for some types of address modes, thus
+            movl $0x0,(%eax) is "c7 00 00 00 00 00"
+            movb $0x0,(%eax) is "c6 00 00"
+(b) small integer structure members, when you have many such
+    structures allocated,
+    or when these are global objects of this structure type
+
+small[u]ints are *NOT* useful for:
+
+(a) function parameters and return values -
+    they are pushed on-stack or stored in registers, bytes here are *harder*
+    to deal with than ints
+(b) "computational" variables - "a++", "a = b*3 + 7" may take more code to do
+    on bytes than on ints on some architectires.
index fdf6cfe..10ed893 100644 (file)
@@ -679,11 +679,10 @@ line in the midst of your #includes, if you need to parse long options:
 
 Then have long options defined:
 
-       static const struct option <applet>_long_options[] = {
-               { "list",    0, NULL, 't' },
-               { "extract", 0, NULL, 'x' },
-               { NULL, 0, NULL, 0 }
-       };
+       static const char <applet>_longopts[] ALIGN1 =
+               "list\0"    No_argument "t"
+               "extract\0" No_argument "x"
+       ;
 
 And a code block similar to the following near the top of your applet_main()
 routine:
@@ -691,7 +690,7 @@ routine:
        char *str_b;
 
        opt_complementary = "cryptic_string";
-       applet_long_options = <applet>_long_options; /* if you have them */
+       applet_long_options = <applet>_longopts; /* if you have them */
        opt = getopt32(argc, argv, "ab:c", &str_b);
        if (opt & 1) {
                handle_option_a();
diff --git a/docs/syslog.conf.txt b/docs/syslog.conf.txt
new file mode 100644 (file)
index 0000000..6d9c4a1
--- /dev/null
@@ -0,0 +1,28 @@
+If syslogd applet compiled with FEATURE_SYSLOGD_CFG=y, then it supports restricted syslog.conf.
+The config resembles rsyslog.conf in RULES part:
+
+LINE = DELIM [RULE | COMMENT]
+COMMENT = #.*
+DELIM = SPACE TAB
+RULE = SELECTOR [;SELECTOR]* DELIM* ACTION DELIM*
+SELECTOR = FACILITY [,FACILITY]* .[[!]=] PRIORITY
+FACILITY = * | kern | user ... (see syslog.h)
+PRIORITY = * | emerg | alert ... (see syslog.h)
+ACTION = FILE
+
+"mark" facility is NOT supported.
+"none" priority is supported.
+In FACILITY and PRIORITY "*" stands for "any".
+FILE is a regular file or tty device.
+
+Here is an example:
+
+#syslog.conf
+kern,user.*                                 /var/log/messages  #all messages of kern and user facilities
+kern.!err                                   /var/log/critical  #all messages of kern facility with priorities lower than err (warn, notice ...)
+*.*;auth,authpriv.none                      /var/log/noauth    #all messages except ones with auth and authpriv facilities
+kern,user.*;kern.!=notice;*.err;syslog.none /var/log/OMG       #some whicked rule just as an example =)
+*.*                                         /dev/null          #this prevents from logging to default log file (-O FILE or /var/log/messages)
+
+Even in the case of match with some rule another rules will be tried too.
+If there was no match with any of the rules, logging to default log file or shared memory will be performed.
diff --git a/docs/tcp.txt b/docs/tcp.txt
new file mode 100644 (file)
index 0000000..2000f31
--- /dev/null
@@ -0,0 +1,93 @@
+       Some less-widely known details of TCP connections.
+
+       Properly closing the connection.
+
+After this code sequence:
+
+    sock = socket(AF_INET, SOCK_STREAM, 0);
+    connect(sock, &remote, sizeof(remote));
+    write(sock, buffer, 1000000);
+
+a large block of data is only buffered by kernel, it can't be sent all at once.
+What will happen if we close the socket?
+
+"A host MAY implement a 'half-duplex' TCP close sequence, so that
+ an application that has called close() cannot continue to read
+ data from the connection. If such a host issues a close() call
+ while received data is still pending in TCP, or if new data is
+ received after close() is called, its TCP SHOULD send a RST
+ to show that data was lost."
+
+IOW: if we just close(sock) now, kernel can reset the TCP connection
+(send RST packet).
+
+This is problematic for two reasons: it discards some not-yet sent
+data, and it may be reported as error, not EOF, on peer's side.
+
+What can be done about it?
+
+Solution #1: block until sending is done:
+
+    /* When enabled, a close(2) or shutdown(2) will not return until
+     * all queued messages for the socket have been successfully sent
+     * or the linger timeout has been reached.
+     */
+    struct linger {
+       int l_onoff;    /* linger active */
+       int l_linger;   /* how many seconds to linger for */
+    } linger;
+    linger.l_onoff = 1;
+    linger.l_linger = SOME_NUM;
+    setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
+    close(sock);
+
+Solution #2: tell kernel that you are done sending.
+This makes kernel send FIN after all data is written:
+
+    shutdown(sock, SHUT_WR);
+    close(sock);
+
+However, experiments on Linux 3.9.4 show that kernel can return from
+shutdown() and from close() before all data is sent,
+and if peer sends any data to us after this, kernel still responds with
+RST before all our data is sent.
+
+In practice the protocol in use often does not allow peer to send
+such data to us, in which case this solution is acceptable.
+
+Solution #3: if you know that peer is going to close its end after it sees
+our FIN (as EOF), it might be a good idea to perform a read after shutdown().
+When read finishes with 0-sized result, we conclude that peer received all
+the data, saw EOF, and closed its end.
+
+However, this incurs small performance penalty (we run for a longer time)
+and requires safeguards (nonblocking reads, timeouts etc) against
+malicious peers which don't close the connection.
+
+Solutions #1 and #2 can be combined:
+
+    /* ...set up struct linger... then: */
+    setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
+    shutdown(sock, SHUT_WR);
+    /* At this point, kernel sent FIN packet, not RST, to the peer, */
+    /* even if there is buffered read data from the peer. */
+    close(sock);
+
+       Defeating Nagle.
+
+Method #1: manually control whether partial sends are allowed:
+
+This prevents partially filled packets being sent:
+
+    int state = 1;
+    setsockopt(fd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state));
+
+and this forces last, partially filled packet (if any) to be sent:
+
+    int state = 0;
+    setsockopt(fd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state));
+
+Method #2: make any write to immediately send data, even if it's partial:
+
+    int state = 1;
+    setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &state, sizeof(state));
index 32df24d..9c159ce 100644 (file)
@@ -29,7 +29,7 @@ But we also need to handle the following problematic moments:
        Editors (vi, ed)
 
 This case is a bit similar to "shell input", but unlike shell,
-editors may encounder many more unexpected unicode sequences
+editors may encounter many more unexpected unicode sequences
 (try to load a random binary file...), and they need to preserve
 them, unlike shell which can afford to drop bogus input.
 
index 62bc810..743e1e1 100644 (file)
@@ -33,6 +33,7 @@ config FSCK
 config LSATTR
        bool "lsattr"
        default y
+       select PLATFORM_LINUX
        help
          lsattr lists the file attributes on a second extended file system.
 
index 31c2712..b7a14c3 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
index ab52cb0..f1cc838 100644 (file)
  * 98/12/29    - Display version info only when -V specified (G M Sipe)
  */
 
+//usage:#define chattr_trivial_usage
+//usage:       "[-R] [-+=AacDdijsStTu] [-v VERSION] [FILE]..."
+//usage:#define chattr_full_usage "\n\n"
+//usage:       "Change file attributes on an ext2 fs\n"
+//usage:     "\nModifiers:"
+//usage:     "\n       -       Remove attributes"
+//usage:     "\n       +       Add attributes"
+//usage:     "\n       =       Set attributes"
+//usage:     "\nAttributes:"
+//usage:     "\n       A       Don't track atime"
+//usage:     "\n       a       Append mode only"
+//usage:     "\n       c       Enable compress"
+//usage:     "\n       D       Write dir contents synchronously"
+//usage:     "\n       d       Don't backup with dump"
+//usage:     "\n       i       Cannot be modified (immutable)"
+//usage:     "\n       j       Write all data to journal first"
+//usage:     "\n       s       Zero disk storage when deleted"
+//usage:     "\n       S       Write file contents synchronously"
+//usage:     "\n       t       Disable tail-merging of partial blocks with other files"
+//usage:     "\n       u       Allow file to be undeleted"
+//usage:     "\n       -R      Recurse"
+//usage:     "\n       -v      Set the file's version/generation number"
+
 #include "libbb.h"
 #include "e2fs_lib.h"
 
index f033a19..a6aec94 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * See README for additional information
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index 3905ee7..f2ae56f 100644 (file)
@@ -7,7 +7,7 @@
  */
 
 /* Constants and structures */
-#include "e2fs_defs.h"
+#include "bb_e2fs_defs.h"
 
 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 
index 7c449e3..d32f396 100644 (file)
@@ -20,7 +20,7 @@
  * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
  *      2001, 2002, 2003, 2004, 2005 by  Theodore Ts'o.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /* All filesystem specific hooks have been removed.
  * It doesn't guess filesystem types from on-disk format.
  */
 
+//usage:#define fsck_trivial_usage
+//usage:       "[-ANPRTV] [-C FD] [-t FSTYPE] [FS_OPTS] [BLOCKDEV]..."
+//usage:#define fsck_full_usage "\n\n"
+//usage:       "Check and repair filesystems\n"
+//usage:     "\n       -A      Walk /etc/fstab and check all filesystems"
+//usage:     "\n       -N      Don't execute, just show what would be done"
+//usage:     "\n       -P      With -A, check filesystems in parallel"
+//usage:     "\n       -R      With -A, skip the root filesystem"
+//usage:     "\n       -T      Don't show title on startup"
+//usage:     "\n       -V      Verbose"
+//usage:     "\n       -C n    Write status information to specified filedescriptor"
+//usage:     "\n       -t TYPE List of filesystem types to check"
+
 #include "libbb.h"
 
 /* "progress indicator" code is somewhat buggy and ext[23] specific.
@@ -303,7 +316,6 @@ static void load_fs_info(const char *filename)
 {
        FILE *fstab;
        struct mntent mte;
-       struct fs_info *fs;
 
        fstab = setmntent(filename, "r");
        if (!fstab) {
@@ -316,7 +328,7 @@ static void load_fs_info(const char *filename)
                //bb_info_msg("CREATE[%s][%s][%s][%s][%d]", mte.mnt_fsname, mte.mnt_dir,
                //      mte.mnt_type, mte.mnt_opts,
                //      mte.mnt_passno);
-               fs = create_fs_device(mte.mnt_fsname, mte.mnt_dir,
+               create_fs_device(mte.mnt_fsname, mte.mnt_dir,
                        mte.mnt_type, mte.mnt_opts,
                        mte.mnt_passno);
        }
@@ -466,7 +478,7 @@ static int wait_one(int flags)
                instance_list = inst->next;
        if (verbose > 1)
                printf("Finished with %s (exit status %d)\n",
-                      inst->device, status);
+                       inst->device, status);
        num_running--;
        free_instance(inst);
 
@@ -832,7 +844,7 @@ static int check_all(void)
                if (verbose > 1)
                        printf("--waiting-- (pass %d)\n", passno);
                status |= wait_many(pass_done ? FLAG_WAIT_ALL :
-                                   FLAG_WAIT_ATLEAST_ONE);
+                               FLAG_WAIT_ATLEAST_ONE);
                if (pass_done) {
                        if (verbose > 1)
                                puts("----------------------------------");
@@ -972,13 +984,13 @@ int fsck_main(int argc UNUSED_PARAM, char **argv)
                        case 'C':
                                progress = 1;
                                if (arg[++j]) { /* -Cn */
-                                       progress_fd = xatoi_u(&arg[j]);
+                                       progress_fd = xatoi_positive(&arg[j]);
                                        goto next_arg;
                                }
                                /* -C n */
                                if (!*++argv)
                                        bb_show_usage();
-                               progress_fd = xatoi_u(*argv);
+                               progress_fd = xatoi_positive(*argv);
                                goto next_arg;
 #endif
                        case 'V':
index 7d475a9..1312fe7 100644 (file)
  * 98/12/29    - Display version info only when -V specified (G M Sipe)
  */
 
+//usage:#define lsattr_trivial_usage
+//usage:       "[-Radlv] [FILE]..."
+//usage:#define lsattr_full_usage "\n\n"
+//usage:       "List file attributes on an ext2 fs\n"
+//usage:     "\n       -R      Recurse"
+//usage:     "\n       -a      Don't hide entries starting with ."
+//usage:     "\n       -d      List directory entries instead of contents"
+//usage:     "\n       -l      List long flag names"
+//usage:     "\n       -v      List the file's version/generation number"
+
 #include "libbb.h"
 #include "e2fs_lib.h"
 
index 07a09ca..fff1a0d 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
index bae77fe..02b4d24 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 NEEDED-$(CONFIG_E2FSCK) = y
 NEEDED-$(CONFIG_FSCK) = y
index d6b2b42..bbadc8e 100644 (file)
@@ -179,8 +179,4 @@ extern int blkid_set_tag(blkid_dev dev, const char *name,
 extern blkid_dev blkid_new_dev(void);
 extern void blkid_free_dev(blkid_dev dev);
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
index 941efa4..e1f6ba6 100644 (file)
@@ -96,9 +96,9 @@ blkid_loff_t blkid_get_dev_size(int fd)
 
 #ifdef BLKGETSIZE64
 #ifdef __linux__
-       if ((uname(&ut) == 0) &&
-           ((ut.release[0] == '2') && (ut.release[1] == '.') &&
-            (ut.release[2] < '6') && (ut.release[3] == '.')))
+       uname(&ut);
+       if ((ut.release[0] == '2') && (ut.release[1] == '.') &&
+            (ut.release[2] < '6') && (ut.release[3] == '.'))
                valid_blkgetsize64 = 0;
 #endif
        if (valid_blkgetsize64 &&
index d1d2914..251e499 100644 (file)
@@ -115,7 +115,7 @@ int main(int argc, char** argv)
                        argv[0], ret);
                exit(1);
        }
-       if ((ret = blkid_probe_all(cache) < 0))
+       if ((ret = blkid_probe_all(cache)) < 0)
                fprintf(stderr, "error probing devices\n");
 
        blkid_put_cache(cache);
index bb0cc91..260e49c 100644 (file)
@@ -153,7 +153,7 @@ extern int optind;
 void usage(char *prog)
 {
        fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask]\n", prog);
-       fprintf(stderr, "\tList all devices and exit\n", prog);
+       fprintf(stderr, "\tList all devices and exit\n");
        exit(1);
 }
 
@@ -176,7 +176,7 @@ int main(int argc, char **argv)
                case 'm':
                        blkid_debug_mask = strtoul (optarg, &tmp, 0);
                        if (*tmp) {
-                               fprintf(stderr, "Invalid debug mask: %d\n",
+                               fprintf(stderr, "Invalid debug mask: %s\n",
                                        optarg);
                                exit(1);
                        }
index 77bfc73..651193b 100644 (file)
@@ -575,8 +575,12 @@ blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev)
            printf("need to revalidate %s (time since last check %lu)\n",
                   dev->bid_name, diff));
 
-       if (((fd = open(dev->bid_name, O_RDONLY)) < 0) ||
-           (fstat(fd, &st) < 0)) {
+       fd = open(dev->bid_name, O_RDONLY);
+       if (fd < 0
+        || fstat(fd, &st) < 0
+       ) {
+               if (fd >= 0)
+                       close(fd);
                if (errno == ENXIO || errno == ENODEV || errno == ENOENT) {
                        blkid_free_dev(dev);
                        return NULL;
@@ -653,6 +657,7 @@ try_again:
 
        if (!dev->bid_type) {
                blkid_free_dev(dev);
+               close(fd);
                return NULL;
        }
 
index f795a5d..feeda51 100644 (file)
@@ -385,7 +385,7 @@ void blkid_read_cache(blkid_cache cache)
                        continue;
                end = strlen(buf) - 1;
                /* Continue reading next line if it ends with a backslash */
-               while (buf[end] == '\\' && end < sizeof(buf) - 2 &&
+               while (end < sizeof(buf) - 2 && buf[end] == '\\' &&
                       fgets(buf + end, sizeof(buf) - end, file)) {
                        end = strlen(buf) - 1;
                        lineno++;
index 8337b46..7424ede 100644 (file)
@@ -356,7 +356,7 @@ void usage(char *prog)
        fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask] device "
                "[type value]\n",
                prog);
-       fprintf(stderr, "\tList all tags for a device and exit\n", prog);
+       fprintf(stderr, "\tList all tags for a device and exit\n");
        exit(1);
 }
 
@@ -382,7 +382,7 @@ int main(int argc, char **argv)
                case 'm':
                        blkid_debug_mask = strtoul (optarg, &tmp, 0);
                        if (*tmp) {
-                               fprintf(stderr, "Invalid debug mask: %d\n",
+                               fprintf(stderr, "Invalid debug mask: %s\n",
                                        optarg);
                                exit(1);
                        }
@@ -407,7 +407,7 @@ int main(int argc, char **argv)
 
        dev = blkid_get_dev(cache, devname, flags);
        if (!dev) {
-               fprintf(stderr, "%s: cannot find device in blkid cache\n");
+               fprintf(stderr, "%s: cannot find device in blkid cache\n", devname);
                exit(1);
        }
        if (search_type) {
index 4c4c78d..8400a92 100644 (file)
  * Journal recovery routines for the generic filesystem journaling code;
  * part of the ext2fs journaling system.
  *
- * Licensed under GPLv2 or later, see file License in this tarball for details.
- */
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/*
+//usage:#define e2fsck_trivial_usage
+//usage:       "[-panyrcdfvstDFSV] [-b superblock] [-B blocksize] "
+//usage:       "[-I inode_buffer_blocks] [-P process_inode_size] "
+//usage:       "[-l|-L bad_blocks_file] [-C fd] [-j external_journal] "
+//usage:       "[-E extended-options] device"
+//usage:#define e2fsck_full_usage "\n\n"
+//usage:       "Check ext2/ext3 file system\n"
+//usage:     "\n       -p              Automatic repair (no questions)"
+//usage:     "\n       -n              Make no changes to the filesystem"
+//usage:     "\n       -y              Assume 'yes' to all questions"
+//usage:     "\n       -c              Check for bad blocks and add them to the badblock list"
+//usage:     "\n       -f              Force checking even if filesystem is marked clean"
+//usage:     "\n       -v              Verbose"
+//usage:     "\n       -b superblock   Use alternative superblock"
+//usage:     "\n       -B blocksize    Force blocksize when looking for superblock"
+//usage:     "\n       -j journal      Set location of the external journal"
+//usage:     "\n       -l file         Add to badblocks list"
+//usage:     "\n       -L file         Set badblocks list"
+*/
 
 #include "e2fsck.h"    /*Put all of our defines here to clean things up*/
 
@@ -517,7 +538,6 @@ static void dict_insert(dict_t *dict, dnode_t *node, const void *key)
        }
 
        dict_root(dict)->color = dnode_black;
-
 }
 
 /*
@@ -561,7 +581,7 @@ static dnode_t *dict_first(dict_t *dict)
 
 /*
  * Return the given node's successor node---the node which has the
- * next key in the the left to right ordering. If the node has
+ * next key in the left to right ordering. If the node has
  * no successor, a null pointer is returned rather than a pointer to
  * the nil node.
  */
@@ -801,7 +821,6 @@ static void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks)
        dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks
                                       * sizeof (struct dx_dirblock_info),
                                       "dx_block info array");
-
 }
 
 /*
@@ -1031,7 +1050,7 @@ static errcode_t ea_refcount_create(int size, ext2_refcount_t *ret)
        refcount->size = size;
        bytes = (size_t) (size * sizeof(struct ea_refcount_el));
 #ifdef DEBUG
-       printf("Refcount allocated %d entries, %d bytes.\n",
+       printf("Refcount allocated %d entries, %lu bytes.\n",
               refcount->size, bytes);
 #endif
        retval = ext2fs_get_mem(bytes, &refcount->list);
@@ -1724,7 +1743,6 @@ errout:
        ext2fs_free_mem(&j_inode);
        ext2fs_free_mem(&journal);
        return retval;
-
 }
 
 static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
@@ -3375,7 +3393,6 @@ static void e2fsck_pass1(e2fsck_t ctx)
                                        e2fsck_write_inode(ctx, ino, inode,
                                                           "pass1");
                                }
-
                        }
                        /*
                         * If dtime is set, offer to clear it.  mke2fs
@@ -3407,7 +3424,7 @@ static void e2fsck_pass1(e2fsck_t ctx)
                                continue;
                        }
                        if ((inode->i_links_count || inode->i_blocks ||
-                            inode->i_blocks || inode->i_block[0]) &&
+                            inode->i_block[0]) &&
                            fix_problem(ctx, PR_1_JOURNAL_INODE_NOT_CLEAR,
                                        &pctx)) {
                                memset(inode, 0, inode_size);
@@ -3678,7 +3695,6 @@ endit:
 
        ext2fs_free_mem(&block_buf);
        ext2fs_free_mem(&inode);
-
 }
 
 /*
@@ -4451,8 +4467,7 @@ static void mark_table_blocks(e2fsck_t ctx)
                                                ctx->invalid_bitmaps++;
                                        }
                                } else {
-                                   ext2fs_mark_block_bitmap(ctx->block_found_map,
-                                                            b);
+                                       ext2fs_mark_block_bitmap(ctx->block_found_map, b);
                                }
                        }
                }
@@ -4469,10 +4484,9 @@ static void mark_table_blocks(e2fsck_t ctx)
                                        ctx->invalid_bitmaps++;
                                }
                        } else {
-                           ext2fs_mark_block_bitmap(ctx->block_found_map,
-                                    fs->group_desc[i].bg_block_bitmap);
-                   }
-
+                               ext2fs_mark_block_bitmap(ctx->block_found_map,
+                                       fs->group_desc[i].bg_block_bitmap);
+                       }
                }
                /*
                 * Mark block used for the inode bitmap
@@ -4486,8 +4500,8 @@ static void mark_table_blocks(e2fsck_t ctx)
                                        ctx->invalid_bitmaps++;
                                }
                        } else {
-                           ext2fs_mark_block_bitmap(ctx->block_found_map,
-                                    fs->group_desc[i].bg_inode_bitmap);
+                               ext2fs_mark_block_bitmap(ctx->block_found_map,
+                                       fs->group_desc[i].bg_inode_bitmap);
                        }
                }
                block += fs->super->s_blocks_per_group;
@@ -5588,7 +5602,6 @@ static void e2fsck_pass2(e2fsck_t ctx)
                        ext2fs_mark_super_dirty(fs);
                }
        }
-
 }
 
 #define MAX_DEPTH 32000
@@ -9748,7 +9761,6 @@ int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
                if (print_answer)
                        printf("%s.\n", answer ?
                               _(preen_msg[(int) ptr->prompt]) : _("IGNORED"));
-
        }
 
        if ((ptr->prompt == PROMPT_ABORT) && answer)
@@ -11324,7 +11336,7 @@ static int release_inode_block(ext2_filsys fs, blk_t *block_nr,
        if ((blk < fs->super->s_first_data_block) ||
            (blk >= fs->super->s_blocks_count)) {
                fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx);
      return_abort:
+ return_abort:
                pb->abort = 1;
                return BLOCK_ABORT;
        }
@@ -11537,7 +11549,7 @@ static int release_orphan_inodes(e2fsck_t ctx)
        }
        ext2fs_free_mem(&block_buf);
        return 0;
-return_abort:
+ return_abort:
        ext2fs_free_mem(&block_buf);
        return 1;
 }
@@ -11565,7 +11577,7 @@ static void check_resize_inode(e2fsck_t ctx)
         * s_reserved_gdt_blocks must be zero.
         */
        if (!(fs->super->s_feature_compat &
-             EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
+             EXT2_FEATURE_COMPAT_RESIZE_INO)) {
                if (fs->super->s_reserved_gdt_blocks) {
                        pctx.num = fs->super->s_reserved_gdt_blocks;
                        if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS,
@@ -11581,7 +11593,7 @@ static void check_resize_inode(e2fsck_t ctx)
        retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
        if (retval) {
                if (fs->super->s_feature_compat &
-                   EXT2_FEATURE_COMPAT_RESIZE_INODE)
+                   EXT2_FEATURE_COMPAT_RESIZE_INO)
                        ctx->flags |= E2F_FLAG_RESIZE_INODE;
                return;
        }
@@ -11591,7 +11603,7 @@ static void check_resize_inode(e2fsck_t ctx)
         * the resize inode is cleared; then we're done.
         */
        if (!(fs->super->s_feature_compat &
-             EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
+             EXT2_FEATURE_COMPAT_RESIZE_INO)) {
                for (i=0; i < EXT2_N_BLOCKS; i++) {
                        if (inode.i_block[i])
                                break;
@@ -11618,7 +11630,7 @@ static void check_resize_inode(e2fsck_t ctx)
            !(inode.i_mode & LINUX_S_IFREG) ||
            (blk < fs->super->s_first_data_block ||
             blk >= fs->super->s_blocks_count)) {
      resize_inode_invalid:
+ resize_inode_invalid:
                if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) {
                        memset(&inode, 0, sizeof(inode));
                        e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
@@ -11660,10 +11672,9 @@ static void check_resize_inode(e2fsck_t ctx)
                }
        }
 
-cleanup:
+ cleanup:
        ext2fs_free_mem(&dind_buf);
-
- }
+}
 
 static void check_super_block(e2fsck_t ctx)
 {
@@ -11842,7 +11853,6 @@ static void check_super_block(e2fsck_t ctx)
                    (gd->bg_free_inodes_count > sb->s_inodes_per_group) ||
                    (gd->bg_used_dirs_count > sb->s_inodes_per_group))
                        ext2fs_unmark_valid(fs);
-
        }
 
        /*
@@ -11902,7 +11912,6 @@ static void check_super_block(e2fsck_t ctx)
                        fs->super->s_feature_incompat &=
                                ~EXT2_FEATURE_INCOMPAT_FILETYPE;
                        ext2fs_mark_super_dirty(fs);
-
                }
        }
 
@@ -12186,11 +12195,7 @@ static void swap_filesys(e2fsck_t ctx)
 void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
                             const char *description)
 {
-       void *ret;
-       char buf[256];
-
-       ret = xzalloc(size);
-       return ret;
+       return xzalloc(size);
 }
 
 static char *string_copy(const char *str, int len)
index 73d398f..c159fab 100644 (file)
@@ -258,7 +258,7 @@ The following defines are used in the 'flags' field of a dx_dirblock_info
 #define PR_1_SET_IMAGIC    0x01002F  /* Imagic flag set on an inode when filesystem doesn't support it */
 #define PR_1_SET_IMMUTABLE            0x010030  /* Immutable flag set on a device or socket inode */
 #define PR_1_COMPR_SET                0x010031  /* Compression flag set on a non-compressed filesystem */
-#define PR_1_SET_NONZSIZE             0x010032  /* Non-zero size on on device, fifo or socket inode */
+#define PR_1_SET_NONZSIZE             0x010032  /* Non-zero size on device, fifo or socket inode */
 #define PR_1_FS_REV_LEVEL             0x010033  /* Filesystem revision is 0, but feature flags are set */
 #define PR_1_JOURNAL_INODE_NOT_CLEAR  0x010034  /* Journal inode not in use, needs clearing */
 #define PR_1_JOURNAL_BAD_MODE         0x010035  /* Journal inode has wrong mode */
@@ -629,12 +629,10 @@ struct e2fsck_struct {
 };
 
 
-#define tid_gt(x, y)           ((x - y) > 0)
+#define tid_gt(x, y)  ((x - y) > 0)
 
 static inline int tid_geq(tid_t x, tid_t y)
 {
        int difference = (x - y);
        return (difference >= 0);
 }
-
-
index 1948707..482630c 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 NEEDED-$(CONFIG_CHATTR) = y
 NEEDED-$(CONFIG_LSATTR) = y
index b45754f..2102ed8 100644 (file)
@@ -34,7 +34,7 @@ static const struct feature feature_list[] = {
                        "ext_attr" },
        {       E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX,
                        "dir_index" },
-       {       E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_RESIZE_INODE,
+       {       E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_RESIZE_INO,
                        "resize_inode" },
        {       E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER,
                        "sparse_super" },
index 1abe2ba..6a2f178 100644 (file)
@@ -70,5 +70,3 @@ int main(int argc, char **argv)
        exit(0);
 }
 #endif
-
-
index 8ca65ee..12adc6e 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 NEEDED-$(CONFIG_E2FSCK) = y
 NEEDED-$(CONFIG_FSCK) = y
index 5021d72..cbb63e1 100644 (file)
@@ -171,4 +171,3 @@ errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish,
        } while (b != finish);
        return EXT2_ET_BLOCK_ALLOC_FAIL;
 }
-
index b2d786e..7c60e2b 100644 (file)
@@ -97,12 +97,9 @@ errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
                fs->group_desc[group].bg_inode_table = new_blk;
        }
 
-
        return 0;
 }
 
-
-
 errcode_t ext2fs_allocate_tables(ext2_filsys fs)
 {
        errcode_t       retval;
@@ -115,4 +112,3 @@ errcode_t ext2fs_allocate_tables(ext2_filsys fs)
        }
        return 0;
 }
-
index 1deae54..a967896 100644 (file)
@@ -260,9 +260,3 @@ static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
        *block_nr = blk;
        return BLOCK_CHANGED;
 }
-
-
-
-
-
-
index 9870611..3d08394 100644 (file)
@@ -65,7 +65,7 @@ int ext2fs_test_bit(unsigned int nr, const void * addr)
        return (mask & *ADDR);
 }
 
-#endif /* !_EXT2_HAVE_ASM_BITOPS_ */
+#endif  /* !_EXT2_HAVE_ASM_BITOPS_ */
 
 void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg,
                        const char *description)
@@ -88,4 +88,3 @@ void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap,
                bb_error_msg("#%lu", arg);
 #endif
 }
-
index 4980969..dbd04f8 100644 (file)
@@ -435,4 +435,3 @@ errcode_t ext2fs_block_iterate(ext2_filsys fs,
        return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags,
                                     block_buf, xlate_func, &xl);
 }
-
index b2d0279..796b0e4 100644 (file)
@@ -259,6 +259,3 @@ done:
        }
        return retval;
 }
-
-
-
index 635410d..ec9244d 100644 (file)
@@ -153,4 +153,3 @@ errcode_t ext2fs_move_blocks(ext2_filsys fs,
        }
        return 0;
 }
-
index 216fd13..87bf72b 100644 (file)
@@ -84,4 +84,3 @@ errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block,
 #define ext2fs_brel_move(brel, old, new) ((brel)->move((brel), old, new))
 #define ext2fs_brel_delete(brel, old) ((brel)->delete((brel), old))
 #define ext2fs_brel_free(brel) ((brel)->free((brel)))
-
index 7ba7f22..bfa15e2 100644 (file)
@@ -378,4 +378,3 @@ errcode_t ext2fs_close(ext2_filsys fs)
        ext2fs_free(fs);
        return 0;
 }
-
index 05b8eb8..7f78ff8 100644 (file)
@@ -70,4 +70,3 @@ errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1,
 
        return 0;
 }
-
index b7d8735..eb5dae0 100644 (file)
@@ -217,4 +217,3 @@ next:
                return BLOCK_ABORT;
        return 0;
 }
-
index f651338..f9c5a10 100644 (file)
@@ -130,4 +130,3 @@ errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block,
 {
        return ext2fs_write_dir_block2(fs, block, inbuf, 0);
 }
-
index 203c29f..d187937 100644 (file)
@@ -92,6 +92,4 @@ errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest)
 errout:
        ext2fs_free(fs);
        return retval;
-
 }
-
index 8d38ecc..a598d01 100644 (file)
@@ -37,16 +37,3 @@ struct ext2_image_hdr {
        __u32   offset_blockmap; /* Byte offset of the inode bitmaps */
        __u32   offset_reserved[8];
 };
-
-
-
-
-
-
-
-
-
-
-
-
-
index cc91bb8..ca309c0 100644 (file)
@@ -50,4 +50,3 @@ struct ext2_ext_attr_entry {
        sizeof(struct ext2_xattr_entry)) & ~EXT2_EXT_ATTR_ROUND)
 #define EXT2_XATTR_SIZE(size) \
        (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND)
-
index 6f4f708..80ea2cb 100644 (file)
@@ -475,7 +475,7 @@ struct ext2_super_block {
 #define EXT2_FEATURE_COMPAT_IMAGIC_INODES      0x0002
 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL                0x0004
 #define EXT2_FEATURE_COMPAT_EXT_ATTR           0x0008
-#define EXT2_FEATURE_COMPAT_RESIZE_INODE       0x0010
+#define EXT2_FEATURE_COMPAT_RESIZE_INO         0x0010
 #define EXT2_FEATURE_COMPAT_DIR_INDEX          0x0020
 
 #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER    0x0001
index 9f77201..39fb116 100644 (file)
@@ -383,7 +383,7 @@ typedef struct ext2_icount *ext2_icount_t;
 #define EXT2_LIB_FEATURE_COMPAT_SUPP   (EXT2_FEATURE_COMPAT_DIR_PREALLOC|\
                                         EXT2_FEATURE_COMPAT_IMAGIC_INODES|\
                                         EXT3_FEATURE_COMPAT_HAS_JOURNAL|\
-                                        EXT2_FEATURE_COMPAT_RESIZE_INODE|\
+                                        EXT2_FEATURE_COMPAT_RESIZE_INO|\
                                         EXT2_FEATURE_COMPAT_DIR_INDEX|\
                                         EXT2_FEATURE_COMPAT_EXT_ATTR)
 
index 908b5d9..7a02e9a 100644 (file)
@@ -85,5 +85,3 @@ extern int ext2fs_process_dir_block(ext2_filsys               fs,
                                    blk_t               ref_block,
                                    int                 ref_offset,
                                    void                *priv_data);
-
-
index b9aab44..7d37d23 100644 (file)
@@ -155,8 +155,8 @@ int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino)
 blk_t ext2fs_inode_data_blocks(ext2_filsys fs,
                                        struct ext2_inode *inode)
 {
-       return inode->i_blocks -
-             (inode->i_file_acl ? fs->blocksize >> 9 : 0);
+       return inode->i_blocks -
+               (inode->i_file_acl ? fs->blocksize >> 9 : 0);
 }
 
 
index e429826..45ed765 100644 (file)
@@ -23,7 +23,7 @@
 #endif
 #if HAVE_SYS_MOUNT_H
 #include <sys/param.h>
-#include <sys/mount.h>         /* This may define BLKFLSBUF */
+#include <sys/mount.h>  /* This may define BLKFLSBUF */
 #endif
 
 #include "ext2_fs.h"
  */
 #ifdef __linux__
 #ifndef BLKFLSBUF
-#define BLKFLSBUF      _IO(0x12,97)    /* flush buffer cache */
+#define BLKFLSBUF  _IO(0x12,97)  /* flush buffer cache */
 #endif
 #ifndef FDFLUSH
-#define FDFLUSH                _IO(2,0x4b)     /* flush floppy disk */
+#define FDFLUSH    _IO(2,0x4b)  /* flush floppy disk */
 #endif
 #endif
 
index 65c4ee7..0c5d48b 100644 (file)
@@ -125,4 +125,3 @@ void ext2fs_free_dblist(ext2_dblist dblist)
        dblist->magic = 0;
        ext2fs_free_mem(&dblist);
 }
-
index a98b2b9..2bb1cc2 100644 (file)
@@ -15,7 +15,7 @@
  *     string, placing the result in <name>.  <dir> is the containing
  *     directory inode, and <ino> is the inode number itself.  If
  *     <ino> is zero, then ext2fs_get_pathname will return pathname
- *     of the the directory <dir>.
+ *     of the directory <dir>.
  *
  */
 
@@ -153,5 +153,4 @@ errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino,
        retval = ext2fs_get_pathname_int(fs, dir, ino, 32, buf, name);
        ext2fs_free_mem(&buf);
        return retval;
-
 }
index ff11fe9..ee4bbb7 100644 (file)
@@ -174,9 +174,9 @@ errcode_t ext2fs_get_device_size(const char *file, int blocksize,
 
 #ifdef BLKGETSIZE64
 #ifdef __linux__
-       if ((uname(&ut) == 0) &&
-           ((ut.release[0] == '2') && (ut.release[1] == '.') &&
-            (ut.release[2] < '6') && (ut.release[3] == '.')))
+       uname(&ut);
+       if ((ut.release[0] == '2') && (ut.release[1] == '.') &&
+            (ut.release[2] < '6') && (ut.release[3] == '.'))
                valid_blkgetsize64 = 0;
 #endif
        if (valid_blkgetsize64 &&
index c86a1c5..a103830 100644 (file)
@@ -67,5 +67,3 @@ errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf)
 #endif
        return io_channel_write_blk(fs->io, blk, 1, buf);
 }
-
-
index ef1d343..240335b 100644 (file)
@@ -83,7 +83,7 @@ static int calc_reserved_gdt_blocks(ext2_filsys fs)
        if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb))
                rsv_gdb = EXT2_ADDR_PER_BLOCK(sb);
 #ifdef RES_GDT_DEBUG
-       printf("max_blocks %lu, rsv_groups = %lu, rsv_gdb = %lu\n",
+       printf("max_blocks %lu, rsv_groups = %lu, rsv_gdb = %u\n",
               max_blocks, rsv_groups, rsv_gdb);
 #endif
 
@@ -284,7 +284,7 @@ retry:
        /*
         * check the number of reserved group descriptor table blocks
         */
-       if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE)
+       if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INO)
                rsv_gdt = calc_reserved_gdt_blocks(fs);
        else
                rsv_gdt = 0;
index 9b620a7..7457b93 100644 (file)
@@ -1,8 +1,8 @@
 /* vi: set sw=4 ts=4: */
 /*
  * inline.c --- Includes the inlined functions defined in the header
- *     files as standalone functions, in case the application program
- *     is compiled with inlining turned off.
+ * files as standalone functions, in case the application program
+ * is compiled with inlining turned off.
  *
  * Copyright (C) 1993, 1994 Theodore Ts'o.
  *
@@ -30,4 +30,3 @@
 #include "ext2_fs.h"
 #define INCLUDE_INLINE_FUNCS
 #include "ext2fs.h"
-
index 5e0d081..7a1d5c9 100644 (file)
@@ -764,4 +764,3 @@ errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino)
                return EXT2_ET_NO_DIRECTORY;
        return 0;
 }
-
index 4bfa93a..b861d5f 100644 (file)
@@ -268,4 +268,3 @@ static errcode_t inode_flush(io_channel channel)
 
        return ext2fs_file_flush(data->file);
 }
-
index 7f24f9b..f5f6f31 100644 (file)
@@ -59,7 +59,7 @@ static errcode_t check_mntent_file(const char *mtab_file, const char *file,
                if (S_ISBLK(st_buf.st_mode)) {
 #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
                        file_rdev = st_buf.st_rdev;
-#endif /* __GNU__ */
+#endif  /* __GNU__ */
                } else {
                        file_dev = st_buf.st_dev;
                        file_ino = st_buf.st_ino;
@@ -73,7 +73,7 @@ static errcode_t check_mntent_file(const char *mtab_file, const char *file,
 #ifndef __GNU__
                                if (file_rdev && (file_rdev == st_buf.st_rdev))
                                        break;
-#endif /* __GNU__ */
+#endif  /* __GNU__ */
                        } else {
                                if (file_dev && ((file_dev == st_buf.st_dev) &&
                                                 (file_ino == st_buf.st_ino)))
@@ -99,7 +99,7 @@ static errcode_t check_mntent_file(const char *mtab_file, const char *file,
                                goto is_root;
                        }
                }
-#endif /* __GNU__ */
+#endif  /* __GNU__ */
                goto errout;
        }
 #ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */
@@ -247,7 +247,7 @@ static int is_swap_device(const char *file)
        if ((stat(file, &st_buf) == 0) &&
            S_ISBLK(st_buf.st_mode))
                file_dev = st_buf.st_rdev;
-#endif /* __GNU__ */
+#endif  /* __GNU__ */
 
        if (!(f = fopen_for_read("/proc/swaps")))
                return 0;
@@ -271,7 +271,7 @@ static int is_swap_device(const char *file)
                        ret++;
                        break;
                }
-#endif /* __GNU__ */
+#endif  /* __GNU__ */
        }
        fclose(f);
        return ret;
index 136635d..17c586a 100644 (file)
@@ -60,6 +60,4 @@ typedef struct journal_superblock_s
        /* Dynamic information describing the current state of the log */
        __u32           s_sequence;     /* first commit ID expected in log */
        __u32           s_start;        /* blocknr of start of log */
-
 } journal_superblock_t;
-
index 31b30a1..b2e8de8 100644 (file)
@@ -66,5 +66,3 @@ errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name,
 
        return (ls.found) ? 0 : EXT2_ET_FILE_NOT_FOUND;
 }
-
-
index 93f47b0..a86ac8e 100644 (file)
@@ -136,7 +136,4 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
 cleanup:
        ext2fs_free_mem(&block);
        return retval;
-
 }
-
-
index db1c8bf..748d9ab 100644 (file)
@@ -188,7 +188,6 @@ static int mkjournal_proc(ext2_filsys       fs,
                return (BLOCK_CHANGED | BLOCK_ABORT);
        else
                return BLOCK_CHANGED;
-
 }
 
 /*
@@ -423,6 +422,5 @@ main(int argc, char **argv)
        }
        ext2fs_close(fs);
        exit(0);
-
 }
 #endif
index 14d48fb..1824461 100644 (file)
@@ -202,4 +202,3 @@ errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
        ext2fs_free_mem(&buf);
        return retval;
 }
-
index 9470e7f..9f15662 100644 (file)
@@ -66,7 +66,6 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
                dir->name_len = 2 | filetype;
                dir->name[0] = '.';
                dir->name[1] = '.';
-
        }
        *block = buf;
        return 0;
index 4766157..ce77bc9 100644 (file)
@@ -94,5 +94,3 @@ errcode_t ext2fs_read_bb_inode(ext2_filsys fs, ext2_badblocks_list *bb_list)
 
        return rb.err;
 }
-
-
index 831adcc..bf1fc32 100644 (file)
@@ -94,5 +94,3 @@ errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,
        return ext2fs_read_bb_FILE2(fs, f, bb_list, (void *) invalid,
                                    call_compat_invalid);
 }
-
-
index 3c550d5..403463a 100644 (file)
@@ -218,4 +218,3 @@ out_free:
        ext2fs_free_mem((void *)&dindir_buf);
        return retval;
 }
-
index e932b3c..32e87b7 100644 (file)
@@ -104,4 +104,3 @@ errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end,
        bmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP;
        return retval;
 }
-
index 0ae0a82..bba4326 100644 (file)
@@ -266,7 +266,6 @@ errcode_t ext2fs_read_block_bitmap(ext2_filsys fs)
 
 errcode_t ext2fs_read_bitmaps(ext2_filsys fs)
 {
-
        EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
        if (fs->inode_map && fs->block_map)
@@ -293,4 +292,3 @@ errcode_t ext2fs_write_bitmaps(ext2_filsys fs)
        }
        return 0;
 }
-
index 2fca3cf..07b757a 100644 (file)
@@ -66,7 +66,6 @@ void ext2fs_swap_super(struct ext2_super_block * sb)
                sb->s_hash_seed[i] = ext2fs_swab32(sb->s_hash_seed[i]);
        for (i=0; i < 17; i++)
                sb->s_jnl_blocks[i] = ext2fs_swab32(sb->s_jnl_blocks[i]);
-
 }
 
 void ext2fs_swap_group_desc(struct ext2_group_desc *gdp)
@@ -222,7 +221,6 @@ void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
        ext2fs_swap_ext_attr((char *) (eat + 1), (char *) (eaf + 1),
                             bufsize - sizeof(struct ext2_inode) -
                             t->i_extra_isize - sizeof(__u32), 0);
-
 }
 
 void ext2fs_swap_inode(ext2_filsys fs, struct ext2_inode *t,
index 474f073..3c95829 100644 (file)
@@ -544,7 +544,7 @@ static errcode_t unix_read_blk(io_channel channel, unsigned long block,
                /* If it's in the cache, use it! */
                if ((cache = find_cached_block(data, block, &reuse[0]))) {
 #ifdef DEBUG
-                       printf("Using cached block %d\n", block);
+                       printf("Using cached block %lu\n", block);
 #endif
                        memcpy(cp, cache->buf, channel->block_size);
                        count--;
@@ -560,7 +560,7 @@ static errcode_t unix_read_blk(io_channel channel, unsigned long block,
                        if (find_cached_block(data, block+i, &reuse[i]))
                                break;
 #ifdef DEBUG
-               printf("Reading %d blocks starting at %d\n", i, block);
+               printf("Reading %d blocks starting at %lu\n", i, block);
 #endif
                if ((retval = raw_read_blk(channel, data, block, i, cp)))
                        return retval;
index 83ac271..71a9ffc 100644 (file)
@@ -97,4 +97,3 @@ errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir,
 
        return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE;
 }
-
index 2a66d72..91cce97 100644 (file)
@@ -20,7 +20,7 @@
  * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
  *      2001, 2002, 2003, 2004, 2005 by  Theodore Ts'o.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include <sys/types.h>
@@ -349,15 +349,7 @@ static void parse_escape(char *word)
        if (!word)
                return;
 
-       for (p = q = word; *p; q++) {
-               c = *p++;
-               if (c != '\\') {
-                       *q = c;
-               } else {
-                       *q = bb_process_escape_sequence(&p);
-               }
-       }
-       *q = 0;
+       strcpy_and_process_escape_sequences(word, word);
 }
 
 static void free_instance(struct fsck_instance *i)
@@ -1247,7 +1239,6 @@ static void PRS(int argc, char **argv)
                                                progress_fd = 0;
                                        else {
                                                goto next_arg;
-                                               i++;
                                        }
                                }
                                break;
index 294bf2f..9eab68b 100644 (file)
 
 /*
  * History:
- * 93/10/30    - Creation
- * 93/11/13    - Replace stat() calls by lstat() to avoid loops
- * 94/02/27    - Integrated in Ted's distribution
- * 98/12/29    - Display version info only when -V specified (G M Sipe)
+ * 93/10/30 - Creation
+ * 93/11/13 - Replace stat() calls by lstat() to avoid loops
+ * 94/02/27 - Integrated in Ted's distribution
+ * 98/12/29 - Display version info only when -V specified (G M Sipe)
  */
 
 #include <sys/types.h>
@@ -93,7 +93,7 @@ static int lsattr_dir_proc(const char *dir_name, struct dirent *de,
        path = concat_path_file(dir_name, de->d_name);
 
        if (lstat(path, &st) == -1)
-               bb_perror_msg(path);
+               bb_simple_perror_msg(path);
        else {
                if (de->d_name[0] != '.' || (flags & OPT_ALL)) {
                        list_attributes(path);
index 5203645..ebcb46c 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
  *     2003, 2004, 2005 by Theodore Ts'o.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /* Usage: mke2fs [options] device
@@ -239,7 +239,7 @@ static void test_disk(ext2_filsys fs, badblocks_list *bb_list)
        errcode_t       retval;
        char            buf[1024];
 
-       sprintf(buf, "badblocks -b %d %s%s%s %d", fs->blocksize,
+       sprintf(buf, "badblocks -b %u %s%s%s %d", fs->blocksize,
                quiet ? "" : "-s ", (cflag > 1) ? "-w " : "",
                fs->device_name, fs->super->s_blocks_count);
        mke2fs_verbose("Running command: %s\n", buf);
@@ -385,7 +385,7 @@ static errcode_t zero_blocks(ext2_filsys fs, blk_t blk, int num,
                             struct progress_struct *progress,
                             blk_t *ret_blk, int *ret_count)
 {
-       int             j, count, next_update, next_update_incr;
+       int             j, count, next_update;
        static char     *buf;
        errcode_t       retval;
 
@@ -403,9 +403,7 @@ static errcode_t zero_blocks(ext2_filsys fs, blk_t blk, int num,
        }
        /* OK, do the write loop */
        next_update = 0;
-       next_update_incr = num / 100;
-       if (next_update_incr < 1)
-               next_update_incr = 1;
+
        for (j=0; j < num; j += STRIDE_LENGTH, blk += STRIDE_LENGTH) {
                count = num - j;
                if (count > STRIDE_LENGTH)
@@ -757,7 +755,7 @@ static void parse_extended_opts(struct ext2_super_block *sb_param,
 
                        if (rsv_gdb > 0) {
                                sb_param->s_feature_compat |=
-                                       EXT2_FEATURE_COMPAT_RESIZE_INODE;
+                                       EXT2_FEATURE_COMPAT_RESIZE_INO;
 
                                sb_param->s_reserved_gdt_blocks = rsv_gdb;
                        }
@@ -778,7 +776,7 @@ static void parse_extended_opts(struct ext2_super_block *sb_param,
 
 static __u32 ok_features[3] = {
        EXT3_FEATURE_COMPAT_HAS_JOURNAL |
-               EXT2_FEATURE_COMPAT_RESIZE_INODE |
+               EXT2_FEATURE_COMPAT_RESIZE_INO |
                EXT2_FEATURE_COMPAT_DIR_INDEX,  /* Compat */
        EXT2_FEATURE_INCOMPAT_FILETYPE|         /* Incompat */
                EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|
@@ -895,7 +893,7 @@ static int PRS(int argc, char **argv)
                        creator_os = optarg;
                        break;
                case 'r':
-                       param.s_rev_level = xatoi_u(optarg);
+                       param.s_rev_level = xatoi_positive(optarg);
                        if (param.s_rev_level == EXT2_GOOD_OLD_REV) {
                                param.s_feature_incompat = 0;
                                param.s_feature_compat = 0;
@@ -912,11 +910,11 @@ static int PRS(int argc, char **argv)
                        break;
 #ifdef EXT2_DYNAMIC_REV
                case 'I':
-                       inode_size = xatoi_u(optarg);
+                       inode_size = xatoi_positive(optarg);
                        break;
 #endif
                case 'N':
-                       num_inodes = xatoi_u(optarg);
+                       num_inodes = xatoi_positive(optarg);
                        break;
                case 'v':
                        quiet = 0;
@@ -1123,7 +1121,7 @@ static int PRS(int argc, char **argv)
        /* Since sparse_super is the default, we would only have a problem
         * here if it was explicitly disabled.
         */
-       if ((param.s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) &&
+       if ((param.s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INO) &&
            !(param.s_feature_ro_compat&EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
                bb_error_msg_and_die("reserved online resize blocks not supported "
                          "on non-sparse filesystem");
@@ -1312,7 +1310,7 @@ int mke2fs_main (int argc, char **argv)
                reserve_inodes(fs);
                create_bad_block_inode(fs, bb_list);
                if (fs->super->s_feature_compat &
-                   EXT2_FEATURE_COMPAT_RESIZE_INODE) {
+                   EXT2_FEATURE_COMPAT_RESIZE_INO) {
                        retval = ext2fs_create_resize_inode(fs);
                        mke2fs_error_msg_and_die(retval, "reserve blocks for online resize");
                }
index 00f8682..bbe30e5 100644 (file)
@@ -8,7 +8,7 @@
  *
  * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /*
@@ -607,7 +607,7 @@ int tune2fs_main(int argc, char **argv)
        if (e_flag) {
                sb->s_errors = errors;
                ext2fs_mark_super_dirty(fs);
-               printf("Setting error behavior to %d\n", errors);
+               printf("Setting error behavior to %u\n", errors);
        }
        if (g_flag) {
                sb->s_def_resgid = resgid;
index 64cca05..3e7ee8e 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include <stdio.h>
@@ -95,7 +95,6 @@ force_check:
                bb_error_msg("%s is apparently in use by the system", device);
                goto force_check;
        }
-
 }
 
 void parse_journal_opts(char **journal_device, int *journal_flags,
@@ -118,7 +117,7 @@ void parse_journal_opts(char **journal_device, int *journal_flags,
                }
                if (strcmp(token, "device") == 0) {
                        *journal_device = blkid_get_devname(NULL, arg, NULL);
-                       if (!journal_device) {
+                       if (!*journal_device) {
                                journal_usage++;
                                continue;
                        }
@@ -240,7 +239,7 @@ void make_journal_blocks(ext2_filsys fs, int journal_size, int journal_flags, in
                return;
        }
        if (!quiet)
-               printf("Creating journal (%ld blocks): ", journal_blocks);
+               printf("Creating journal (%lu blocks): ", journal_blocks);
        fflush(stdout);
        retval = ext2fs_add_journal_inode(fs, journal_blocks,
                                                  journal_flags);
index 9454fab..b8c687d 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 NEEDED-$(CONFIG_E2FSCK) = y
 NEEDED-$(CONFIG_FSCK) = y
index 00ede4f..46a745e 100644 (file)
@@ -4,11 +4,11 @@
  *
  * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 #include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "bb_e2fs_defs.h"
 
 // storage helpers
 char BUG_wrong_field_size(void);
@@ -27,24 +27,42 @@ do { \
 #define FETCH_LE32(field) \
        (sizeof(field) == 4 ? SWAP_LE32(field) : BUG_wrong_field_size())
 
+//usage:#define tune2fs_trivial_usage
+//usage:       "[-c MAX_MOUNT_COUNT] "
+////usage:     "[-e errors-behavior] [-g group] "
+//usage:       "[-i DAYS] "
+////usage:     "[-j] [-J journal-options] [-l] [-s sparse-flag] "
+////usage:     "[-m reserved-blocks-percent] [-o [^]mount-options[,...]] "
+////usage:     "[-r reserved-blocks-count] [-u user] "
+//usage:       "[-C MOUNT_COUNT] "
+//usage:       "[-L LABEL] "
+////usage:     "[-M last-mounted-dir] [-O [^]feature[,...]] "
+////usage:     "[-T last-check-time] [-U UUID] "
+//usage:       "BLOCKDEV"
+//usage:
+//usage:#define tune2fs_full_usage "\n\n"
+//usage:       "Adjust filesystem options on ext[23] filesystems"
+
 enum {
-       OPT_L = 1 << 0, // label
+       OPT_L = 1 << 0, // label
+       OPT_c = 1 << 1, // max mount count
+       OPT_i = 1 << 2, // check interval
+       OPT_C = 1 << 3, // current mount count
 };
 
 int tune2fs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int tune2fs_main(int argc UNUSED_PARAM, char **argv)
 {
        unsigned opts;
-       const char *label;
+       const char *label, *str_c, *str_i, *str_C;
        struct ext2_super_block *sb;
        int fd;
 
        opt_complementary = "=1";
-       opts = getopt32(argv, "L:", &label);
-       argv += optind; // argv[0] -- device
-
+       opts = getopt32(argv, "L:c:i:C:", &label, &str_c, &str_i, &str_C);
        if (!opts)
                bb_show_usage();
+       argv += optind; // argv[0] -- device
 
        // read superblock
        fd = xopen(argv[0], O_RDWR);
@@ -54,9 +72,28 @@ int tune2fs_main(int argc UNUSED_PARAM, char **argv)
 
        // mangle superblock
        //STORE_LE(sb->s_wtime, time(NULL)); - why bother?
+
+       if (opts & OPT_C) {
+               int n = xatoi_range(str_C, 1, 0xfffe);
+               STORE_LE(sb->s_mnt_count, (unsigned)n);
+       }
+
        // set the label
-       if (1 /*opts & OPT_L*/)
+       if (opts & OPT_L)
                safe_strncpy((char *)sb->s_volume_name, label, sizeof(sb->s_volume_name));
+
+       if (opts & OPT_c) {
+               int n = xatoi_range(str_c, -1, 0xfffe);
+               if (n == 0)
+                       n = -1;
+               STORE_LE(sb->s_max_mnt_count, (unsigned)n);
+       }
+
+       if (opts & OPT_i) {
+               unsigned n = xatou_range(str_i, 0, (unsigned)0xffffffff / (24*60*60)) * 24*60*60;
+               STORE_LE(sb->s_checkinterval, n);
+       }
+
        // write superblock
        xlseek(fd, 1024, SEEK_SET);
        xwrite(fd, sb, 1024);
index fc824cc..c6e9d92 100644 (file)
@@ -7,189 +7,6 @@ menu "Editors"
 
 INSERT
 
-config AWK
-       bool "awk"
-       default y
-       help
-         Awk is used as a pattern scanning and processing language. This is
-         the BusyBox implementation of that programming language.
-
-config FEATURE_AWK_LIBM
-       bool "Enable math functions (requires libm)"
-       default y
-       depends on AWK
-       help
-         Enable math functions of the Awk programming language.
-         NOTE: This will require libm to be present for linking.
-
-config CMP
-       bool "cmp"
-       default y
-       help
-         cmp is used to compare two files and returns the result
-         to standard output.
-
-config DIFF
-       bool "diff"
-       default y
-       help
-         diff compares two files or directories and outputs the
-         differences between them in a form that can be given to
-         the patch command.
-
-config FEATURE_DIFF_LONG_OPTIONS
-       bool "Enable long options"
-       default y
-       depends on DIFF && LONG_OPTS
-       help
-         Enable use of long options.
-
-config FEATURE_DIFF_DIR
-       bool "Enable directory support"
-       default y
-       depends on DIFF
-       help
-         This option enables support for directory and subdirectory
-         comparison.
-
-config ED
-       bool "ed"
-       default y
-       help
-         The original 1970's Unix text editor, from the days of teletypes.
-         Small, simple, evil. Part of SUSv3. If you're not already using
-         this, you don't need it.
-
-config PATCH
-       bool "patch"
-       default y
-       help
-         Apply a unified diff formatted patch.
-
-config SED
-       bool "sed"
-       default y
-       help
-         sed is used to perform text transformations on a file
-         or input from a pipeline.
-
-config VI
-       bool "vi"
-       default y
-       help
-         'vi' is a text editor. More specifically, it is the One True
-         text editor <grin>. It does, however, have a rather steep
-         learning curve. If you are not already comfortable with 'vi'
-         you may wish to use something else.
-
-config FEATURE_VI_MAX_LEN
-       int "Maximum screen width in vi"
-       range 256 16384
-       default 4096
-       depends on VI
-       help
-         Contrary to what you may think, this is not eating much.
-         Make it smaller than 4k only if you are very limited on memory.
-
-config FEATURE_VI_8BIT
-       bool "Allow vi to display 8-bit chars (otherwise shows dots)"
-       default n
-       depends on VI
-       help
-         If your terminal can display characters with high bit set,
-         you may want to enable this. Note: vi is not Unicode-capable.
-         If your terminal combines several 8-bit bytes into one character
-         (as in Unicode mode), this will not work properly.
-
-config FEATURE_VI_COLON
-       bool "Enable \":\" colon commands (no \"ex\" mode)"
-       default y
-       depends on VI
-       help
-         Enable a limited set of colon commands for vi. This does not
-         provide an "ex" mode.
-
-config FEATURE_VI_YANKMARK
-       bool "Enable yank/put commands and mark cmds"
-       default y
-       depends on VI
-       help
-         This will enable you to use yank and put, as well as mark in
-         busybox vi.
-
-config FEATURE_VI_SEARCH
-       bool "Enable search and replace cmds"
-       default y
-       depends on VI
-       help
-         Select this if you wish to be able to do search and replace in
-         busybox vi.
-
-config FEATURE_VI_USE_SIGNALS
-       bool "Catch signals"
-       default y
-       depends on VI
-       help
-         Selecting this option will make busybox vi signal aware. This will
-         make busybox vi support SIGWINCH to deal with Window Changes, catch
-         Ctrl-Z and Ctrl-C and alarms.
-
-config FEATURE_VI_DOT_CMD
-       bool "Remember previous cmd and \".\" cmd"
-       default y
-       depends on VI
-       help
-         Make busybox vi remember the last command and be able to repeat it.
-
-config FEATURE_VI_READONLY
-       bool "Enable -R option and \"view\" mode"
-       default y
-       depends on VI
-       help
-         Enable the read-only command line option, which allows the user to
-         open a file in read-only mode.
-
-config FEATURE_VI_SETOPTS
-       bool "Enable set-able options, ai ic showmatch"
-       default y
-       depends on VI
-       help
-         Enable the editor to set some (ai, ic, showmatch) options.
-
-config FEATURE_VI_SET
-       bool "Support for :set"
-       default y
-       depends on VI
-       help
-         Support for ":set".
-
-config FEATURE_VI_WIN_RESIZE
-       bool "Handle window resize"
-       default y
-       depends on VI
-       help
-         Make busybox vi behave nicely with terminals that get resized.
-
-config FEATURE_VI_ASK_TERMINAL
-       bool "Use 'tell me cursor position' ESC sequence to measure window"
-       default y
-       depends on VI
-       help
-         If terminal size can't be retrieved and $LINES/$COLUMNS are not set,
-         this option makes vi perform a last-ditch effort to find it:
-         vi positions cursor to 999,999 and asks terminal to report real
-         cursor position using "ESC [ 6 n" escape sequence, then reads stdin.
-
-         This is not clean but helps a lot on serial lines and such.
-
-config FEATURE_VI_OPTIMIZE_CURSOR
-       bool "Optimize cursor movement"
-       default y
-       depends on VI
-       help
-         This will make the cursor movement faster, but requires more memory
-         and it makes the applet a tiny bit larger.
-
 config FEATURE_ALLOW_EXEC
        bool "Allow vi and awk to execute shell commands"
        default y
index 9d4b9d0..6b4fb74 100644 (file)
@@ -2,15 +2,8 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
 INSERT
-lib-$(CONFIG_AWK)       += awk.o
-lib-$(CONFIG_CMP)       += cmp.o
-lib-$(CONFIG_DIFF)      += diff.o
-lib-$(CONFIG_ED)        += ed.o
-lib-$(CONFIG_PATCH)     += patch.o
-lib-$(CONFIG_SED)       += sed.o
-lib-$(CONFIG_VI)        += vi.o
index 83c5b47..d0e3781 100644 (file)
@@ -4,9 +4,49 @@
  *
  * Copyright (C) 2002 by Dmitry Zakharov <dmit@crp.bank.gov.ua>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//config:config AWK
+//config:      bool "awk"
+//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:
+//config:config FEATURE_AWK_LIBM
+//config:      bool "Enable math functions (requires libm)"
+//config:      default y
+//config:      depends on AWK
+//config:      help
+//config:        Enable math functions of the Awk programming language.
+//config:        NOTE: This will require libm to be present for linking.
+//config:
+//config:config FEATURE_AWK_GNU_EXTENSIONS
+//config:      bool "Enable a few GNU extensions"
+//config:      default y
+//config:      depends on AWK
+//config:      help
+//config:        Enable a few features from gawk:
+//config:        * command line option -e AWK_PROGRAM
+//config:        * simultaneous use of -f and -e on the command line.
+//config:          This enables the use of awk library files.
+//config:          Ex: awk -f mylib.awk -e '{print myfunction($1);}' ...
+
+//applet:IF_AWK(APPLET_NOEXEC(awk, awk, BB_DIR_USR_BIN, BB_SUID_DROP, awk))
+
+//kbuild:lib-$(CONFIG_AWK) += awk.o
+
+//usage:#define awk_trivial_usage
+//usage:       "[OPTIONS] [AWK_PROGRAM] [FILE]..."
+//usage:#define awk_full_usage "\n\n"
+//usage:       "       -v VAR=VAL      Set variable"
+//usage:     "\n       -F SEP          Use SEP as field separator"
+//usage:     "\n       -f FILE         Read program from FILE"
+//usage:       IF_FEATURE_AWK_GNU_EXTENSIONS(
+//usage:     "\n       -e AWK_PROGRAM"
+//usage:       )
+
 #include "libbb.h"
 #include "xregex.h"
 #include <math.h>
 /* If you comment out one of these below, it will be #defined later
  * to perform debug printfs to stderr: */
 #define debug_printf_walker(...)  do {} while (0)
+#define debug_printf_eval(...)  do {} while (0)
+#define debug_printf_parse(...)  do {} while (0)
 
 #ifndef debug_printf_walker
 # define debug_printf_walker(...) (fprintf(stderr, __VA_ARGS__))
 #endif
+#ifndef debug_printf_eval
+# define debug_printf_eval(...) (fprintf(stderr, __VA_ARGS__))
+#endif
+#ifndef debug_printf_parse
+# define debug_printf_parse(...) (fprintf(stderr, __VA_ARGS__))
+#endif
 
 
+#define OPTSTR_AWK \
+       "F:v:f:" \
+       IF_FEATURE_AWK_GNU_EXTENSIONS("e:") \
+       "W:"
+#define OPTCOMPLSTR_AWK \
+       "v::f::" \
+       IF_FEATURE_AWK_GNU_EXTENSIONS("e::")
+enum {
+       OPTBIT_F,       /* define field separator */
+       OPTBIT_v,       /* define variable */
+       OPTBIT_f,       /* pull in awk program from file */
+       IF_FEATURE_AWK_GNU_EXTENSIONS(OPTBIT_e,) /* -e AWK_PROGRAM */
+       OPTBIT_W,       /* -W ignored */
+       OPT_F = 1 << OPTBIT_F,
+       OPT_v = 1 << OPTBIT_v,
+       OPT_f = 1 << OPTBIT_f,
+       OPT_e = IF_FEATURE_AWK_GNU_EXTENSIONS((1 << OPTBIT_e)) + 0,
+       OPT_W = 1 << OPTBIT_W
+};
 
 #define        MAXVARFMT       240
 #define        MINNVBLOCK      64
@@ -140,7 +207,7 @@ typedef struct tsplitter_s {
 
 /* simple token classes */
 /* Order and hex values are very important!!!  See next_token() */
-#define        TC_SEQSTART      1                              /* ( */
+#define        TC_SEQSTART     1                       /* ( */
 #define        TC_SEQTERM      (1 << 1)                /* ) */
 #define        TC_REGEXP       (1 << 2)                /* /.../ */
 #define        TC_OUTRDR       (1 << 3)                /* | > >> */
@@ -175,7 +242,7 @@ typedef struct tsplitter_s {
 
 /* combined token classes */
 #define        TC_BINOP   (TC_BINOPX | TC_COMMA | TC_PIPE | TC_IN)
-#define        TC_UNARYOP (TC_UOPPRE | TC_UOPPOST)
+//#define      TC_UNARYOP (TC_UOPPRE | TC_UOPPOST)
 #define        TC_OPERAND (TC_VARIABLE | TC_ARRAY | TC_FUNCTION \
                    | TC_BUILTIN | TC_GETLINE | TC_SEQSTART | TC_STRING | TC_NUMBER)
 
@@ -227,6 +294,9 @@ typedef struct tsplitter_s {
  * For builtins it has different meaning: n n s3 s2 s1 v3 v2 v1,
  * n - min. number of args, vN - resolve Nth arg to var, sN - resolve to string
  */
+#undef P
+#undef PRIMASK
+#undef PRIMASK2
 #define P(x)      (x << 24)
 #define PRIMASK   0x7F000000
 #define PRIMASK2  0x7E000000
@@ -273,94 +343,86 @@ enum {
 
 /* tokens and their corresponding info values */
 
-#define        NTC     "\377"  /* switch to next token class (tc<<1) */
-#define        NTCC    '\377'
+#define NTC     "\377"  /* switch to next token class (tc<<1) */
+#define NTCC    '\377'
 
-#define        OC_B    OC_BUILTIN
+#define OC_B  OC_BUILTIN
 
 static const char tokenlist[] ALIGN1 =
-       "\1("       NTC
-       "\1)"       NTC
-       "\1/"       NTC                                 /* REGEXP */
-       "\2>>"      "\1>"       "\1|"       NTC         /* OUTRDR */
-       "\2++"      "\2--"      NTC                     /* UOPPOST */
-       "\2++"      "\2--"      "\1$"       NTC         /* UOPPRE1 */
-       "\2=="      "\1="       "\2+="      "\2-="      /* BINOPX */
-       "\2*="      "\2/="      "\2%="      "\2^="
-       "\1+"       "\1-"       "\3**="     "\2**"
-       "\1/"       "\1%"       "\1^"       "\1*"
-       "\2!="      "\2>="      "\2<="      "\1>"
-       "\1<"       "\2!~"      "\1~"       "\2&&"
-       "\2||"      "\1?"       "\1:"       NTC
-       "\2in"      NTC
-       "\1,"       NTC
-       "\1|"       NTC
-       "\1+"       "\1-"       "\1!"       NTC         /* UOPPRE2 */
-       "\1]"       NTC
-       "\1{"       NTC
-       "\1}"       NTC
-       "\1;"       NTC
-       "\1\n"      NTC
-       "\2if"      "\2do"      "\3for"     "\5break"   /* STATX */
-       "\10continue"           "\6delete"  "\5print"
-       "\6printf"  "\4next"    "\10nextfile"
-       "\6return"  "\4exit"    NTC
-       "\5while"   NTC
-       "\4else"    NTC
-
-       "\3and"     "\5compl"   "\6lshift"  "\2or"
-       "\6rshift"  "\3xor"
-       "\5close"   "\6system"  "\6fflush"  "\5atan2"   /* BUILTIN */
-       "\3cos"     "\3exp"     "\3int"     "\3log"
-       "\4rand"    "\3sin"     "\4sqrt"    "\5srand"
-       "\6gensub"  "\4gsub"    "\5index"   "\6length"
-       "\5match"   "\5split"   "\7sprintf" "\3sub"
-       "\6substr"  "\7systime" "\10strftime" "\6mktime"
-       "\7tolower" "\7toupper" NTC
-       "\7getline" NTC
-       "\4func"    "\10function"   NTC
-       "\5BEGIN"   NTC
-       "\3END"     "\0"
+       "\1("         NTC
+       "\1)"         NTC
+       "\1/"         NTC                                   /* REGEXP */
+       "\2>>"        "\1>"         "\1|"       NTC         /* OUTRDR */
+       "\2++"        "\2--"        NTC                     /* UOPPOST */
+       "\2++"        "\2--"        "\1$"       NTC         /* UOPPRE1 */
+       "\2=="        "\1="         "\2+="      "\2-="      /* BINOPX */
+       "\2*="        "\2/="        "\2%="      "\2^="
+       "\1+"         "\1-"         "\3**="     "\2**"
+       "\1/"         "\1%"         "\1^"       "\1*"
+       "\2!="        "\2>="        "\2<="      "\1>"
+       "\1<"         "\2!~"        "\1~"       "\2&&"
+       "\2||"        "\1?"         "\1:"       NTC
+       "\2in"        NTC
+       "\1,"         NTC
+       "\1|"         NTC
+       "\1+"         "\1-"         "\1!"       NTC         /* UOPPRE2 */
+       "\1]"         NTC
+       "\1{"         NTC
+       "\1}"         NTC
+       "\1;"         NTC
+       "\1\n"        NTC
+       "\2if"        "\2do"        "\3for"     "\5break"   /* STATX */
+       "\10continue" "\6delete"    "\5print"
+       "\6printf"    "\4next"      "\10nextfile"
+       "\6return"    "\4exit"      NTC
+       "\5while"     NTC
+       "\4else"      NTC
+
+       "\3and"       "\5compl"     "\6lshift"  "\2or"
+       "\6rshift"    "\3xor"
+       "\5close"     "\6system"    "\6fflush"  "\5atan2"   /* BUILTIN */
+       "\3cos"       "\3exp"       "\3int"     "\3log"
+       "\4rand"      "\3sin"       "\4sqrt"    "\5srand"
+       "\6gensub"    "\4gsub"      "\5index"   "\6length"
+       "\5match"     "\5split"     "\7sprintf" "\3sub"
+       "\6substr"    "\7systime"   "\10strftime" "\6mktime"
+       "\7tolower"   "\7toupper"   NTC
+       "\7getline"   NTC
+       "\4func"      "\10function" NTC
+       "\5BEGIN"     NTC
+       "\3END"
+       /* compiler adds trailing "\0" */
        ;
 
 static const uint32_t tokeninfo[] = {
        0,
        0,
        OC_REGEXP,
-       xS|'a',     xS|'w',     xS|'|',
-       OC_UNARY|xV|P(9)|'p',       OC_UNARY|xV|P(9)|'m',
-       OC_UNARY|xV|P(9)|'P',       OC_UNARY|xV|P(9)|'M',
-           OC_FIELD|xV|P(5),
-       OC_COMPARE|VV|P(39)|5,      OC_MOVE|VV|P(74),
-           OC_REPLACE|NV|P(74)|'+',    OC_REPLACE|NV|P(74)|'-',
-       OC_REPLACE|NV|P(74)|'*',    OC_REPLACE|NV|P(74)|'/',
-           OC_REPLACE|NV|P(74)|'%',    OC_REPLACE|NV|P(74)|'&',
-       OC_BINARY|NV|P(29)|'+',     OC_BINARY|NV|P(29)|'-',
-           OC_REPLACE|NV|P(74)|'&',    OC_BINARY|NV|P(15)|'&',
-       OC_BINARY|NV|P(25)|'/',     OC_BINARY|NV|P(25)|'%',
-           OC_BINARY|NV|P(15)|'&',     OC_BINARY|NV|P(25)|'*',
-       OC_COMPARE|VV|P(39)|4,      OC_COMPARE|VV|P(39)|3,
-           OC_COMPARE|VV|P(39)|0,      OC_COMPARE|VV|P(39)|1,
-       OC_COMPARE|VV|P(39)|2,      OC_MATCH|Sx|P(45)|'!',
-           OC_MATCH|Sx|P(45)|'~',      OC_LAND|Vx|P(55),
-       OC_LOR|Vx|P(59),            OC_TERNARY|Vx|P(64)|'?',
-           OC_COLON|xx|P(67)|':',
-       OC_IN|SV|P(49),
+       xS|'a',                  xS|'w',                  xS|'|',
+       OC_UNARY|xV|P(9)|'p',    OC_UNARY|xV|P(9)|'m',
+       OC_UNARY|xV|P(9)|'P',    OC_UNARY|xV|P(9)|'M',    OC_FIELD|xV|P(5),
+       OC_COMPARE|VV|P(39)|5,   OC_MOVE|VV|P(74),        OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-',
+       OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/', OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&',
+       OC_BINARY|NV|P(29)|'+',  OC_BINARY|NV|P(29)|'-',  OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&',
+       OC_BINARY|NV|P(25)|'/',  OC_BINARY|NV|P(25)|'%',  OC_BINARY|NV|P(15)|'&',  OC_BINARY|NV|P(25)|'*',
+       OC_COMPARE|VV|P(39)|4,   OC_COMPARE|VV|P(39)|3,   OC_COMPARE|VV|P(39)|0,   OC_COMPARE|VV|P(39)|1,
+       OC_COMPARE|VV|P(39)|2,   OC_MATCH|Sx|P(45)|'!',   OC_MATCH|Sx|P(45)|'~',   OC_LAND|Vx|P(55),
+       OC_LOR|Vx|P(59),         OC_TERNARY|Vx|P(64)|'?', OC_COLON|xx|P(67)|':',
+       OC_IN|SV|P(49), /* in */
        OC_COMMA|SS|P(80),
        OC_PGETLINE|SV|P(37),
-       OC_UNARY|xV|P(19)|'+',      OC_UNARY|xV|P(19)|'-',
-           OC_UNARY|xV|P(19)|'!',
-       0,
+       OC_UNARY|xV|P(19)|'+',   OC_UNARY|xV|P(19)|'-',   OC_UNARY|xV|P(19)|'!',
+       0, /* ] */
        0,
        0,
        0,
-       0,
-       ST_IF,          ST_DO,          ST_FOR,         OC_BREAK,
-       OC_CONTINUE,                    OC_DELETE|Vx,   OC_PRINT,
-       OC_PRINTF,      OC_NEXT,        OC_NEXTFILE,
-       OC_RETURN|Vx,   OC_EXIT|Nx,
+       0, /* \n */
+       ST_IF,        ST_DO,        ST_FOR,      OC_BREAK,
+       OC_CONTINUE,  OC_DELETE|Vx, OC_PRINT,
+       OC_PRINTF,    OC_NEXT,      OC_NEXTFILE,
+       OC_RETURN|Vx, OC_EXIT|Nx,
        ST_WHILE,
-       0,
+       0, /* else */
 
        OC_B|B_an|P(0x83), OC_B|B_co|P(0x41), OC_B|B_ls|P(0x83), OC_B|B_or|P(0x83),
        OC_B|B_rs|P(0x83), OC_B|B_xo|P(0x83),
@@ -372,9 +434,9 @@ static const uint32_t tokeninfo[] = {
        OC_B|B_ss|P(0x8f), OC_FBLTIN|F_ti,    OC_B|B_ti|P(0x0b), OC_B|B_mt|P(0x0b),
        OC_B|B_lo|P(0x49), OC_B|B_up|P(0x49),
        OC_GETLINE|SV|P(0),
-       0,      0,
+       0,                 0,
        0,
-       0
+       0 /* END */
 };
 
 /* internal variable names and their initial values       */
@@ -384,7 +446,7 @@ enum {
        ORS,        RS,         RT,         FILENAME,
        SUBSEP,     F0,         ARGIND,     ARGC,
        ARGV,       ERRNO,      FNR,        NR,
-       NF,         IGNORECASE, ENVIRON,    NUM_INTERNAL_VARS
+       NF,         IGNORECASE, ENVIRON,    NUM_INTERNAL_VARS
 };
 
 static const char vNames[] ALIGN1 =
@@ -429,13 +491,13 @@ struct globals {
        smallint nextrec;
        smallint nextfile;
        smallint is_f0_split;
+       smallint t_rollback;
 };
 struct globals2 {
        uint32_t t_info; /* often used */
        uint32_t t_tclass;
        char *t_string;
        int t_lineno;
-       int t_rollback;
 
        var *intvar[NUM_INTERNAL_VARS]; /* often used */
 
@@ -493,11 +555,11 @@ struct globals2 {
 #define nextrec      (G1.nextrec     )
 #define nextfile     (G1.nextfile    )
 #define is_f0_split  (G1.is_f0_split )
+#define t_rollback   (G1.t_rollback  )
 #define t_info       (G.t_info      )
 #define t_tclass     (G.t_tclass    )
 #define t_string     (G.t_string    )
 #define t_lineno     (G.t_lineno    )
-#define t_rollback   (G.t_rollback  )
 #define intvar       (G.intvar      )
 #define fsplitter    (G.fsplitter   )
 #define rsplitter    (G.rsplitter   )
@@ -528,9 +590,7 @@ static const char EMSG_TOO_FEW_ARGS[] ALIGN1 = "Too few arguments for builtin";
 static const char EMSG_NOT_ARRAY[] ALIGN1 = "Not an array";
 static const char EMSG_POSSIBLE_ERROR[] ALIGN1 = "Possible syntax error";
 static const char EMSG_UNDEF_FUNC[] ALIGN1 = "Call to undefined function";
-#if !ENABLE_FEATURE_AWK_LIBM
 static const char EMSG_NO_MATH[] ALIGN1 = "Math support is not compiled in";
-#endif
 
 static void zero_out_var(var *vp)
 {
@@ -688,11 +748,27 @@ static char nextchar(char **s)
        pps = *s;
        if (c == '\\')
                c = bb_process_escape_sequence((const char**)s);
-       if (c == '\\' && *s == pps)
-               c = *(*s)++;
+       /* Example awk statement:
+        * s = "abc\"def"
+        * we must treat \" as "
+        */
+       if (c == '\\' && *s == pps) { /* unrecognized \z? */
+               c = *(*s); /* yes, fetch z */
+               if (c)
+                       (*s)++; /* advance unless z = NUL */
+       }
        return c;
 }
 
+/* TODO: merge with strcpy_and_process_escape_sequences()?
+ */
+static void unescape_string_in_place(char *s1)
+{
+       char *s = s1;
+       while ((*s1 = nextchar(&s)) != '\0')
+               s1++;
+}
+
 static ALWAYS_INLINE int isalnum_(int c)
 {
        return (isalnum(c) || c == '_');
@@ -700,14 +776,25 @@ static ALWAYS_INLINE int isalnum_(int c)
 
 static double my_strtod(char **pp)
 {
-#if ENABLE_DESKTOP
-       if ((*pp)[0] == '0'
-        && ((((*pp)[1] | 0x20) == 'x') || isdigit((*pp)[1]))
-       ) {
-               return strtoull(*pp, pp, 0);
+       char *cp = *pp;
+       if (ENABLE_DESKTOP && cp[0] == '0') {
+               /* Might be hex or octal integer: 0x123abc or 07777 */
+               char c = (cp[1] | 0x20);
+               if (c == 'x' || isdigit(cp[1])) {
+                       unsigned long long ull = strtoull(cp, pp, 0);
+                       if (c == 'x')
+                               return ull;
+                       c = **pp;
+                       if (!isdigit(c) && c != '.')
+                               return ull;
+                       /* else: it may be a floating number. Examples:
+                        * 009.123 (*pp points to '9')
+                        * 000.123 (*pp points to '.')
+                        * fall through to strtod.
+                        */
+               }
        }
-#endif
-       return strtod(*pp, pp);
+       return strtod(cp, pp);
 }
 
 /* -------- working with variables (set/get/copy/etc) -------- */
@@ -817,17 +904,21 @@ static double getvar_i(var *v)
                v->number = 0;
                s = v->string;
                if (s && *s) {
+                       debug_printf_eval("getvar_i: '%s'->", s);
                        v->number = my_strtod(&s);
+                       debug_printf_eval("%f (s:'%s')\n", v->number, s);
                        if (v->type & VF_USER) {
                                s = skip_spaces(s);
                                if (*s != '\0')
                                        v->type &= ~VF_USER;
                        }
                } else {
+                       debug_printf_eval("getvar_i: '%s'->zero\n", s);
                        v->type &= ~VF_USER;
                }
                v->type |= VF_CACHED;
        }
+       debug_printf_eval("getvar_i: %f\n", v->number);
        return v->number;
 }
 
@@ -849,6 +940,7 @@ static var *copyvar(var *dest, const var *src)
        if (dest != src) {
                clrvar(dest);
                dest->type |= (src->type & ~(VF_DONTTOUCH | VF_FSTR));
+               debug_printf_eval("copyvar: number:%f string:'%s'\n", src->number, src->string);
                dest->number = src->number;
                if (src->string)
                        dest->string = xstrdup(src->string);
@@ -965,7 +1057,6 @@ static uint32_t next_token(uint32_t expected)
        const char *tl;
        uint32_t tc;
        const uint32_t *ti;
-       int l;
 
        if (t_rollback) {
                t_rollback = FALSE;
@@ -989,20 +1080,23 @@ static uint32_t next_token(uint32_t expected)
 
                if (*p == '\0') {
                        tc = TC_EOF;
+                       debug_printf_parse("%s: token found: TC_EOF\n", __func__);
 
                } else if (*p == '\"') {
                        /* it's a string */
                        t_string = s = ++p;
                        while (*p != '\"') {
-                               char *pp = p;
+                               char *pp;
                                if (*p == '\0' || *p == '\n')
                                        syntax_error(EMSG_UNEXP_EOS);
+                               pp = p;
                                *s++ = nextchar(&pp);
                                p = pp;
                        }
                        p++;
                        *s = '\0';
                        tc = TC_STRING;
+                       debug_printf_parse("%s: token found:'%s' TC_STRING\n", __func__, t_string);
 
                } else if ((expected & TC_REGEXP) && *p == '/') {
                        /* it's regexp */
@@ -1025,15 +1119,17 @@ static uint32_t next_token(uint32_t expected)
                        p++;
                        *s = '\0';
                        tc = TC_REGEXP;
+                       debug_printf_parse("%s: token found:'%s' TC_REGEXP\n", __func__, t_string);
 
                } else if (*p == '.' || isdigit(*p)) {
                        /* it's a number */
                        char *pp = p;
                        t_double = my_strtod(&pp);
                        p = pp;
-                       if (*pp == '.')
+                       if (*p == '.')
                                syntax_error(EMSG_UNEXP_TOKEN);
                        tc = TC_NUMBER;
+                       debug_printf_parse("%s: token found:%f TC_NUMBER\n", __func__, t_double);
 
                } else {
                        /* search for something known */
@@ -1041,53 +1137,56 @@ static uint32_t next_token(uint32_t expected)
                        tc = 0x00000001;
                        ti = tokeninfo;
                        while (*tl) {
-                               l = *tl++;
-                               if (l == NTCC) {
+                               int l = (unsigned char) *tl++;
+                               if (l == (unsigned char) NTCC) {
                                        tc <<= 1;
                                        continue;
                                }
-                               /* if token class is expected, token
-                                * matches and it's not a longer word,
-                                * then this is what we are looking for
+                               /* if token class is expected,
+                                * token matches,
+                                * and it's not a longer word,
                                 */
                                if ((tc & (expected | TC_WORD | TC_NEWLINE))
-                                && *tl == *p && strncmp(p, tl, l) == 0
+                                && strncmp(p, tl, l) == 0
                                 && !((tc & TC_WORD) && isalnum_(p[l]))
                                ) {
+                                       /* then this is what we are looking for */
                                        t_info = *ti;
+                                       debug_printf_parse("%s: token found:'%.*s' t_info:%x\n", __func__, l, p, t_info);
                                        p += l;
-                                       break;
+                                       goto token_found;
                                }
                                ti++;
                                tl += l;
                        }
-
-                       if (!*tl) {
-                               /* it's a name (var/array/function),
-                                * otherwise it's something wrong
-                                */
-                               if (!isalnum_(*p))
-                                       syntax_error(EMSG_UNEXP_TOKEN);
-
-                               t_string = --p;
-                               while (isalnum_(*++p)) {
-                                       p[-1] = *p;
-                               }
-                               p[-1] = '\0';
-                               tc = TC_VARIABLE;
-                               /* also consume whitespace between functionname and bracket */
-                               if (!(expected & TC_VARIABLE) || (expected & TC_ARRAY))
-                                       p = skip_spaces(p);
-                               if (*p == '(') {
-                                       tc = TC_FUNCTION;
-                               } else {
-                                       if (*p == '[') {
-                                               p++;
-                                               tc = TC_ARRAY;
-                                       }
-                               }
+                       /* not a known token */
+
+                       /* is it a name? (var/array/function) */
+                       if (!isalnum_(*p))
+                               syntax_error(EMSG_UNEXP_TOKEN); /* no */
+                       /* yes */
+                       t_string = --p;
+                       while (isalnum_(*++p)) {
+                               p[-1] = *p;
+                       }
+                       p[-1] = '\0';
+                       tc = TC_VARIABLE;
+                       /* also consume whitespace between functionname and bracket */
+                       if (!(expected & TC_VARIABLE) || (expected & TC_ARRAY))
+                               p = skip_spaces(p);
+                       if (*p == '(') {
+                               tc = TC_FUNCTION;
+                               debug_printf_parse("%s: token found:'%s' TC_FUNCTION\n", __func__, t_string);
+                       } else {
+                               if (*p == '[') {
+                                       p++;
+                                       tc = TC_ARRAY;
+                                       debug_printf_parse("%s: token found:'%s' TC_ARRAY\n", __func__, t_string);
+                               } else
+                                       debug_printf_parse("%s: token found:'%s' TC_VARIABLE\n", __func__, t_string);
                        }
                }
+ token_found:
                g_pos = p;
 
                /* skipping newlines in some cases */
@@ -1159,19 +1258,24 @@ static node *parse_expr(uint32_t iexp)
        uint32_t tc, xtc;
        var *v;
 
+       debug_printf_parse("%s(%x)\n", __func__, iexp);
+
        sn.info = PRIMASK;
        sn.r.n = glptr = NULL;
        xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP | iexp;
 
        while (!((tc = next_token(xtc)) & iexp)) {
+
                if (glptr && (t_info == (OC_COMPARE | VV | P(39) | 2))) {
                        /* input redirection (<) attached to glptr node */
+                       debug_printf_parse("%s: input redir\n", __func__);
                        cn = glptr->l.n = new_node(OC_CONCAT | SS | P(37));
                        cn->a.n = glptr;
                        xtc = TC_OPERAND | TC_UOPPRE;
                        glptr = NULL;
 
                } else if (tc & (TC_BINOP | TC_UOPPOST)) {
+                       debug_printf_parse("%s: TC_BINOP | TC_UOPPOST\n", __func__);
                        /* for binary and postfix-unary operators, jump back over
                         * previous operators with higher priority */
                        vn = cn;
@@ -1201,6 +1305,7 @@ static node *parse_expr(uint32_t iexp)
                        vn->a.n = cn;
 
                } else {
+                       debug_printf_parse("%s: other\n", __func__);
                        /* for operands and prefix-unary operators, attach them
                         * to last node */
                        vn = cn;
@@ -1208,12 +1313,14 @@ static node *parse_expr(uint32_t iexp)
                        cn->a.n = vn;
                        xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP;
                        if (tc & (TC_OPERAND | TC_REGEXP)) {
+                               debug_printf_parse("%s: TC_OPERAND | TC_REGEXP\n", __func__);
                                xtc = TC_UOPPRE | TC_UOPPOST | TC_BINOP | TC_OPERAND | iexp;
                                /* one should be very careful with switch on tclass -
                                 * only simple tclasses should be used! */
                                switch (tc) {
                                case TC_VARIABLE:
                                case TC_ARRAY:
+                                       debug_printf_parse("%s: TC_VARIABLE | TC_ARRAY\n", __func__);
                                        cn->info = OC_VAR;
                                        v = hash_search(ahash, t_string);
                                        if (v != NULL) {
@@ -1230,6 +1337,7 @@ static node *parse_expr(uint32_t iexp)
 
                                case TC_NUMBER:
                                case TC_STRING:
+                                       debug_printf_parse("%s: TC_NUMBER | TC_STRING\n", __func__);
                                        cn->info = OC_VAR;
                                        v = cn->l.v = xzalloc(sizeof(var));
                                        if (tc & TC_NUMBER)
@@ -1239,32 +1347,41 @@ static node *parse_expr(uint32_t iexp)
                                        break;
 
                                case TC_REGEXP:
+                                       debug_printf_parse("%s: TC_REGEXP\n", __func__);
                                        mk_re_node(t_string, cn, xzalloc(sizeof(regex_t)*2));
                                        break;
 
                                case TC_FUNCTION:
+                                       debug_printf_parse("%s: TC_FUNCTION\n", __func__);
                                        cn->info = OC_FUNC;
                                        cn->r.f = newfunc(t_string);
                                        cn->l.n = condition();
                                        break;
 
                                case TC_SEQSTART:
+                                       debug_printf_parse("%s: TC_SEQSTART\n", __func__);
                                        cn = vn->r.n = parse_expr(TC_SEQTERM);
+                                       if (!cn)
+                                               syntax_error("Empty sequence");
                                        cn->a.n = vn;
                                        break;
 
                                case TC_GETLINE:
+                                       debug_printf_parse("%s: TC_GETLINE\n", __func__);
                                        glptr = cn;
                                        xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
                                        break;
 
                                case TC_BUILTIN:
+                                       debug_printf_parse("%s: TC_BUILTIN\n", __func__);
                                        cn->l.n = condition();
                                        break;
                                }
                        }
                }
        }
+
+       debug_printf_parse("%s() returns %p\n", __func__, sn.r.n);
        return sn.r.n;
 }
 
@@ -1333,18 +1450,25 @@ static void chain_group(void)
        } while (c & TC_NEWLINE);
 
        if (c & TC_GRPSTART) {
+               debug_printf_parse("%s: TC_GRPSTART\n", __func__);
                while (next_token(TC_GRPSEQ | TC_GRPTERM) != TC_GRPTERM) {
+                       debug_printf_parse("%s: !TC_GRPTERM\n", __func__);
                        if (t_tclass & TC_NEWLINE)
                                continue;
                        rollback_token();
                        chain_group();
                }
+               debug_printf_parse("%s: TC_GRPTERM\n", __func__);
        } else if (c & (TC_OPSEQ | TC_OPTERM)) {
+               debug_printf_parse("%s: TC_OPSEQ | TC_OPTERM\n", __func__);
                rollback_token();
                chain_expr(OC_EXEC | Vx);
-       } else {                                                /* TC_STATEMNT */
+       } else {
+               /* TC_STATEMNT */
+               debug_printf_parse("%s: TC_STATEMNT(?)\n", __func__);
                switch (t_info & OPCLSMASK) {
                case ST_IF:
+                       debug_printf_parse("%s: ST_IF\n", __func__);
                        n = chain_node(OC_BR | Vx);
                        n->l.n = condition();
                        chain_group();
@@ -1359,12 +1483,14 @@ static void chain_group(void)
                        break;
 
                case ST_WHILE:
+                       debug_printf_parse("%s: ST_WHILE\n", __func__);
                        n2 = condition();
                        n = chain_loop(NULL);
                        n->l.n = n2;
                        break;
 
                case ST_DO:
+                       debug_printf_parse("%s: ST_DO\n", __func__);
                        n2 = chain_node(OC_EXEC);
                        n = chain_loop(NULL);
                        n2->a.n = n->a.n;
@@ -1373,6 +1499,7 @@ static void chain_group(void)
                        break;
 
                case ST_FOR:
+                       debug_printf_parse("%s: ST_FOR\n", __func__);
                        next_token(TC_SEQSTART);
                        n2 = parse_expr(TC_SEMICOL | TC_SEQTERM);
                        if (t_tclass & TC_SEQTERM) {    /* for-in */
@@ -1398,6 +1525,7 @@ static void chain_group(void)
 
                case OC_PRINT:
                case OC_PRINTF:
+                       debug_printf_parse("%s: OC_PRINT[F]\n", __func__);
                        n = chain_node(t_info);
                        n->l.n = parse_expr(TC_OPTERM | TC_OUTRDR | TC_GRPTERM);
                        if (t_tclass & TC_OUTRDR) {
@@ -1409,17 +1537,20 @@ static void chain_group(void)
                        break;
 
                case OC_BREAK:
+                       debug_printf_parse("%s: OC_BREAK\n", __func__);
                        n = chain_node(OC_EXEC);
                        n->a.n = break_ptr;
                        break;
 
                case OC_CONTINUE:
+                       debug_printf_parse("%s: OC_CONTINUE\n", __func__);
                        n = chain_node(OC_EXEC);
                        n->a.n = continue_ptr;
                        break;
 
                /* delete, next, nextfile, return, exit */
                default:
+                       debug_printf_parse("%s: default\n", __func__);
                        chain_expr(t_info);
                }
        }
@@ -1437,19 +1568,24 @@ static void parse_program(char *p)
        while ((tclass = next_token(TC_EOF | TC_OPSEQ | TC_GRPSTART |
                        TC_OPTERM | TC_BEGIN | TC_END | TC_FUNCDECL)) != TC_EOF) {
 
-               if (tclass & TC_OPTERM)
+               if (tclass & TC_OPTERM) {
+                       debug_printf_parse("%s: TC_OPTERM\n", __func__);
                        continue;
+               }
 
                seq = &mainseq;
                if (tclass & TC_BEGIN) {
+                       debug_printf_parse("%s: TC_BEGIN\n", __func__);
                        seq = &beginseq;
                        chain_group();
 
                } else if (tclass & TC_END) {
+                       debug_printf_parse("%s: TC_END\n", __func__);
                        seq = &endseq;
                        chain_group();
 
                } else if (tclass & TC_FUNCDECL) {
+                       debug_printf_parse("%s: TC_FUNCDECL\n", __func__);
                        next_token(TC_FUNCTION);
                        g_pos++;
                        f = newfunc(t_string);
@@ -1467,22 +1603,27 @@ static void parse_program(char *p)
                        clear_array(ahash);
 
                } else if (tclass & TC_OPSEQ) {
+                       debug_printf_parse("%s: TC_OPSEQ\n", __func__);
                        rollback_token();
                        cn = chain_node(OC_TEST);
                        cn->l.n = parse_expr(TC_OPTERM | TC_EOF | TC_GRPSTART);
                        if (t_tclass & TC_GRPSTART) {
+                               debug_printf_parse("%s: TC_GRPSTART\n", __func__);
                                rollback_token();
                                chain_group();
                        } else {
+                               debug_printf_parse("%s: !TC_GRPSTART\n", __func__);
                                chain_node(OC_PRINT);
                        }
                        cn->r.n = mainseq.last;
 
                } else /* if (tclass & TC_GRPSTART) */ {
+                       debug_printf_parse("%s: TC_GRPSTART(?)\n", __func__);
                        rollback_token();
                        chain_group();
                }
        }
+       debug_printf_parse("%s: TC_EOF\n", __func__);
 }
 
 
@@ -1500,10 +1641,10 @@ static node *mk_splitter(const char *s, tsplitter *spl)
                regfree(re);
                regfree(ire); // TODO: nuke ire, use re+1?
        }
-       if (strlen(s) > 1) {
+       if (s[0] && s[1]) { /* strlen(s) > 1 */
                mk_re_node(s, n, re);
        } else {
-               n->info = (uint32_t) *s;
+               n->info = (uint32_t) s[0];
        }
 
        return n;
@@ -1560,24 +1701,22 @@ static void fsrealloc(int size)
        if (size >= maxfields) {
                i = maxfields;
                maxfields = size + 16;
-               Fields = xrealloc(Fields, maxfields * sizeof(var));
+               Fields = xrealloc(Fields, maxfields * sizeof(Fields[0]));
                for (; i < maxfields; i++) {
                        Fields[i].type = VF_SPECIAL;
                        Fields[i].string = NULL;
                }
        }
-
-       if (size < nfields) {
-               for (i = size; i < nfields; i++) {
-                       clrvar(Fields + i);
-               }
+       /* if size < nfields, clear extra field variables */
+       for (i = size; i < nfields; i++) {
+               clrvar(Fields + i);
        }
        nfields = size;
 }
 
 static int awk_split(const char *s, node *spl, char **slist)
 {
-       int l, n = 0;
+       int l, n;
        char c[4];
        char *s1;
        regmatch_t pmatch[2]; // TODO: why [2]? [1] is enough...
@@ -1591,6 +1730,7 @@ static int awk_split(const char *s, node *spl, char **slist)
        if (*getvar_s(intvar[RS]) == '\0')
                c[2] = '\n';
 
+       n = 0;
        if ((spl->info & OPCLSMASK) == OC_REGEXP) {  /* regex split */
                if (!*s)
                        return n; /* "": zero fields */
@@ -1636,7 +1776,7 @@ static int awk_split(const char *s, node *spl, char **slist)
                }
                if (*s1)
                        n++;
-               while ((s1 = strpbrk(s1, c))) {
+               while ((s1 = strpbrk(s1, c)) != NULL) {
                        *s1++ = '\0';
                        n++;
                }
@@ -1724,6 +1864,18 @@ static void handle_special(var *v)
                is_f0_split = FALSE;
 
        } else if (v == intvar[FS]) {
+               /*
+                * The POSIX-2008 standard says that changing FS should have no effect on the
+                * current input line, but only on the next one. The language is:
+                *
+                * > Before the first reference to a field in the record is evaluated, the record
+                * > shall be split into fields, according to the rules in Regular Expressions,
+                * > using the value of FS that was current at the time the record was read.
+                *
+                * So, split up current line before assignment to FS:
+                */
+               split_f0();
+
                mk_splitter(getvar_s(v), &fsplitter);
 
        } else if (v == intvar[RS]) {
@@ -1816,6 +1968,8 @@ static int awk_getline(rstream *rsm, var *v)
        int fd, so, eo, r, rp;
        char c, *m, *s;
 
+       debug_printf_eval("entered %s()\n", __func__);
+
        /* we're using our own buffer since we need access to accumulating
         * characters
         */
@@ -1902,6 +2056,8 @@ static int awk_getline(rstream *rsm, var *v)
        rsm->pos = p - eo;
        rsm->size = size;
 
+       debug_printf_eval("returning from %s(): %d\n", __func__, r);
+
        return r;
 }
 
@@ -1911,8 +2067,8 @@ static int fmt_num(char *b, int size, const char *format, double n, int int_as_i
        char c;
        const char *s = format;
 
-       if (int_as_int && n == (int)n) {
-               r = snprintf(b, size, "%d", (int)n);
+       if (int_as_int && n == (long long)n) {
+               r = snprintf(b, size, "%lld", (long long)n);
        } else {
                do { c = *s; } while (c && *++s);
                if (strchr("diouxX", c)) {
@@ -2148,11 +2304,10 @@ static NOINLINE var *exec_builtin(node *op, var *res)
        switch (info) {
 
        case B_a2:
-#if ENABLE_FEATURE_AWK_LIBM
-               setvar_i(res, atan2(getvar_i(av[0]), getvar_i(av[1])));
-#else
-               syntax_error(EMSG_NO_MATH);
-#endif
+               if (ENABLE_FEATURE_AWK_LIBM)
+                       setvar_i(res, atan2(getvar_i(av[0]), getvar_i(av[1])));
+               else
+                       syntax_error(EMSG_NO_MATH);
                break;
 
        case B_sp: {
@@ -2320,13 +2475,15 @@ static var *evaluate(node *op, var *res)
 #define fnargs (G.evaluate__fnargs)
 /* seed is initialized to 1 */
 #define seed   (G.evaluate__seed)
-#define        sreg   (G.evaluate__sreg)
+#define sreg   (G.evaluate__sreg)
 
        var *v1;
 
        if (!op)
                return setvar_s(res, NULL);
 
+       debug_printf_eval("entered %s()\n", __func__);
+
        v1 = nvalloc(2);
 
        while (op) {
@@ -2347,19 +2504,27 @@ static var *evaluate(node *op, var *res)
                opn = (opinfo & OPNMASK);
                g_lineno = op->lineno;
                op1 = op->l.n;
+               debug_printf_eval("opinfo:%08x opn:%08x\n", opinfo, opn);
 
                /* execute inevitable things */
                if (opinfo & OF_RES1)
                        L.v = evaluate(op1, v1);
                if (opinfo & OF_RES2)
                        R.v = evaluate(op->r.n, v1+1);
-               if (opinfo & OF_STR1)
+               if (opinfo & OF_STR1) {
                        L.s = getvar_s(L.v);
-               if (opinfo & OF_STR2)
+                       debug_printf_eval("L.s:'%s'\n", L.s);
+               }
+               if (opinfo & OF_STR2) {
                        R.s = getvar_s(R.v);
-               if (opinfo & OF_NUM1)
+                       debug_printf_eval("R.s:'%s'\n", R.s);
+               }
+               if (opinfo & OF_NUM1) {
                        L_d = getvar_i(L.v);
+                       debug_printf_eval("L_d:%f\n", L_d);
+               }
 
+               debug_printf_eval("switch(0x%x)\n", XC(opinfo & OPCLSMASK));
                switch (XC(opinfo & OPCLSMASK)) {
 
                /* -- iterative node type -- */
@@ -2526,6 +2691,7 @@ static var *evaluate(node *op, var *res)
                        break;
 
                case XC( OC_MOVE ):
+                       debug_printf_eval("MOVE\n");
                        /* if source is a temporary string, jusk relink it to dest */
 //Disabled: if R.v is numeric but happens to have cached R.v->string,
 //then L.v ends up being a string, which is wrong
@@ -2547,7 +2713,8 @@ static var *evaluate(node *op, var *res)
                        var *vbeg, *v;
                        const char *sv_progname;
 
-                       if (!op->r.f->body.first)
+                       /* The body might be empty, still has to eval the args */
+                       if (!op->r.n->info && !op->r.f->body.first)
                                syntax_error(EMSG_UNDEF_FUNC);
 
                        vbeg = v = nvalloc(op->r.f->nargs + 1);
@@ -2585,7 +2752,7 @@ static var *evaluate(node *op, var *res)
                                                rsm->F = popen(L.s, "r");
                                                rsm->is_pipe = TRUE;
                                        } else {
-                                               rsm->F = fopen_for_read(L.s);           /* not xfopen! */
+                                               rsm->F = fopen_for_read(L.s);  /* not xfopen! */
                                        }
                                }
                        } else {
@@ -2594,7 +2761,7 @@ static var *evaluate(node *op, var *res)
                                rsm = iF;
                        }
 
-                       if (!rsm->F) {
+                       if (!rsm || !rsm->F) {
                                setvar_i(intvar[ERRNO], errno);
                                setvar_i(res, -1);
                                break;
@@ -2614,47 +2781,50 @@ static var *evaluate(node *op, var *res)
 
                /* simple builtins */
                case XC( OC_FBLTIN ): {
-                       int i;
-                       rstream *rsm;
                        double R_d = R_d; /* for compiler */
 
                        switch (opn) {
                        case F_in:
-                               R_d = (int)L_d;
+                               R_d = (long long)L_d;
                                break;
 
                        case F_rn:
                                R_d = (double)rand() / (double)RAND_MAX;
                                break;
-#if ENABLE_FEATURE_AWK_LIBM
+
                        case F_co:
-                               R_d = cos(L_d);
-                               break;
+                               if (ENABLE_FEATURE_AWK_LIBM) {
+                                       R_d = cos(L_d);
+                                       break;
+                               }
 
                        case F_ex:
-                               R_d = exp(L_d);
-                               break;
+                               if (ENABLE_FEATURE_AWK_LIBM) {
+                                       R_d = exp(L_d);
+                                       break;
+                               }
 
                        case F_lg:
-                               R_d = log(L_d);
-                               break;
+                               if (ENABLE_FEATURE_AWK_LIBM) {
+                                       R_d = log(L_d);
+                                       break;
+                               }
 
                        case F_si:
-                               R_d = sin(L_d);
-                               break;
+                               if (ENABLE_FEATURE_AWK_LIBM) {
+                                       R_d = sin(L_d);
+                                       break;
+                               }
 
                        case F_sq:
-                               R_d = sqrt(L_d);
-                               break;
-#else
-                       case F_co:
-                       case F_ex:
-                       case F_lg:
-                       case F_si:
-                       case F_sq:
+                               if (ENABLE_FEATURE_AWK_LIBM) {
+                                       R_d = sqrt(L_d);
+                                       break;
+                               }
+
                                syntax_error(EMSG_NO_MATH);
                                break;
-#endif
+
                        case F_sr:
                                R_d = (double)seed;
                                seed = op1 ? (unsigned)L_d : (unsigned)time(NULL);
@@ -2666,8 +2836,16 @@ static var *evaluate(node *op, var *res)
                                break;
 
                        case F_le:
-                               if (!op1)
+                               debug_printf_eval("length: L.s:'%s'\n", L.s);
+                               if (!op1) {
                                        L.s = getvar_s(intvar[F0]);
+                                       debug_printf_eval("length: L.s='%s'\n", L.s);
+                               }
+                               else if (L.v->type & VF_ARRAY) {
+                                       R_d = L.v->x.array->nel;
+                                       debug_printf_eval("length: array_len:%d\n", L.v->x.array->nel);
+                                       break;
+                               }
                                R_d = strlen(L.s);
                                break;
 
@@ -2681,26 +2859,37 @@ static var *evaluate(node *op, var *res)
                                if (!op1) {
                                        fflush(stdout);
                                } else if (L.s && *L.s) {
-                                       rsm = newfile(L.s);
+                                       rstream *rsm = newfile(L.s);
                                        fflush(rsm->F);
                                } else {
                                        fflush_all();
                                }
                                break;
 
-                       case F_cl:
-                               i = 0;
+                       case F_cl: {
+                               rstream *rsm;
+                               int err = 0;
                                rsm = (rstream *)hash_search(fdhash, L.s);
+                               debug_printf_eval("OC_FBLTIN F_cl rsm:%p\n", rsm);
                                if (rsm) {
-                                       i = rsm->is_pipe ? pclose(rsm->F) : fclose(rsm->F);
+                                       debug_printf_eval("OC_FBLTIN F_cl "
+                                               "rsm->is_pipe:%d, ->F:%p\n",
+                                               rsm->is_pipe, rsm->F);
+                                       /* Can be NULL if open failed. Example:
+                                        * getline line <"doesnt_exist";
+                                        * close("doesnt_exist"); <--- here rsm->F is NULL
+                                        */
+                                       if (rsm->F)
+                                               err = rsm->is_pipe ? pclose(rsm->F) : fclose(rsm->F);
                                        free(rsm->buffer);
                                        hash_remove(fdhash, L.s);
                                }
-                               if (i != 0)
+                               if (err)
                                        setvar_i(intvar[ERRNO], errno);
-                               R_d = (double)i;
+                               R_d = (double)err;
                                break;
                        }
+                       } /* switch */
                        setvar_i(res, R_d);
                        break;
                }
@@ -2777,6 +2966,7 @@ static var *evaluate(node *op, var *res)
                case XC( OC_BINARY ):
                case XC( OC_REPLACE ): {
                        double R_d = getvar_i(R.v);
+                       debug_printf_eval("BINARY/REPLACE: R_d:%f opn:%c\n", R_d, opn);
                        switch (opn) {
                        case '+':
                                L_d += R_d;
@@ -2793,18 +2983,18 @@ static var *evaluate(node *op, var *res)
                                L_d /= R_d;
                                break;
                        case '&':
-#if ENABLE_FEATURE_AWK_LIBM
-                               L_d = pow(L_d, R_d);
-#else
-                               syntax_error(EMSG_NO_MATH);
-#endif
+                               if (ENABLE_FEATURE_AWK_LIBM)
+                                       L_d = pow(L_d, R_d);
+                               else
+                                       syntax_error(EMSG_NO_MATH);
                                break;
                        case '%':
                                if (R_d == 0)
                                        syntax_error(EMSG_DIV_BY_ZERO);
-                               L_d -= (int)(L_d / R_d) * R_d;
+                               L_d -= (long long)(L_d / R_d) * R_d;
                                break;
                        }
+                       debug_printf_eval("BINARY/REPLACE result:%f\n", L_d);
                        res = setvar_i(((opinfo & OPCLSMASK) == OC_BINARY) ? res : L.v, L_d);
                        break;
                }
@@ -2847,6 +3037,7 @@ static var *evaluate(node *op, var *res)
        } /* while (op) */
 
        nvfree(v1);
+       debug_printf_eval("returning from %s(): %p\n", __func__, res);
        return res;
 #undef fnargs
 #undef seed
@@ -2887,21 +3078,18 @@ static int awk_exit(int r)
  * otherwise return 0 */
 static int is_assignment(const char *expr)
 {
-       char *exprc, *s, *s0, *s1;
+       char *exprc, *val;
 
-       exprc = xstrdup(expr);
-       if (!isalnum_(*exprc) || (s = strchr(exprc, '=')) == NULL) {
-               free(exprc);
+       if (!isalnum_(*expr) || (val = strchr(expr, '=')) == NULL) {
                return FALSE;
        }
 
-       *s++ = '\0';
-       s0 = s1 = s;
-       while (*s)
-               *s1++ = nextchar(&s);
+       exprc = xstrdup(expr);
+       val = exprc + (val - expr);
+       *val++ = '\0';
 
-       *s1 = '\0';
-       setvar_u(newvar(exprc), s0);
+       unescape_string_in_place(val);
+       setvar_u(newvar(exprc), val);
        free(exprc);
        return TRUE;
 }
@@ -2912,7 +3100,7 @@ static rstream *next_input_file(void)
 #define rsm          (G.next_input_file__rsm)
 #define files_happen (G.next_input_file__files_happen)
 
-       FILE *F = NULL;
+       FILE *F;
        const char *fname, *ind;
 
        if (rsm.F)
@@ -2920,19 +3108,21 @@ static rstream *next_input_file(void)
        rsm.F = NULL;
        rsm.pos = rsm.adv = 0;
 
-       do {
+       for (;;) {
                if (getvar_i(intvar[ARGIND])+1 >= getvar_i(intvar[ARGC])) {
                        if (files_happen)
                                return NULL;
                        fname = "-";
                        F = stdin;
-               } else {
-                       ind = getvar_s(incvar(intvar[ARGIND]));
-                       fname = getvar_s(findvar(iamarray(intvar[ARGV]), ind));
-                       if (fname && *fname && !is_assignment(fname))
-                               F = xfopen_stdin(fname);
+                       break;
                }
-       } while (!F);
+               ind = getvar_s(incvar(intvar[ARGIND]));
+               fname = getvar_s(findvar(iamarray(intvar[ARGV]), ind));
+               if (fname && *fname && !is_assignment(fname)) {
+                       F = xfopen_stdin(fname);
+                       break;
+               }
+       }
 
        files_happen = TRUE;
        setvar_s(intvar[FILENAME], fname);
@@ -2946,9 +3136,12 @@ int awk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int awk_main(int argc, char **argv)
 {
        unsigned opt;
-       char *opt_F, *opt_W;
+       char *opt_F;
        llist_t *list_v = NULL;
        llist_t *list_f = NULL;
+#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS
+       llist_t *list_e = NULL;
+#endif
        int i, j;
        var *v;
        var tv;
@@ -3007,45 +3200,51 @@ int awk_main(int argc, char **argv)
                        *s1 = '=';
                }
        }
-       opt_complementary = "v::f::"; /* -v and -f can occur multiple times */
-       opt = getopt32(argv, "F:v:f:W:", &opt_F, &list_v, &list_f, &opt_W);
+       opt_complementary = OPTCOMPLSTR_AWK;
+       opt = getopt32(argv, OPTSTR_AWK, &opt_F, &list_v, &list_f, IF_FEATURE_AWK_GNU_EXTENSIONS(&list_e,) NULL);
        argv += optind;
        argc -= optind;
-       if (opt & 0x1)
-               setvar_s(intvar[FS], opt_F); // -F
-       while (list_v) { /* -v */
+       if (opt & OPT_W)
+               bb_error_msg("warning: option -W is ignored");
+       if (opt & OPT_F) {
+               unescape_string_in_place(opt_F);
+               setvar_s(intvar[FS], opt_F);
+       }
+       while (list_v) {
                if (!is_assignment(llist_pop(&list_v)))
                        bb_show_usage();
        }
-       if (list_f) { /* -f */
-               do {
-                       char *s = NULL;
-                       FILE *from_file;
-
-                       g_progname = llist_pop(&list_f);
-                       from_file = xfopen_stdin(g_progname);
-                       /* one byte is reserved for some trick in next_token */
-                       for (i = j = 1; j > 0; i += j) {
-                               s = xrealloc(s, i + 4096);
-                               j = fread(s + i, 1, 4094, from_file);
-                       }
-                       s[i] = '\0';
-                       fclose(from_file);
-                       parse_program(s + 1);
-                       free(s);
-               } while (list_f);
-               argc++;
-       } else { // no -f: take program from 1st parameter
-               if (!argc)
+       while (list_f) {
+               char *s = NULL;
+               FILE *from_file;
+
+               g_progname = llist_pop(&list_f);
+               from_file = xfopen_stdin(g_progname);
+               /* one byte is reserved for some trick in next_token */
+               for (i = j = 1; j > 0; i += j) {
+                       s = xrealloc(s, i + 4096);
+                       j = fread(s + i, 1, 4094, from_file);
+               }
+               s[i] = '\0';
+               fclose(from_file);
+               parse_program(s + 1);
+               free(s);
+       }
+       g_progname = "cmd. line";
+#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS
+       while (list_e) {
+               parse_program(llist_pop(&list_e));
+       }
+#endif
+       if (!(opt & (OPT_f | OPT_e))) {
+               if (!*argv)
                        bb_show_usage();
-               g_progname = "cmd. line";
                parse_program(*argv++);
+               argc--;
        }
-       if (opt & 0x8) // -W
-               bb_error_msg("warning: unrecognized option '-W %s' ignored", opt_W);
 
        /* fill in ARGV array */
-       setvar_i(intvar[ARGC], argc);
+       setvar_i(intvar[ARGC], argc + 1);
        setari_u(intvar[ARGV], 0, "awk");
        i = 0;
        while (*argv)
index dbfa4be..a4af6f4 100644 (file)
@@ -4,12 +4,31 @@
  *
  * Copyright (C) 2000,2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 (virtually) compliant -- uses nicer GNU format for -l. */
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */
 
+//config:config CMP
+//config:      bool "cmp"
+//config:      default y
+//config:      help
+//config:        cmp is used to compare two files and returns the result
+//config:        to standard output.
+
+//kbuild:lib-$(CONFIG_CMP) += cmp.o
+
+//applet:IF_CMP(APPLET(cmp, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//usage:#define cmp_trivial_usage
+//usage:       "[-l] [-s] FILE1 [FILE2" IF_DESKTOP(" [SKIP1 [SKIP2]]") "]"
+//usage:#define cmp_full_usage "\n\n"
+//usage:       "Compare FILE1 with FILE2 (or stdin)\n"
+//usage:     "\n       -l      Write the byte numbers (decimal) and values (octal)"
+//usage:     "\n               for all differing bytes"
+//usage:     "\n       -s      Quiet"
+
 #include "libbb.h"
 
 static const char fmt_eof[] ALIGN1 = "cmp: EOF on %s\n";
@@ -33,8 +52,6 @@ int cmp_main(int argc UNUSED_PARAM, char **argv)
        unsigned opt;
        int retval = 0;
 
-       xfunc_error_retval = 2; /* 1 is returned if files are different. */
-
        opt_complementary = "-1"
                        IF_DESKTOP(":?4")
                        IF_NOT_DESKTOP(":?2")
@@ -43,8 +60,6 @@ int cmp_main(int argc UNUSED_PARAM, char **argv)
        argv += optind;
 
        filename1 = *argv;
-       fp1 = xfopen_stdin(filename1);
-
        if (*++argv) {
                filename2 = *argv;
                if (ENABLE_DESKTOP && *++argv) {
@@ -55,6 +70,10 @@ int cmp_main(int argc UNUSED_PARAM, char **argv)
                }
        }
 
+       xfunc_error_retval = 2;  /* missing file results in exitcode 2 */
+       if (opt & CMP_OPT_s)
+               logmode = 0;  /* -s suppresses open error messages */
+       fp1 = xfopen_stdin(filename1);
        fp2 = xfopen_stdin(filename2);
        if (fp1 == fp2) {               /* Paranoia check... stdin == stdin? */
                /* Note that we don't bother reading stdin.  Neither does gnu wc.
@@ -63,6 +82,7 @@ int cmp_main(int argc UNUSED_PARAM, char **argv)
                 */
                return 0;
        }
+       logmode = LOGMODE_STDIO;
 
        if (opt & CMP_OPT_l)
                fmt = fmt_l_opt;
index a3ca2b6..a78a0ee 100644 (file)
@@ -10,7 +10,7 @@
  * Agency (DARPA) and Air Force Research Laboratory, Air Force
  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*
  * 6n words for files of length n.
  */
 
+//config:config DIFF
+//config:      bool "diff"
+//config:      default y
+//config:      help
+//config:        diff compares two files or directories and outputs the
+//config:        differences between them in a form that can be given to
+//config:        the patch command.
+//config:
+//config:config FEATURE_DIFF_LONG_OPTIONS
+//config:      bool "Enable long options"
+//config:      default y
+//config:      depends on DIFF && LONG_OPTS
+//config:      help
+//config:        Enable use of long options.
+//config:
+//config:config FEATURE_DIFF_DIR
+//config:      bool "Enable directory support"
+//config:      default y
+//config:      depends on DIFF
+//config:      help
+//config:        This option enables support for directory and subdirectory
+//config:        comparison.
+
+//kbuild:lib-$(CONFIG_DIFF) += diff.o
+
+//applet:IF_DIFF(APPLET(diff, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//usage:#define diff_trivial_usage
+//usage:       "[-abBdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] FILE1 FILE2"
+//usage:#define diff_full_usage "\n\n"
+//usage:       "Compare files line by line and output the differences between them.\n"
+//usage:       "This implementation supports unified diffs only.\n"
+//usage:     "\n       -a      Treat all files as text"
+//usage:     "\n       -b      Ignore changes in the amount of whitespace"
+//usage:     "\n       -B      Ignore changes whose lines are all blank"
+//usage:     "\n       -d      Try hard to find a smaller set of changes"
+//usage:     "\n       -i      Ignore case differences"
+//usage:     "\n       -L      Use LABEL instead of the filename in the unified header"
+//usage:     "\n       -N      Treat absent files as empty"
+//usage:     "\n       -q      Output only whether files differ"
+//usage:     "\n       -r      Recurse"
+//usage:     "\n       -S      Start with FILE when comparing directories"
+//usage:     "\n       -T      Make tabs line up by prefixing a tab when necessary"
+//usage:     "\n       -s      Report when two files are the same"
+//usage:     "\n       -t      Expand tabs to spaces in output"
+//usage:     "\n       -U      Output LINES lines of context"
+//usage:     "\n       -w      Ignore all whitespace"
+
 #include "libbb.h"
 
 #if 0
-//#define dbg_error_msg(...) bb_error_msg(__VA_ARGS__)
+define dbg_error_msg(...) bb_error_msg(__VA_ARGS__)
 #else
-#define dbg_error_msg(...) ((void)0)
+# define dbg_error_msg(...) ((void)0)
 #endif
 
 enum {                  /* print_status() and diffreg() return values */
@@ -230,7 +278,7 @@ static int search(const int *c, int k, int y, const struct cand *list)
 {
        int i, j;
 
-       if (list[c[k]].y < y)   /* quick look for typical case */
+       if (list[c[k]].y < y)  /* quick look for typical case */
                return k + 1;
 
        for (i = 0, j = k + 1;;) {
@@ -479,7 +527,7 @@ start:
        for (; suff < nlen[0] - pref && suff < nlen[1] - pref &&
               nfile[0][nlen[0] - suff].value == nfile[1][nlen[1] - suff].value;
               suff++);
-       /* Arrays are pruned by the suffix and prefix lenght,
+       /* Arrays are pruned by the suffix and prefix length,
         * the result being sorted and stored in sfile[fileno],
         * and their sizes are stored in slen[fileno]
         */
@@ -672,10 +720,12 @@ static bool diff(FILE* fp[2], char *file[2])
 
 static int diffreg(char *file[2])
 {
-       FILE *fp[2] = { stdin, stdin };
+       FILE *fp[2];
        bool binary = false, differ = false;
        int status = STATUS_SAME, i;
 
+       fp[0] = stdin;
+       fp[1] = stdin;
        for (i = 0; i < 2; i++) {
                int fd = open_or_warn_stdin(file[i]);
                if (fd == -1)
@@ -685,9 +735,8 @@ static int diffreg(char *file[2])
                 */
                if (lseek(fd, 0, SEEK_SET) == -1 && errno == ESPIPE) {
                        char name[] = "/tmp/difXXXXXX";
-                       int fd_tmp = mkstemp(name);
-                       if (fd_tmp < 0)
-                               bb_perror_msg_and_die("mkstemp");
+                       int fd_tmp = xmkstemp(name);
+
                        unlink(name);
                        if (bb_copyfd_eof(fd, fd_tmp) < 0)
                                xfunc_die();
@@ -795,7 +844,9 @@ static int FAST_FUNC skip_dir(const char *filename,
                        free(othername);
                        if (r != 0 || !S_ISDIR(osb.st_mode)) {
                                /* other dir doesn't have similarly named
-                                * directory, don't recurse */
+                                * directory, don't recurse; return 1 upon
+                                * exit, just like diffutils' diff */
+                               exit_status |= 1;
                                return SKIP;
                        }
                }
@@ -819,7 +870,7 @@ static void diffdir(char *p[2], const char *s_start)
                 * add_to_dirlist will remove it. */
                list[i].len = strlen(p[i]);
                recursive_action(p[i], ACTION_RECURSE | ACTION_FOLLOWLINKS,
-                                add_to_dirlist, skip_dir, &list[i], 0);
+                               add_to_dirlist, skip_dir, &list[i], 0);
                /* Sort dl alphabetically.
                 * GNU diff does this ignoring any number of trailing dots.
                 * We don't, so for us dotted files almost always are
@@ -847,9 +898,10 @@ static void diffdir(char *p[2], const char *s_start)
                        break;
                pos = !dp[0] ? 1 : (!dp[1] ? -1 : strcmp(dp[0], dp[1]));
                k = pos > 0;
-               if (pos && !(option_mask32 & FLAG(N)))
+               if (pos && !(option_mask32 & FLAG(N))) {
                        printf("Only in %s: %s\n", p[k], dp[k]);
-               else {
+                       exit_status |= 1;
+               } else {
                        char *fullpath[2], *path[2]; /* if -N */
 
                        for (i = 0; i < 2; i++) {
@@ -950,6 +1002,31 @@ int diff_main(int argc UNUSED_PARAM, char **argv)
        if (gotstdin && (S_ISDIR(stb[0].st_mode) || S_ISDIR(stb[1].st_mode)))
                bb_error_msg_and_die("can't compare stdin to a directory");
 
+       /* Compare metadata to check if the files are the same physical file.
+        *
+        * Comment from diffutils source says:
+        * POSIX says that two files are identical if st_ino and st_dev are
+        * the same, but many file systems incorrectly assign the same (device,
+        * inode) pair to two distinct files, including:
+        * GNU/Linux NFS servers that export all local file systems as a
+        * single NFS file system, if a local device number (st_dev) exceeds
+        * 255, or if a local inode number (st_ino) exceeds 16777215.
+        */
+       if (ENABLE_DESKTOP
+        && stb[0].st_ino == stb[1].st_ino
+        && stb[0].st_dev == stb[1].st_dev
+        && stb[0].st_size == stb[1].st_size
+        && stb[0].st_mtime == stb[1].st_mtime
+        && stb[0].st_ctime == stb[1].st_ctime
+        && stb[0].st_mode == stb[1].st_mode
+        && stb[0].st_nlink == stb[1].st_nlink
+        && stb[0].st_uid == stb[1].st_uid
+        && stb[0].st_gid == stb[1].st_gid
+       ) {
+               /* files are physically the same; no need to compare them */
+               return STATUS_SAME;
+       }
+
        if (S_ISDIR(stb[0].st_mode) && S_ISDIR(stb[1].st_mode)) {
 #if ENABLE_FEATURE_DIFF_DIR
                diffdir(file, s_start);
index 516b8d7..3087fb0 100644 (file)
@@ -7,6 +7,21 @@
  * The "ed" built-in command (much simplified)
  */
 
+//config:config ED
+//config:      bool "ed"
+//config:      default y
+//config:      help
+//config:        The original 1970's Unix text editor, from the days of teletypes.
+//config:        Small, simple, evil. Part of SUSv3. If you're not already using
+//config:        this, you don't need it.
+
+//kbuild:lib-$(CONFIG_ED) += ed.o
+
+//applet:IF_ED(APPLET(ed, BB_DIR_BIN, BB_SUID_DROP))
+
+//usage:#define ed_trivial_usage ""
+//usage:#define ed_full_usage ""
+
 #include "libbb.h"
 
 typedef struct LINE {
@@ -129,7 +144,7 @@ static void doCommands(void)
                 * 0  on ctrl-C,
                 * >0 length of input string, including terminating '\n'
                 */
-               len = read_line_input(": ", buf, sizeof(buf), NULL);
+               len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1);
                if (len <= 0)
                        return;
                endbuf = &buf[len - 1];
@@ -227,7 +242,7 @@ static void doCommands(void)
                        }
                        if (!dirty)
                                return;
-                       len = read_line_input("Really quit? ", buf, 16, NULL);
+                       len = read_line_input(NULL, "Really quit? ", buf, 16, /*timeout*/ -1);
                        /* read error/EOF - no way to continue */
                        if (len < 0)
                                return;
@@ -451,7 +466,7 @@ static void subCommand(const char *cmd, int num1, int num2)
 
                /*
                 * The new string is larger, so allocate a new line
-                * structure and use that.  Link it in in place of
+                * structure and use that.  Link it in place of
                 * the old line structure.
                 */
                nlp = xmalloc(sizeof(LINE) + lp->len + deltaLen);
@@ -541,7 +556,7 @@ static void addLines(int num)
                 * 0  on ctrl-C,
                 * >0 length of input string, including terminating '\n'
                 */
-               len = read_line_input("", buf, sizeof(buf), NULL);
+               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? */
@@ -671,7 +686,7 @@ static int readLines(const char *file, int num)
 
        fd = open(file, 0);
        if (fd < 0) {
-               perror(file);
+               bb_simple_perror_msg(file);
                return FALSE;
        }
 
@@ -721,7 +736,7 @@ static int readLines(const char *file, int num)
        } while (cc > 0);
 
        if (cc < 0) {
-               perror(file);
+               bb_simple_perror_msg(file);
                close(fd);
                return FALSE;
        }
@@ -761,7 +776,7 @@ static int writeLines(const char *file, int num1, int num2)
 
        fd = creat(file, 0666);
        if (fd < 0) {
-               perror(file);
+               bb_simple_perror_msg(file);
                return FALSE;
        }
 
@@ -776,7 +791,7 @@ static int writeLines(const char *file, int num1, int num2)
 
        while (num1++ <= num2) {
                if (full_write(fd, lp->data, lp->len) != lp->len) {
-                       perror(file);
+                       bb_simple_perror_msg(file);
                        close(fd);
                        return FALSE;
                }
@@ -786,7 +801,7 @@ static int writeLines(const char *file, int num1, int num2)
        }
 
        if (close(fd) < 0) {
-               perror(file);
+               bb_simple_perror_msg(file);
                return FALSE;
        }
 
index 62477af..13785ef 100644 (file)
-/* vi: set sw=4 ts=4: */
-/*
- *  busybox patch applet to handle the unified diff format.
- *  Copyright (C) 2003 Glenn McGrath
+/* vi: set sw=4 ts=4:
  *
- *  Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Apply a "universal" diff.
+ * Adapted from toybox's patch implementation.
  *
- *  This applet is written to work with patches generated by GNU diff,
- *  where there is equivalent functionality busybox patch shall behave
- *  as per GNU patch.
+ * Copyright 2007 Rob Landley <rob@landley.net>
  *
- *  There is a SUSv3 specification for patch, however it looks to be
- *  incomplete, it doesnt even mention unified diff format.
- *  http://www.opengroup.org/onlinepubs/007904975/utilities/patch.html
+ * see http://www.opengroup.org/onlinepubs/009695399/utilities/patch.html
+ * (But only does -u, because who still cares about "ed"?)
  *
- *  Issues
- *   - Non-interactive
- *   - Patches must apply cleanly or patch (not just one hunk) will fail.
- *   - Reject file isnt saved
+ * TODO:
+ * -b backup
+ * -l treat all whitespace as a single space
+ * -d chdir first
+ * -D define wrap #ifdef and #ifndef around changes
+ * -o outfile output here instead of in place
+ * -r rejectfile write rejected hunks to this file
+ * --dry-run (regression!)
+ *
+ * -f force (no questions asked)
+ * -F fuzz (number, default 2)
+ * [file] which file to patch
  */
 
+//config:config PATCH
+//config:      bool "patch"
+//config:      default y
+//config:      help
+//config:        Apply a unified diff formatted patch.
+
+//applet:IF_PATCH(APPLET(patch, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_PATCH) += patch.o
+
+//usage:#define patch_trivial_usage
+//usage:       "[OPTIONS] [ORIGFILE [PATCHFILE]]"
+//usage:#define patch_full_usage "\n\n"
+//usage:       IF_LONG_OPTS(
+//usage:       "       -p,--strip N            Strip N leading components from file names"
+//usage:     "\n       -i,--input DIFF         Read DIFF instead of stdin"
+//usage:     "\n       -R,--reverse            Reverse patch"
+//usage:     "\n       -N,--forward            Ignore already applied patches"
+/*usage:     "\n       --dry-run               Don't actually change files" - TODO */
+//usage:     "\n       -E,--remove-empty-files Remove output files if they become empty"
+//usage:       )
+//usage:       IF_NOT_LONG_OPTS(
+//usage:       "       -p N    Strip N leading components from file names"
+//usage:     "\n       -i DIFF Read DIFF instead of stdin"
+//usage:     "\n       -R      Reverse patch"
+//usage:     "\n       -N      Ignore already applied patches"
+//usage:     "\n       -E      Remove output files if they become empty"
+//usage:       )
+/* -u "interpret as unified diff" is supported but not documented: this info is not useful for --help */
+/* -x "debug" is supported but does nothing */
+//usage:
+//usage:#define patch_example_usage
+//usage:       "$ patch -p1 < example.diff\n"
+//usage:       "$ patch -p0 -i example.diff"
+
 #include "libbb.h"
 
-static unsigned copy_lines(FILE *src_stream, FILE *dst_stream, unsigned lines_count)
+
+// libbb candidate?
+
+struct double_list {
+       struct double_list *next;
+       struct double_list *prev;
+       char *data;
+};
+
+// Free all the elements of a linked list
+// Call freeit() on each element before freeing it.
+static void dlist_free(struct double_list *list, void (*freeit)(void *data))
 {
-       while (src_stream && lines_count) {
-               char *line;
-               line = xmalloc_fgets(src_stream);
-               if (line == NULL) {
-                       break;
-               }
-               if (fputs(line, dst_stream) == EOF) {
-                       bb_perror_msg_and_die("error writing to new file");
+       while (list) {
+               void *pop = list;
+               list = list->next;
+               freeit(pop);
+               // Bail out also if list is circular.
+               if (list == pop) break;
+       }
+}
+
+// Add an entry before "list" element in (circular) doubly linked list
+static struct double_list *dlist_add(struct double_list **list, char *data)
+{
+       struct double_list *llist;
+       struct double_list *line = xmalloc(sizeof(*line));
+
+       line->data = data;
+       llist = *list;
+       if (llist) {
+               struct double_list *p;
+               line->next = llist;
+               p = line->prev = llist->prev;
+               // (list is circular, we assume p is never NULL)
+               p->next = line;
+               llist->prev = line;
+       } else
+               *list = line->next = line->prev = line;
+
+       return line;
+}
+
+
+struct globals {
+       char *infile;
+       long prefix;
+
+       struct double_list *current_hunk;
+
+       long oldline, oldlen, newline, newlen;
+       long linenum;
+       int context, state, hunknum;
+       int filein, fileout;
+       char *tempname;
+
+       int exitval;
+};
+#define TT (*ptr_to_globals)
+#define INIT_TT() do { \
+       SET_PTR_TO_GLOBALS(xzalloc(sizeof(TT))); \
+} while (0)
+
+
+#define FLAG_STR "Rup:i:NEx"
+/* FLAG_REVERSE must be == 1! Code uses this fact. */
+#define FLAG_REVERSE (1 << 0)
+#define FLAG_u       (1 << 1)
+#define FLAG_PATHLEN (1 << 2)
+#define FLAG_INPUT   (1 << 3)
+#define FLAG_IGNORE  (1 << 4)
+#define FLAG_RMEMPTY (1 << 5)
+/* Enable this bit and use -x for debug output: */
+#define FLAG_DEBUG   (0 << 6)
+
+// Dispose of a line of input, either by writing it out or discarding it.
+
+// state < 2: just free
+// state = 2: write whole line to stderr
+// state = 3: write whole line to fileout
+// state > 3: write line+1 to fileout when *line != state
+
+#define PATCH_DEBUG (option_mask32 & FLAG_DEBUG)
+
+static void do_line(void *data)
+{
+       struct double_list *dlist = data;
+
+       if (TT.state>1 && *dlist->data != TT.state)
+               fdprintf(TT.state == 2 ? 2 : TT.fileout,
+                       "%s\n", dlist->data+(TT.state>3 ? 1 : 0));
+
+       if (PATCH_DEBUG) fdprintf(2, "DO %d: %s\n", TT.state, dlist->data);
+
+       free(dlist->data);
+       free(dlist);
+}
+
+static void finish_oldfile(void)
+{
+       if (TT.tempname) {
+               // Copy the rest of the data and replace the original with the copy.
+               char *temp;
+
+               if (TT.filein != -1) {
+                       bb_copyfd_eof(TT.filein, TT.fileout);
+                       xclose(TT.filein);
                }
-               free(line);
-               lines_count--;
+               xclose(TT.fileout);
+
+               temp = xstrdup(TT.tempname);
+               temp[strlen(temp) - 6] = '\0';
+               rename(TT.tempname, temp);
+               free(temp);
+
+               free(TT.tempname);
+               TT.tempname = NULL;
        }
-       return lines_count;
+       TT.fileout = TT.filein = -1;
 }
 
-/* If patch_level is -1 it will remove all directory names
- * char *line must be greater than 4 chars
- * returns NULL if the file doesnt exist or error
- * returns malloc'ed filename
- * NB: frees 1st argument!
- */
-static char *extract_filename(char *line, int patch_level, const char *pat)
+static void fail_hunk(void)
+{
+       if (!TT.current_hunk) return;
+
+       fdprintf(2, "Hunk %d FAILED %ld/%ld.\n", TT.hunknum, TT.oldline, TT.newline);
+       TT.exitval = 1;
+
+       // If we got to this point, we've seeked to the end.  Discard changes to
+       // this file and advance to next file.
+
+       TT.state = 2;
+       TT.current_hunk->prev->next = NULL;
+       dlist_free(TT.current_hunk, do_line);
+       TT.current_hunk = NULL;
+
+       // Abort the copy and delete the temporary file.
+       close(TT.filein);
+       close(TT.fileout);
+       unlink(TT.tempname);
+       free(TT.tempname);
+       TT.tempname = NULL;
+
+       TT.state = 0;
+}
+
+// Given a hunk of a unified diff, make the appropriate change to the file.
+// This does not use the location information, but instead treats a hunk
+// as a sort of regex.  Copies data from input to output until it finds
+// the change to be made, then outputs the changed data and returns.
+// (Finding EOF first is an error.)  This is a single pass operation, so
+// multiple hunks must occur in order in the file.
+
+static int apply_one_hunk(void)
 {
-       char *temp = NULL, *filename_start_ptr = line + 4;
-
-       if (strncmp(line, pat, 4) == 0) {
-               /* Terminate string at end of source filename */
-               line[strcspn(line, "\t\n\r")] = '\0';
-
-               /* Skip over (patch_level) number of leading directories */
-               while (patch_level--) {
-                       temp = strchr(filename_start_ptr, '/');
-                       if (!temp)
-                               break;
-                       filename_start_ptr = temp + 1;
+       struct double_list *plist, *buf = NULL, *check;
+       int matcheof = 0, reverse = option_mask32 & FLAG_REVERSE, backwarn = 0;
+       /* Do we try "dummy" revert to check whether
+        * to silently skip this hunk? Used to implement -N.
+        */
+       int dummy_revert = 0;
+
+       // Break doubly linked list so we can use singly linked traversal function.
+       TT.current_hunk->prev->next = NULL;
+
+       // Match EOF if there aren't as many ending context lines as beginning
+       for (plist = TT.current_hunk; plist; plist = plist->next) {
+               if (plist->data[0]==' ') matcheof++;
+               else matcheof = 0;
+               if (PATCH_DEBUG) fdprintf(2, "HUNK:%s\n", plist->data);
+       }
+       matcheof = !matcheof || matcheof < TT.context;
+
+       if (PATCH_DEBUG) fdprintf(2,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N');
+
+       // Loop through input data searching for this hunk.  Match all context
+       // lines and all lines to be removed until we've found the end of a
+       // complete hunk.
+       plist = TT.current_hunk;
+       buf = NULL;
+       if (reverse ? TT.oldlen : TT.newlen) for (;;) {
+               char *data = xmalloc_reads(TT.filein, NULL);
+
+               TT.linenum++;
+
+               // Figure out which line of hunk to compare with next.  (Skip lines
+               // of the hunk we'd be adding.)
+               while (plist && *plist->data == "+-"[reverse]) {
+                       if (data && !strcmp(data, plist->data+1)) {
+                               if (!backwarn) {
+                                       backwarn = TT.linenum;
+                                       if (option_mask32 & FLAG_IGNORE) {
+                                               dummy_revert = 1;
+                                               reverse ^= 1;
+                                               continue;
+                                       }
+                               }
+                       }
+                       plist = plist->next;
+               }
+
+               // Is this EOF?
+               if (!data) {
+                       if (PATCH_DEBUG) fdprintf(2, "INEOF\n");
+
+                       // Does this hunk need to match EOF?
+                       if (!plist && matcheof) break;
+
+                       if (backwarn)
+                               fdprintf(2,"Possibly reversed hunk %d at %ld\n",
+                                       TT.hunknum, TT.linenum);
+
+                       // File ended before we found a place for this hunk.
+                       fail_hunk();
+                       goto done;
+               }
+
+               if (PATCH_DEBUG) fdprintf(2, "IN: %s\n", data);
+               check = dlist_add(&buf, data);
+
+               // Compare this line with next expected line of hunk.
+               // todo: teach the strcmp() to ignore whitespace.
+
+               // A match can fail because the next line doesn't match, or because
+               // we hit the end of a hunk that needed EOF, and this isn't EOF.
+
+               // If match failed, flush first line of buffered data and
+               // recheck buffered data for a new match until we find one or run
+               // out of buffer.
+
+               for (;;) {
+                       if (!plist || strcmp(check->data, plist->data+1)) {
+                               // Match failed.  Write out first line of buffered data and
+                               // recheck remaining buffered data for a new match.
+
+                               if (PATCH_DEBUG)
+                                       fdprintf(2, "NOT: %s\n", plist->data);
+
+                               TT.state = 3;
+                               check = buf;
+                               buf = buf->next;
+                               check->prev->next = buf;
+                               buf->prev = check->prev;
+                               do_line(check);
+                               plist = TT.current_hunk;
+
+                               // If we've reached the end of the buffer without confirming a
+                               // match, read more lines.
+                               if (check == buf) {
+                                       buf = NULL;
+                                       break;
+                               }
+                               check = buf;
+                       } else {
+                               if (PATCH_DEBUG)
+                                       fdprintf(2, "MAYBE: %s\n", plist->data);
+                               // This line matches.  Advance plist, detect successful match.
+                               plist = plist->next;
+                               if (!plist && !matcheof) goto out;
+                               check = check->next;
+                               if (check == buf) break;
+                       }
                }
-               temp = xstrdup(filename_start_ptr);
        }
-       free(line);
-       return temp;
+out:
+       // We have a match.  Emit changed data.
+       TT.state = "-+"[reverse ^ dummy_revert];
+       dlist_free(TT.current_hunk, do_line);
+       TT.current_hunk = NULL;
+       TT.state = 1;
+done:
+       if (buf) {
+               buf->prev->next = NULL;
+               dlist_free(buf, do_line);
+       }
+
+       return TT.state;
 }
 
+// Read a patch file and find hunks, opening/creating/deleting files.
+// Call apply_one_hunk() on each hunk.
+
+// state 0: Not in a hunk, look for +++.
+// state 1: Found +++ file indicator, look for @@
+// state 2: In hunk: counting initial context lines
+// state 3: In hunk: getting body
+
 int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int patch_main(int argc UNUSED_PARAM, char **argv)
 {
-       struct stat saved_stat;
-       char *patch_line;
-       FILE *patch_file;
-       int patch_level;
-       int ret = 0;
-       char plus = '+';
-       unsigned opt;
-       enum {
-               OPT_R = (1 << 2),
-               OPT_N = (1 << 3),
-               /*OPT_f = (1 << 4), ignored */
-               /*OPT_E = (1 << 5), ignored, this is the default */
-               /*OPT_g = (1 << 6), ignored */
-               OPT_dry_run = (1 << 7) * ENABLE_LONG_OPTS,
-       };
-
-       xfunc_error_retval = 2;
-       {
-               const char *p = "-1";
-               const char *i = "-"; /* compat */
-#if ENABLE_LONG_OPTS
-               static const char patch_longopts[] ALIGN1 =
-                       "strip\0"                 Required_argument "p"
-                       "input\0"                 Required_argument "i"
-                       "reverse\0"               No_argument       "R"
-                       "forward\0"               No_argument       "N"
-               /* "Assume user knows what [s]he is doing, do not ask any questions": */
-                       "force\0"                 No_argument       "f" /*ignored*/
-# if ENABLE_DESKTOP
-                       "remove-empty-files\0"    No_argument       "E" /*ignored*/
-               /* "Controls actions when a file is under RCS or SCCS control,
-                * and does not exist or is read-only and matches the default version,
-                * or when a file is under ClearCase control and does not exist..."
-                * IOW: rather obscure option.
-                * But Gentoo's portage does use -g0 */
-                       "get\0"                   Required_argument "g" /*ignored*/
-# endif
-                       "dry-run\0"               No_argument       "\xfd"
-# if ENABLE_DESKTOP
-                       "backup-if-mismatch\0"    No_argument       "\xfe" /*ignored*/
-                       "no-backup-if-mismatch\0" No_argument       "\xff" /*ignored*/
-# endif
-                       ;
-               applet_long_options = patch_longopts;
-#endif
-               /* -f,-E,-g are ignored */
-               opt = getopt32(argv, "p:i:RN""fEg:", &p, &i, NULL);
-               if (opt & OPT_R)
-                       plus = '-';
-               patch_level = xatoi(p); /* can be negative! */
-               patch_file = xfopen_stdin(i);
+       int opts;
+       int reverse, state = 0;
+       char *oldname = NULL, *newname = NULL;
+       char *opt_p, *opt_i;
+       long oldlen = oldlen; /* for compiler */
+       long newlen = newlen; /* for compiler */
+
+       INIT_TT();
+
+       opts = getopt32(argv, FLAG_STR, &opt_p, &opt_i);
+       argv += optind;
+       reverse = opts & FLAG_REVERSE;
+       TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative!
+       TT.filein = TT.fileout = -1;
+       if (opts & FLAG_INPUT) {
+               xmove_fd(xopen_stdin(opt_i), STDIN_FILENO);
+       } else {
+               if (argv[0] && argv[1]) {
+                       xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO);
+               }
+       }
+       if (argv[0]) {
+               oldname = xstrdup(argv[0]);
+               newname = xstrdup(argv[0]);
        }
 
-       patch_line = xmalloc_fgetline(patch_file);
-       while (patch_line) {
-               FILE *src_stream;
-               FILE *dst_stream;
-               //char *old_filename;
-               char *new_filename;
-               char *backup_filename = NULL;
-               unsigned src_cur_line = 1;
-               unsigned dst_cur_line = 0;
-               unsigned dst_beg_line;
-               unsigned bad_hunk_count = 0;
-               unsigned hunk_count = 0;
-               smallint copy_trailing_lines_flag = 0;
-
-               /* Skip everything upto the "---" marker
-                * No need to parse the lines "Only in <dir>", and "diff <args>"
-                */
-               do {
-                       /* Extract the filename used before the patch was generated */
-                       new_filename = extract_filename(patch_line, patch_level, "--- ");
-                       // was old_filename above
-                       patch_line = xmalloc_fgetline(patch_file);
-                       if (!patch_line) goto quit;
-               } while (!new_filename);
-               free(new_filename); // "source" filename is irrelevant
-
-               new_filename = extract_filename(patch_line, patch_level, "+++ ");
-               if (!new_filename) {
-                       bb_error_msg_and_die("invalid patch");
+       // Loop through the lines in the patch
+       for(;;) {
+               char *patchline;
+
+               patchline = xmalloc_fgetline(stdin);
+               if (!patchline) break;
+
+               // Other versions of patch accept damaged patches,
+               // so we need to also.
+               if (!*patchline) {
+                       free(patchline);
+                       patchline = xstrdup(" ");
                }
 
-               /* Get access rights from the file to be patched */
-               if (stat(new_filename, &saved_stat) != 0) {
-                       char *slash = strrchr(new_filename, '/');
-                       if (slash) {
-                               /* Create leading directories */
-                               *slash = '\0';
-                               bb_make_directory(new_filename, -1, FILEUTILS_RECUR);
-                               *slash = '/';
+               // Are we assembling a hunk?
+               if (state >= 2) {
+                       if (*patchline==' ' || *patchline=='+' || *patchline=='-') {
+                               dlist_add(&TT.current_hunk, patchline);
+
+                               if (*patchline != '+') oldlen--;
+                               if (*patchline != '-') newlen--;
+
+                               // Context line?
+                               if (*patchline==' ' && state==2) TT.context++;
+                               else state=3;
+
+                               // If we've consumed all expected hunk lines, apply the hunk.
+
+                               if (!oldlen && !newlen) state = apply_one_hunk();
+                               continue;
                        }
-                       src_stream = NULL;
-                       saved_stat.st_mode = 0644;
-               } else if (!(opt & OPT_dry_run)) {
-                       backup_filename = xasprintf("%s.orig", new_filename);
-                       xrename(new_filename, backup_filename);
-                       src_stream = xfopen_for_read(backup_filename);
-               } else
-                       src_stream = xfopen_for_read(new_filename);
-
-               if (opt & OPT_dry_run) {
-                       dst_stream = xfopen_for_write("/dev/null");
-               } else {
-                       dst_stream = xfopen_for_write(new_filename);
-                       fchmod(fileno(dst_stream), saved_stat.st_mode);
+                       fail_hunk();
+                       state = 0;
+                       continue;
                }
 
-               printf("patching file %s\n", new_filename);
-
-               /* Handle all hunks for this file */
-               patch_line = xmalloc_fgets(patch_file);
-               while (patch_line) {
-                       unsigned count;
-                       unsigned src_beg_line;
-                       unsigned hunk_offset_start;
-                       unsigned src_last_line = 1;
-                       unsigned dst_last_line = 1;
-
-                       if ((sscanf(patch_line, "@@ -%d,%d +%d,%d", &src_beg_line, &src_last_line, &dst_beg_line, &dst_last_line) < 3)
-                        && (sscanf(patch_line, "@@ -%d +%d,%d", &src_beg_line, &dst_beg_line, &dst_last_line) < 2)
-                       ) {
-                               /* No more hunks for this file */
-                               break;
-                       }
-                       if (plus != '+') {
-                               /* reverse patch */
-                               unsigned tmp = src_last_line;
-                               src_last_line = dst_last_line;
-                               dst_last_line = tmp;
-                               tmp = src_beg_line;
-                               src_beg_line = dst_beg_line;
-                               dst_beg_line = tmp;
+               // Open a new file?
+               if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) {
+                       char *s, **name = reverse ? &newname : &oldname;
+                       int i;
+
+                       if (*patchline == '+') {
+                               name = reverse ? &oldname : &newname;
+                               state = 1;
                        }
-                       hunk_count++;
-
-                       if (src_beg_line && dst_beg_line) {
-                               /* Copy unmodified lines upto start of hunk */
-                               /* src_beg_line will be 0 if it's a new file */
-                               count = src_beg_line - src_cur_line;
-                               if (copy_lines(src_stream, dst_stream, count)) {
-                                       bb_error_msg_and_die("bad src file");
+
+                       finish_oldfile();
+
+                       if (!argv[0]) {
+                               free(*name);
+                               // Trim date from end of filename (if any).  We don't care.
+                               for (s = patchline+4; *s && *s!='\t'; s++)
+                                       if (*s=='\\' && s[1]) s++;
+                               i = atoi(s);
+                               if (i>1900 && i<=1970)
+                                       *name = xstrdup("/dev/null");
+                               else {
+                                       *s = 0;
+                                       *name = xstrdup(patchline+4);
                                }
-                               src_cur_line += count;
-                               dst_cur_line += count;
-                               copy_trailing_lines_flag = 1;
                        }
-                       src_last_line += hunk_offset_start = src_cur_line;
-                       dst_last_line += dst_cur_line;
-
-                       while (1) {
-                               free(patch_line);
-                               patch_line = xmalloc_fgets(patch_file);
-                               if (patch_line == NULL)
-                                       break; /* EOF */
-                               if (!*patch_line) {
-                                       /* whitespace-damaged patch with "" lines */
-                                       free(patch_line);
-                                       patch_line = xstrdup(" ");
-                               }
-                               if ((*patch_line != '-') && (*patch_line != '+')
-                                && (*patch_line != ' ')
-                               ) {
-                                       break; /* End of hunk */
+
+                       // We defer actually opening the file because svn produces broken
+                       // patches that don't signal they want to create a new file the
+                       // way the patch man page says, so you have to read the first hunk
+                       // and _guess_.
+
+               // Start a new hunk?  Usually @@ -oldline,oldlen +newline,newlen @@
+               // but a missing ,value means the value is 1.
+               } else if (state == 1 && !strncmp("@@ -", patchline, 4)) {
+                       int i;
+                       char *s = patchline+4;
+
+                       // Read oldline[,oldlen] +newline[,newlen]
+
+                       TT.oldlen = oldlen = TT.newlen = newlen = 1;
+                       TT.oldline = strtol(s, &s, 10);
+                       if (*s == ',') TT.oldlen = oldlen = strtol(s+1, &s, 10);
+                       TT.newline = strtol(s+2, &s, 10);
+                       if (*s == ',') TT.newlen = newlen = strtol(s+1, &s, 10);
+
+                       if (oldlen < 1 && newlen < 1)
+                               bb_error_msg_and_die("Really? %s", patchline);
+
+                       TT.context = 0;
+                       state = 2;
+
+                       // If this is the first hunk, open the file.
+                       if (TT.filein == -1) {
+                               int oldsum, newsum, empty = 0;
+                               char *name;
+
+                               oldsum = TT.oldline + oldlen;
+                               newsum = TT.newline + newlen;
+
+                               name = reverse ? oldname : newname;
+
+                               // We're deleting oldname if new file is /dev/null (before -p)
+                               // or if new hunk is empty (zero context) after patching
+                               if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum)) {
+                                       name = reverse ? newname : oldname;
+                                       empty++;
                                }
-                               if (*patch_line != plus) { /* '-' or ' ' */
-                                       char *src_line = NULL;
-                                       if (src_cur_line == src_last_line)
+
+                               // handle -p path truncation.
+                               for (i = 0, s = name; *s;) {
+                                       if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i)
                                                break;
-                                       if (src_stream) {
-                                               src_line = xmalloc_fgets(src_stream);
-                                               if (src_line) {
-                                                       int diff = strcmp(src_line, patch_line + 1);
-                                                       src_cur_line++;
-                                                       free(src_line);
-                                                       if (diff)
-                                                               src_line = NULL;
-                                               }
-                                       }
-                                       /* Do not patch an already patched hunk with -N */
-                                       if (src_line == 0 && (opt & OPT_N)) {
+                                       if (*s++ != '/')
                                                continue;
+                                       while (*s == '/')
+                                               s++;
+                                       i++;
+                                       name = s;
+                               }
+
+                               if (empty) {
+                                       // File is empty after the patches have been applied
+                                       state = 0;
+                                       if (option_mask32 & FLAG_RMEMPTY) {
+                                               // If flag -E or --remove-empty-files is set
+                                               printf("removing %s\n", name);
+                                               xunlink(name);
+                                       } else {
+                                               printf("patching file %s\n", name);
+                                               xclose(xopen(name, O_WRONLY | O_TRUNC));
                                        }
-                                       if (!src_line) {
-                                               bb_error_msg("hunk #%u FAILED at %u", hunk_count, hunk_offset_start);
-                                               bad_hunk_count++;
-                                               break;
-                                       }
-                                       if (*patch_line != ' ') { /* '-' */
-                                               continue;
+                               // If we've got a file to open, do so.
+                               } else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) {
+                                       struct stat statbuf;
+
+                                       // If the old file was null, we're creating a new one.
+                                       if (!strcmp(oldname, "/dev/null") || !oldsum) {
+                                               printf("creating %s\n", name);
+                                               s = strrchr(name, '/');
+                                               if (s) {
+                                                       *s = 0;
+                                                       bb_make_directory(name, -1, FILEUTILS_RECUR);
+                                                       *s = '/';
+                                               }
+                                               TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR);
+                                       } else {
+                                               printf("patching file %s\n", name);
+                                               TT.filein = xopen(name, O_RDONLY);
                                        }
+
+                                       TT.tempname = xasprintf("%sXXXXXX", name);
+                                       TT.fileout = xmkstemp(TT.tempname);
+                                       // Set permissions of output file
+                                       fstat(TT.filein, &statbuf);
+                                       fchmod(TT.fileout, statbuf.st_mode);
+
+                                       TT.linenum = 0;
+                                       TT.hunknum = 0;
                                }
-                               if (dst_cur_line == dst_last_line)
-                                       break;
-                               fputs(patch_line + 1, dst_stream);
-                               dst_cur_line++;
-                       } /* end of while loop handling one hunk */
-               } /* end of while loop handling one file */
-
-               /* Cleanup last patched file */
-               if (copy_trailing_lines_flag) {
-                       copy_lines(src_stream, dst_stream, (unsigned)(-1));
-               }
-               if (src_stream) {
-                       fclose(src_stream);
-               }
-               fclose(dst_stream);
-               if (bad_hunk_count) {
-                       ret = 1;
-                       bb_error_msg("%u out of %u hunk FAILED", bad_hunk_count, hunk_count);
-               } else {
-                       /* It worked, we can remove the backup */
-                       if (backup_filename) {
-                               unlink(backup_filename);
-                       }
-                       if (!(opt & OPT_dry_run)
-                        && ((dst_cur_line == 0) || (dst_beg_line == 0))
-                       ) {
-                               /* The new patched file is empty, remove it */
-                               xunlink(new_filename);
-                               // /* old_filename and new_filename may be the same file */
-                               // unlink(old_filename);
                        }
+
+                       TT.hunknum++;
+
+                       continue;
                }
-               free(backup_filename);
-               //free(old_filename);
-               free(new_filename);
-       } /* end of "while there are patch lines" */
- quit:
-       /* 0 = SUCCESS
-        * 1 = Some hunks failed
-        * 2 = More serious problems (exited earlier)
-        */
-       return ret;
+
+               // If we didn't continue above, discard this line.
+               free(patchline);
+       }
+
+       finish_oldfile();
+
+       if (ENABLE_FEATURE_CLEAN_UP) {
+               free(oldname);
+               free(newname);
+       }
+
+       return TT.exitval;
 }
diff --git a/editors/patch_bbox.c b/editors/patch_bbox.c
new file mode 100644 (file)
index 0000000..aae7b79
--- /dev/null
@@ -0,0 +1,306 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * busybox patch applet to handle the unified diff format.
+ * Copyright (C) 2003 Glenn McGrath
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * This applet is written to work with patches generated by GNU diff,
+ * where there is equivalent functionality busybox patch shall behave
+ * as per GNU patch.
+ *
+ * There is a SUSv3 specification for patch, however it looks to be
+ * incomplete, it doesnt even mention unified diff format.
+ * http://www.opengroup.org/onlinepubs/007904975/utilities/patch.html
+ *
+ * Issues
+ * - Non-interactive
+ * - Patches must apply cleanly or patch (not just one hunk) will fail.
+ * - Reject file isnt saved
+ */
+
+#include "libbb.h"
+
+static unsigned copy_lines(FILE *src_stream, FILE *dst_stream, unsigned lines_count)
+{
+       while (src_stream && lines_count) {
+               char *line;
+               line = xmalloc_fgets(src_stream);
+               if (line == NULL) {
+                       break;
+               }
+               if (fputs(line, dst_stream) == EOF) {
+                       bb_perror_msg_and_die("error writing to new file");
+               }
+               free(line);
+               lines_count--;
+       }
+       return lines_count;
+}
+
+/* If patch_level is -1 it will remove all directory names
+ * char *line must be greater than 4 chars
+ * returns NULL if the file doesnt exist or error
+ * returns malloc'ed filename
+ * NB: frees 1st argument!
+ */
+static char *extract_filename(char *line, int patch_level, const char *pat)
+{
+       char *temp = NULL, *filename_start_ptr = line + 4;
+
+       if (strncmp(line, pat, 4) == 0) {
+               /* Terminate string at end of source filename */
+               line[strcspn(line, "\t\n\r")] = '\0';
+
+               /* Skip over (patch_level) number of leading directories */
+               while (patch_level--) {
+                       temp = strchr(filename_start_ptr, '/');
+                       if (!temp)
+                               break;
+                       filename_start_ptr = temp + 1;
+               }
+               temp = xstrdup(filename_start_ptr);
+       }
+       free(line);
+       return temp;
+}
+
+int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int patch_main(int argc UNUSED_PARAM, char **argv)
+{
+       struct stat saved_stat;
+       char *patch_line;
+       FILE *patch_file;
+       int patch_level;
+       int ret = 0;
+       char plus = '+';
+       unsigned opt;
+       enum {
+               OPT_R = (1 << 2),
+               OPT_N = (1 << 3),
+               /*OPT_f = (1 << 4), ignored */
+               /*OPT_E = (1 << 5), ignored, this is the default */
+               /*OPT_g = (1 << 6), ignored */
+               OPT_dry_run = (1 << 7) * ENABLE_LONG_OPTS,
+       };
+
+       xfunc_error_retval = 2;
+       {
+               const char *p = "-1";
+               const char *i = "-"; /* compat */
+#if ENABLE_LONG_OPTS
+               static const char patch_longopts[] ALIGN1 =
+                       "strip\0"                 Required_argument "p"
+                       "input\0"                 Required_argument "i"
+                       "reverse\0"               No_argument       "R"
+                       "forward\0"               No_argument       "N"
+               /* "Assume user knows what [s]he is doing, do not ask any questions": */
+                       "force\0"                 No_argument       "f" /*ignored*/
+# if ENABLE_DESKTOP
+                       "remove-empty-files\0"    No_argument       "E" /*ignored*/
+               /* "Controls actions when a file is under RCS or SCCS control,
+                * and does not exist or is read-only and matches the default version,
+                * or when a file is under ClearCase control and does not exist..."
+                * IOW: rather obscure option.
+                * But Gentoo's portage does use -g0 */
+                       "get\0"                   Required_argument "g" /*ignored*/
+# endif
+                       "dry-run\0"               No_argument       "\xfd"
+# if ENABLE_DESKTOP
+                       "backup-if-mismatch\0"    No_argument       "\xfe" /*ignored*/
+                       "no-backup-if-mismatch\0" No_argument       "\xff" /*ignored*/
+# endif
+                       ;
+               applet_long_options = patch_longopts;
+#endif
+               /* -f,-E,-g are ignored */
+               opt = getopt32(argv, "p:i:RN""fEg:", &p, &i, NULL);
+               if (opt & OPT_R)
+                       plus = '-';
+               patch_level = xatoi(p); /* can be negative! */
+               patch_file = xfopen_stdin(i);
+       }
+
+       patch_line = xmalloc_fgetline(patch_file);
+       while (patch_line) {
+               FILE *src_stream;
+               FILE *dst_stream;
+               //char *old_filename;
+               char *new_filename;
+               char *backup_filename = NULL;
+               unsigned src_cur_line = 1;
+               unsigned dst_cur_line = 0;
+               unsigned dst_beg_line;
+               unsigned bad_hunk_count = 0;
+               unsigned hunk_count = 0;
+               smallint copy_trailing_lines_flag = 0;
+
+               /* Skip everything upto the "---" marker
+                * No need to parse the lines "Only in <dir>", and "diff <args>"
+                */
+               do {
+                       /* Extract the filename used before the patch was generated */
+                       new_filename = extract_filename(patch_line, patch_level, "--- ");
+                       // was old_filename above
+                       patch_line = xmalloc_fgetline(patch_file);
+                       if (!patch_line) goto quit;
+               } while (!new_filename);
+               free(new_filename); // "source" filename is irrelevant
+
+               new_filename = extract_filename(patch_line, patch_level, "+++ ");
+               if (!new_filename) {
+                       bb_error_msg_and_die("invalid patch");
+               }
+
+               /* Get access rights from the file to be patched */
+               if (stat(new_filename, &saved_stat) != 0) {
+                       char *slash = strrchr(new_filename, '/');
+                       if (slash) {
+                               /* Create leading directories */
+                               *slash = '\0';
+                               bb_make_directory(new_filename, -1, FILEUTILS_RECUR);
+                               *slash = '/';
+                       }
+                       src_stream = NULL;
+                       saved_stat.st_mode = 0644;
+               } else if (!(opt & OPT_dry_run)) {
+                       backup_filename = xasprintf("%s.orig", new_filename);
+                       xrename(new_filename, backup_filename);
+                       src_stream = xfopen_for_read(backup_filename);
+               } else
+                       src_stream = xfopen_for_read(new_filename);
+
+               if (opt & OPT_dry_run) {
+                       dst_stream = xfopen_for_write("/dev/null");
+               } else {
+                       dst_stream = xfopen_for_write(new_filename);
+                       fchmod(fileno(dst_stream), saved_stat.st_mode);
+               }
+
+               printf("patching file %s\n", new_filename);
+
+               /* Handle all hunks for this file */
+               patch_line = xmalloc_fgets(patch_file);
+               while (patch_line) {
+                       unsigned count;
+                       unsigned src_beg_line;
+                       unsigned hunk_offset_start;
+                       unsigned src_last_line = 1;
+                       unsigned dst_last_line = 1;
+
+                       if ((sscanf(patch_line, "@@ -%u,%u +%u,%u", &src_beg_line, &src_last_line, &dst_beg_line, &dst_last_line) < 3)
+                        && (sscanf(patch_line, "@@ -%u +%u,%u", &src_beg_line, &dst_beg_line, &dst_last_line) < 2)
+                       ) {
+                               /* No more hunks for this file */
+                               break;
+                       }
+                       if (plus != '+') {
+                               /* reverse patch */
+                               unsigned tmp = src_last_line;
+                               src_last_line = dst_last_line;
+                               dst_last_line = tmp;
+                               tmp = src_beg_line;
+                               src_beg_line = dst_beg_line;
+                               dst_beg_line = tmp;
+                       }
+                       hunk_count++;
+
+                       if (src_beg_line && dst_beg_line) {
+                               /* Copy unmodified lines upto start of hunk */
+                               /* src_beg_line will be 0 if it's a new file */
+                               count = src_beg_line - src_cur_line;
+                               if (copy_lines(src_stream, dst_stream, count)) {
+                                       bb_error_msg_and_die("bad src file");
+                               }
+                               src_cur_line += count;
+                               dst_cur_line += count;
+                               copy_trailing_lines_flag = 1;
+                       }
+                       src_last_line += hunk_offset_start = src_cur_line;
+                       dst_last_line += dst_cur_line;
+
+                       while (1) {
+                               free(patch_line);
+                               patch_line = xmalloc_fgets(patch_file);
+                               if (patch_line == NULL)
+                                       break; /* EOF */
+                               if (!*patch_line) {
+                                       /* whitespace-damaged patch with "" lines */
+                                       free(patch_line);
+                                       patch_line = xstrdup(" ");
+                               }
+                               if ((*patch_line != '-') && (*patch_line != '+')
+                                && (*patch_line != ' ')
+                               ) {
+                                       break; /* End of hunk */
+                               }
+                               if (*patch_line != plus) { /* '-' or ' ' */
+                                       char *src_line = NULL;
+                                       if (src_cur_line == src_last_line)
+                                               break;
+                                       if (src_stream) {
+                                               src_line = xmalloc_fgets(src_stream);
+                                               if (src_line) {
+                                                       int diff = strcmp(src_line, patch_line + 1);
+                                                       src_cur_line++;
+                                                       free(src_line);
+                                                       if (diff)
+                                                               src_line = NULL;
+                                               }
+                                       }
+                                       /* Do not patch an already patched hunk with -N */
+                                       if (src_line == 0 && (opt & OPT_N)) {
+                                               continue;
+                                       }
+                                       if (!src_line) {
+                                               bb_error_msg("hunk #%u FAILED at %u", hunk_count, hunk_offset_start);
+                                               bad_hunk_count++;
+                                               break;
+                                       }
+                                       if (*patch_line != ' ') { /* '-' */
+                                               continue;
+                                       }
+                               }
+                               if (dst_cur_line == dst_last_line)
+                                       break;
+                               fputs(patch_line + 1, dst_stream);
+                               dst_cur_line++;
+                       } /* end of while loop handling one hunk */
+               } /* end of while loop handling one file */
+
+               /* Cleanup last patched file */
+               if (copy_trailing_lines_flag) {
+                       copy_lines(src_stream, dst_stream, (unsigned)(-1));
+               }
+               if (src_stream) {
+                       fclose(src_stream);
+               }
+               fclose(dst_stream);
+               if (bad_hunk_count) {
+                       ret = 1;
+                       bb_error_msg("%u out of %u hunk FAILED", bad_hunk_count, hunk_count);
+               } else {
+                       /* It worked, we can remove the backup */
+                       if (backup_filename) {
+                               unlink(backup_filename);
+                       }
+                       if (!(opt & OPT_dry_run)
+                        && ((dst_cur_line == 0) || (dst_beg_line == 0))
+                       ) {
+                               /* The new patched file is empty, remove it */
+                               xunlink(new_filename);
+                               // /* old_filename and new_filename may be the same file */
+                               // unlink(old_filename);
+                       }
+               }
+               free(backup_filename);
+               //free(old_filename);
+               free(new_filename);
+       } /* end of "while there are patch lines" */
+ quit:
+       /* 0 = SUCCESS
+        * 1 = Some hunks failed
+        * 2 = More serious problems (exited earlier)
+        */
+       return ret;
+}
index 0e5c070..a60bf07 100644 (file)
@@ -1,4 +1,4 @@
-/* Adapted from toybox's patch. Currently unused */
+/* Adapted from toybox's patch. */
 
 /* vi: set sw=4 ts=4:
  *
@@ -23,7 +23,7 @@
  * -F fuzz (number, default 2)
  * [file] which file to patch
 
-USE_PATCH(NEWTOY(patch, "up#i:R", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PATCH(NEWTOY(patch, USE_TOYBOX_DEBUG("x")"up#i:R", TOYFLAG_USR|TOYFLAG_BIN))
 
 config PATCH
        bool "patch"
@@ -223,15 +223,16 @@ void delete_tempfile(int fdin, int fdout, char **tempname)
 
 
 struct globals {
-       struct double_list *plines;
-       long linenum;
-       int context;
-       int hunknum;
-       int filein;
-       int fileout;
-       int state;
+       char *infile;
+       long prefix;
+
+       struct double_list *current_hunk;
+       long oldline, oldlen, newline, newlen, linenum;
+       int context, state, filein, fileout, filepatch, hunknum;
        char *tempname;
-       smallint exitval;
+
+       // was toys.foo:
+       int exitval;
 };
 #define TT (*ptr_to_globals)
 #define INIT_TT() do { \
@@ -240,12 +241,14 @@ struct globals {
 
 
 //bbox had: "p:i:RN"
-#define FLAG_STR "Rup:i:"
+#define FLAG_STR "Rup:i:x"
 /* FLAG_REVERSE must be == 1! Code uses this fact. */
 #define FLAG_REVERSE (1 << 0)
 #define FLAG_u       (1 << 1)
 #define FLAG_PATHLEN (1 << 2)
 #define FLAG_INPUT   (1 << 3)
+//non-standard:
+#define FLAG_DEBUG   (1 << 4)
 
 // Dispose of a line of input, either by writing it out or discarding it.
 
@@ -254,6 +257,8 @@ struct globals {
 // state = 3: write whole line to fileout
 // state > 3: write line+1 to fileout when *line != state
 
+#define PATCH_DEBUG (option_mask32 & FLAG_DEBUG)
+
 static void do_line(void *data)
 {
        struct double_list *dlist = (struct double_list *)data;
@@ -262,6 +267,8 @@ static void do_line(void *data)
                fdprintf(TT.state == 2 ? 2 : TT.fileout,
                        "%s\n", dlist->data+(TT.state>3 ? 1 : 0));
 
+       if (PATCH_DEBUG) fdprintf(2, "DO %d: %s\n", TT.state, dlist->data);
+
        free(dlist->data);
        free(data);
 }
@@ -274,94 +281,118 @@ static void finish_oldfile(void)
 
 static void fail_hunk(void)
 {
-       if (!TT.plines) return;
-       TT.plines->prev->next = 0;
+       if (!TT.current_hunk) return;
+       TT.current_hunk->prev->next = 0;
 
-       fdprintf(2, "Hunk %d FAILED.\n", TT.hunknum);
+       fdprintf(2, "Hunk %d FAILED %ld/%ld.\n", TT.hunknum, TT.oldline, TT.newline);
        TT.exitval = 1;
 
        // If we got to this point, we've seeked to the end.  Discard changes to
        // this file and advance to next file.
 
        TT.state = 2;
-       TOY_llist_free(TT.plines, do_line);
-       TT.plines = NULL;
+       TOY_llist_free(TT.current_hunk, do_line);
+       TT.current_hunk = NULL;
        delete_tempfile(TT.filein, TT.fileout, &TT.tempname);
        TT.state = 0;
 }
 
-static int apply_hunk(void)
+// Given a hunk of a unified diff, make the appropriate change to the file.
+// This does not use the location information, but instead treats a hunk
+// as a sort of regex.  Copies data from input to output until it finds
+// the change to be made, then outputs the changed data and returns.
+// (Finding EOF first is an error.)  This is a single pass operation, so
+// multiple hunks must occur in order in the file.
+
+static int apply_one_hunk(void)
 {
        struct double_list *plist, *buf = NULL, *check;
-       int i = 0, backwards = 0, matcheof = 0,
-               reverse = option_mask32 & FLAG_REVERSE;
+       int matcheof = 0, reverse = option_mask32 & FLAG_REVERSE, backwarn = 0;
 
        // Break doubly linked list so we can use singly linked traversal function.
-       TT.plines->prev->next = NULL;
+       TT.current_hunk->prev->next = NULL;
 
        // Match EOF if there aren't as many ending context lines as beginning
-       for (plist = TT.plines; plist; plist = plist->next) {
-               if (plist->data[0]==' ') i++;
-               else i = 0;
+       for (plist = TT.current_hunk; plist; plist = plist->next) {
+               if (plist->data[0]==' ') matcheof++;
+               else matcheof = 0;
+               if (PATCH_DEBUG) fdprintf(2, "HUNK:%s\n", plist->data);
        }
-       if (i < TT.context) matcheof++;
+       matcheof = matcheof < TT.context;
 
-       // Search for a place to apply this hunk.  Match all context lines and
-       // lines to be removed.
-       plist = TT.plines;
-       buf = NULL;
-       i = 0;
+       if (PATCH_DEBUG) fdprintf(2,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N');
 
-       // Start of for loop
+       // Loop through input data searching for this hunk.  Match all context
+       // lines and all lines to be removed until we've found the end of a
+       // complete hunk.
+       plist = TT.current_hunk;
+       buf = NULL;
        if (TT.context) for (;;) {
                char *data = get_line(TT.filein);
 
                TT.linenum++;
 
-               // Skip lines of the hunk we'd be adding.
+               // Figure out which line of hunk to compare with next.  (Skip lines
+               // of the hunk we'd be adding.)
                while (plist && *plist->data == "+-"[reverse]) {
                        if (data && !strcmp(data, plist->data+1)) {
-                               if (++backwards == TT.context)
+                               if (!backwarn) {
                                        fdprintf(2,"Possibly reversed hunk %d at %ld\n",
                                                TT.hunknum, TT.linenum);
-                       } else backwards=0;
+                                       backwarn++;
+                               }
+                       }
                        plist = plist->next;
                }
 
                // Is this EOF?
                if (!data) {
+                       if (PATCH_DEBUG) fdprintf(2, "INEOF\n");
+
                        // Does this hunk need to match EOF?
                        if (!plist && matcheof) break;
 
                        // File ended before we found a place for this hunk.
                        fail_hunk();
                        goto done;
-               }
+               } else if (PATCH_DEBUG) fdprintf(2, "IN: %s\n", data);
                check = dlist_add(&buf, data);
 
+               // Compare this line with next expected line of hunk.
                // todo: teach the strcmp() to ignore whitespace.
 
-               for (;;) {
-                       // If we hit the end of a hunk that needed EOF and this isn't EOF,
-                       // or next line doesn't match, flush first line of buffered data and
-                       // recheck match until we find a new match or run out of buffer.
+               // A match can fail because the next line doesn't match, or because
+               // we hit the end of a hunk that needed EOF, and this isn't EOF.
+
+               // If match failed, flush first line of buffered data and
+               // recheck buffered data for a new match until we find one or run
+               // out of buffer.
 
+               for (;;) {
                        if (!plist || strcmp(check->data, plist->data+1)) {
-                               // First line isn't a match, write it out.
+                               // Match failed.  Write out first line of buffered data and
+                               // recheck remaining buffered data for a new match.
+
+                               if (PATCH_DEBUG)
+                                       fdprintf(2, "NOT: %s\n", plist->data);
+
                                TT.state = 3;
                                check = TOY_llist_pop(&buf);
                                check->prev->next = buf;
                                buf->prev = check->prev;
                                do_line(check);
-                               plist = TT.plines;
+                               plist = TT.current_hunk;
 
-                               // Out of buffered lines?
+                               // If we've reached the end of the buffer without confirming a
+                               // match, read more lines.
                                if (check==buf) {
                                        buf = 0;
                                        break;
                                }
                                check = buf;
                        } else {
+                               if (PATCH_DEBUG)
+                                       fdprintf(2, "MAYBE: %s\n", plist->data);
                                // This line matches.  Advance plist, detect successful match.
                                plist = plist->next;
                                if (!plist && !matcheof) goto out;
@@ -371,10 +402,10 @@ static int apply_hunk(void)
                }
        }
 out:
-       // Got it.  Emit changed data.
+       // We have a match.  Emit changed data.
        TT.state = "-+"[reverse];
-       TOY_llist_free(TT.plines, do_line);
-       TT.plines = NULL;
+       TOY_llist_free(TT.current_hunk, do_line);
+       TT.current_hunk = NULL;
        TT.state = 1;
 done:
        if (buf) {
@@ -385,6 +416,9 @@ done:
        return TT.state;
 }
 
+// Read a patch file and find hunks, opening/creating/deleting files.
+// Call apply_one_hunk() on each hunk.
+
 // state 0: Not in a hunk, look for +++.
 // state 1: Found +++ file indicator, look for @@
 // state 2: In hunk: counting initial context lines
@@ -397,24 +431,20 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
        int reverse, state = 0;
        char *oldname = NULL, *newname = NULL;
        char *opt_p, *opt_i;
-       int prefix;
-
-       long oldline = 0, oldlen = 0, newline = 0, newlen = 0;
 
        INIT_TT();
 
        opts = getopt32(argv, FLAG_STR, &opt_p, &opt_i);
        reverse = opts & FLAG_REVERSE;
-
-       if (opts & FLAG_INPUT) xmove_fd(xopen(opt_i, O_RDONLY), STDIN_FILENO);
-       prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative!
+       TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative!
+       if (opts & FLAG_INPUT) TT.filepatch = xopen(opt_i, O_RDONLY);
        TT.filein = TT.fileout = -1;
 
        // Loop through the lines in the patch
        for(;;) {
                char *patchline;
 
-               patchline = get_line(STDIN_FILENO);
+               patchline = get_line(TT.filepatch);
                if (!patchline) break;
 
                // Other versions of patch accept damaged patches,
@@ -427,17 +457,18 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
                // Are we assembling a hunk?
                if (state >= 2) {
                        if (*patchline==' ' || *patchline=='+' || *patchline=='-') {
-                               dlist_add(&TT.plines, patchline);
+                               dlist_add(&TT.current_hunk, patchline);
 
-                               if (*patchline != '+') oldlen--;
-                               if (*patchline != '-') newlen--;
+                               if (*patchline != '+') TT.oldlen--;
+                               if (*patchline != '-') TT.newlen--;
 
                                // Context line?
                                if (*patchline==' ' && state==2) TT.context++;
                                else state=3;
 
                                // If we've consumed all expected hunk lines, apply the hunk.
-                               if (!oldlen && !newlen) state = apply_hunk();
+
+                               if (!TT.oldlen && !TT.newlen) state = apply_one_hunk();
                                continue;
                        }
                        fail_hunk();
@@ -447,11 +478,11 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
 
                // Open a new file?
                if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) {
-                       char *s, **name = &oldname;
+                       char *s, **name = reverse ? &newname : &oldname;
                        int i;
 
                        if (*patchline == '+') {
-                               name = &newname;
+                               name = reverse ? &oldname : &newname;
                                state = 1;
                        }
 
@@ -462,7 +493,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
                        for (s = patchline+4; *s && *s!='\t'; s++)
                                if (*s=='\\' && s[1]) s++;
                        i = atoi(s);
-                       if (i && i<=1970)
+                       if (i>1900 && i<=1970)
                                *name = xstrdup("/dev/null");
                        else {
                                *s = 0;
@@ -478,8 +509,8 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
                } else if (state == 1 && !strncmp("@@ -", patchline, 4)) {
                        int i;
 
-                       i = sscanf(patchline+4, "%ld,%ld +%ld,%ld",
-                                       &oldline, &oldlen, &newline, &newlen);
+                       i = sscanf(patchline+4, "%ld,%ld +%ld,%ld", &TT.oldline,
+                                               &TT.oldlen, &TT.newline, &TT.newlen);
                        if (i != 4)
                                bb_error_msg_and_die("corrupt hunk %d at %ld", TT.hunknum, TT.linenum);
 
@@ -491,22 +522,22 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
                                int oldsum, newsum, del = 0;
                                char *s, *name;
 
-                               oldsum = oldline + oldlen;
-                               newsum = newline + newlen;
+                               oldsum = TT.oldline + TT.oldlen;
+                               newsum = TT.newline + TT.newlen;
 
                                name = reverse ? oldname : newname;
 
                                // We're deleting oldname if new file is /dev/null (before -p)
                                // or if new hunk is empty (zero context) after patching
-                               if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum)) {
+                               if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum))
+                               {
                                        name = reverse ? newname : oldname;
                                        del++;
                                }
 
                                // handle -p path truncation.
                                for (i=0, s = name; *s;) {
-                                       if ((option_mask32 & FLAG_PATHLEN) && prefix == i)
-                                               break;
+                                       if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i) break;
                                        if (*(s++)=='/') {
                                                name = s;
                                                i++;
@@ -518,7 +549,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
                                        xunlink(name);
                                        state = 0;
                                // If we've got a file to open, do so.
-                               } else if (!(option_mask32 & FLAG_PATHLEN) || i <= prefix) {
+                               } else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) {
                                        // If the old file was null, we're creating a new one.
                                        if (!strcmp(oldname, "/dev/null") || !oldsum) {
                                                printf("creating %s\n", name);
@@ -528,7 +559,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
                                                        xmkpath(name, -1);
                                                        *s = '/';
                                                }
-                                               TT.filein = xopen3(name, O_CREAT|O_EXCL|O_RDWR, 0666);
+                                               TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR);
                                        } else {
                                                printf("patching file %s\n", name);
                                                TT.filein = xopen(name, O_RDWR);
@@ -551,6 +582,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
        finish_oldfile();
 
        if (ENABLE_FEATURE_CLEAN_UP) {
+               close(TT.filepatch);
                free(oldname);
                free(newname);
        }
index 7af8f86..e18e48a 100644 (file)
  *
  * MAINTAINER: Rob Landley <rob@landley.net>
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /* Code overview.
+ *
+ * Files are laid out to avoid unnecessary function declarations.  So for
+ * example, every function add_cmd calls occurs before add_cmd in this file.
+ *
+ * add_cmd() is called on each line of sed command text (from a file or from
+ * the command line).  It calls get_address() and parse_cmd_args().  The
+ * resulting sed_cmd_t structures are appended to a linked list
+ * (G.sed_cmd_head/G.sed_cmd_tail).
+ *
+ * process_files() does actual sedding, reading data lines from each input FILE*
+ * (which could be stdin) and applying the sed command list (sed_cmd_head) to
+ * each of the resulting lines.
+ *
+ * sed_main() is where external code calls into this, with a command line.
+ */
 
-  Files are laid out to avoid unnecessary function declarations.  So for
-  example, every function add_cmd calls occurs before add_cmd in this file.
-
-  add_cmd() is called on each line of sed command text (from a file or from
-  the command line).  It calls get_address() and parse_cmd_args().  The
-  resulting sed_cmd_t structures are appended to a linked list
-  (G.sed_cmd_head/G.sed_cmd_tail).
-
-  add_input_file() adds a FILE* to the list of input files.  We need to
-  know all input sources ahead of time to find the last line for the $ match.
-
-  process_files() does actual sedding, reading data lines from each input FILE *
-  (which could be stdin) and applying the sed command list (sed_cmd_head) to
-  each of the resulting lines.
-
-  sed_main() is where external code calls into this, with a command line.
-*/
-
-
-/*
-       Supported features and commands in this version of sed:
+/* Supported features and commands in this version of sed:
+ *
+ * - comments ('#')
+ * - address matching: num|/matchstr/[,num|/matchstr/|$]command
+ * - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags)
+ * - edit commands: (a)ppend, (i)nsert, (c)hange
+ * - file commands: (r)ead
+ * - backreferences in substitution expressions (\0, \1, \2...\9)
+ * - grouped commands: {cmd1;cmd2}
+ * - transliteration (y/source-chars/dest-chars/)
+ * - pattern space hold space storing / swapping (g, h, x)
+ * - labels / branching (: label, b, t, T)
+ *
+ * (Note: Specifying an address (range) to match is *optional*; commands
+ * default to the whole pattern space if no specific address match was
+ * requested.)
+ *
+ * Todo:
+ * - Create a wrapper around regex to make libc's regex conform with sed
+ *
+ * Reference
+ * http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html
+ * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html
+ */
 
-        - comments ('#')
-        - address matching: num|/matchstr/[,num|/matchstr/|$]command
-        - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags)
-        - edit commands: (a)ppend, (i)nsert, (c)hange
-        - file commands: (r)ead
-        - backreferences in substitution expressions (\0, \1, \2...\9)
-        - grouped commands: {cmd1;cmd2}
-        - transliteration (y/source-chars/dest-chars/)
-        - pattern space hold space storing / swapping (g, h, x)
-        - labels / branching (: label, b, t, T)
+//config:config SED
+//config:      bool "sed"
+//config:      default y
+//config:      help
+//config:        sed is used to perform text transformations on a file
+//config:        or input from a pipeline.
+
+//kbuild:lib-$(CONFIG_SED) += sed.o
+
+//applet:IF_SED(APPLET(sed, BB_DIR_BIN, BB_SUID_DROP))
+
+//usage:#define sed_trivial_usage
+//usage:       "[-inrE] [-f FILE]... [-e CMD]... [FILE]...\n"
+//usage:       "or: sed [-inrE] CMD [FILE]..."
+//usage:#define sed_full_usage "\n\n"
+//usage:       "       -e CMD  Add CMD to sed commands to be executed"
+//usage:     "\n       -f FILE Add FILE contents to sed commands to be executed"
+//usage:     "\n       -i[SFX] Edit files in-place (otherwise sends to stdout)"
+//usage:     "\n               Optionally back files up, appending SFX"
+//usage:     "\n       -n      Suppress automatic printing of pattern space"
+//usage:     "\n       -r,-E   Use extended regex syntax"
+//usage:     "\n"
+//usage:     "\nIf no -e or -f, the first non-option argument is the sed command string."
+//usage:     "\nRemaining arguments are input files (stdin if none)."
+//usage:
+//usage:#define sed_example_usage
+//usage:       "$ echo \"foo\" | sed -e 's/f[a-zA-Z]o/bar/g'\n"
+//usage:       "bar\n"
 
-        (Note: Specifying an address (range) to match is *optional*; commands
-        default to the whole pattern space if no specific address match was
-        requested.)
+#include "libbb.h"
+#include "xregex.h"
 
-       Todo:
-        - Create a wrapper around regex to make libc's regex conform with sed
+#if 0
+# define dbg(...) bb_error_msg(__VA_ARGS__)
+#else
+# define dbg(...) ((void)0)
+#endif
 
-       Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html
-*/
 
-#include "libbb.h"
-#include "xregex.h"
+enum {
+       OPT_in_place = 1 << 0,
+};
 
 /* Each sed command turns into one of these structures. */
 typedef struct sed_cmd_s {
@@ -71,6 +108,7 @@ typedef struct sed_cmd_s {
        regex_t *end_match;     /* sed -e '/match/,/end_match/cmd' */
        regex_t *sub_match;     /* For 's/sub_match/string/' */
        int beg_line;           /* 'sed 1p'   0 == apply commands to all lines */
+       int beg_line_orig;      /* copy of the above, needed for -i */
        int end_line;           /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */
 
        FILE *sw_file;          /* File (sw) command writes to, -1 for none. */
@@ -94,33 +132,36 @@ static const char semicolon_whitespace[] ALIGN1 = "; \n\r\t\v";
 struct globals {
        /* options */
        int be_quiet, regex_type;
+
        FILE *nonstdout;
        char *outname, *hold_space;
+       smallint exitcode;
 
-       /* List of input files */
-       int input_file_count, current_input_file;
-       FILE **input_file_list;
+       /* list of input files */
+       int current_input_file, last_input_file;
+       char **input_file_list;
+       FILE *current_fp;
 
        regmatch_t regmatch[10];
        regex_t *previous_regex_ptr;
 
        /* linked list of sed commands */
-       sed_cmd_t sed_cmd_head, *sed_cmd_tail;
+       sed_cmd_t *sed_cmd_head, **sed_cmd_tail;
 
-       /* Linked list of append lines */
+       /* linked list of append lines */
        llist_t *append_head;
 
        char *add_cmd_line;
 
        struct pipeline {
-               char *buf;      /* Space to hold string */
-               int idx;        /* Space used */
-               int len;        /* Space allocated */
+               char *buf;  /* Space to hold string */
+               int idx;    /* Space used */
+               int len;    /* Space allocated */
        } pipeline;
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
 struct BUG_G_too_big {
-        char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
+       char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
 };
 #define INIT_G() do { \
        G.sed_cmd_tail = &G.sed_cmd_head; \
@@ -130,7 +171,7 @@ struct BUG_G_too_big {
 #if ENABLE_FEATURE_CLEAN_UP
 static void sed_free_and_close_stuff(void)
 {
-       sed_cmd_t *sed_cmd = G.sed_cmd_head.next;
+       sed_cmd_t *sed_cmd = G.sed_cmd_head;
 
        llist_free(G.append_head, free);
 
@@ -159,8 +200,8 @@ static void sed_free_and_close_stuff(void)
 
        free(G.hold_space);
 
-       while (G.current_input_file < G.input_file_count)
-               fclose(G.input_file_list[G.current_input_file++]);
+       if (G.current_fp)
+               fclose(G.current_fp);
 }
 #else
 void sed_free_and_close_stuff(void);
@@ -196,11 +237,16 @@ static void parse_escapes(char *dest, const char *string, int len, char from, ch
 
 static char *copy_parsing_escapes(const char *string, int len)
 {
+       const char *s;
        char *dest = xmalloc(len + 1);
 
-       parse_escapes(dest, string, len, 'n', '\n');
-       /* GNU sed also recognizes \t */
-       parse_escapes(dest, dest, strlen(dest), 't', '\t');
+       /* sed recognizes \n */
+       /* GNU sed also recognizes \t and \r */
+       for (s = "\nn\tt\rr"; *s; s += 2) {
+               parse_escapes(dest, string, len, s[1], s[0]);
+               string = dest;
+               len = strlen(dest);
+       }
        return dest;
 }
 
@@ -223,11 +269,13 @@ static int index_of_next_unescaped_regexp_delim(int delimiter, const char *str)
                delimiter = -delimiter;
        }
 
-       for (; (ch = str[idx]); idx++) {
+       for (; (ch = str[idx]) != '\0'; idx++) {
                if (bracket >= 0) {
-                       if (ch == ']' && !(bracket == idx - 1 || (bracket == idx - 2
-                                       && str[idx - 1] == '^')))
+                       if (ch == ']'
+                        && !(bracket == idx - 1 || (bracket == idx - 2 && str[idx - 1] == '^'))
+                       ) {
                                bracket = -1;
+                       }
                } else if (escaped)
                        escaped = 0;
                else if (ch == '\\')
@@ -248,7 +296,7 @@ static int index_of_next_unescaped_regexp_delim(int delimiter, const char *str)
 static int parse_regex_delim(const char *cmdstr, char **match, char **replace)
 {
        const char *cmdstr_ptr = cmdstr;
-       char delimiter;
+       unsigned char delimiter;
        int idx = 0;
 
        /* verify that the 's' or 'y' is followed by something.  That something
@@ -263,7 +311,7 @@ static int parse_regex_delim(const char *cmdstr, char **match, char **replace)
 
        /* save the replacement string */
        cmdstr_ptr += idx + 1;
-       idx = index_of_next_unescaped_regexp_delim(-delimiter, cmdstr_ptr);
+       idx = index_of_next_unescaped_regexp_delim(- (int)delimiter, cmdstr_ptr);
        *replace = copy_parsing_escapes(cmdstr_ptr, idx);
 
        return ((cmdstr_ptr - cmdstr) + idx);
@@ -288,11 +336,12 @@ static int get_address(const char *my_str, int *linenum, regex_t ** regex)
                char *temp;
 
                delimiter = '/';
-               if (*my_str == '\\') delimiter = *++pos;
+               if (*my_str == '\\')
+                       delimiter = *++pos;
                next = index_of_next_unescaped_regexp_delim(delimiter, ++pos);
                temp = copy_parsing_escapes(pos, next);
-               *regex = xmalloc(sizeof(regex_t));
-               xregcomp(*regex, temp, G.regex_type|REG_NEWLINE);
+               *regex = xzalloc(sizeof(regex_t));
+               xregcomp(*regex, temp, G.regex_type);
                free(temp);
                /* Move position to next character after last delimiter */
                pos += (next+1);
@@ -332,7 +381,7 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
 
        /*
         * A substitution command should look something like this:
-        *    s/match/replace/ #gIpw
+        *    s/match/replace/ #giIpw
         *    ||     |        |||
         *    mandatory       optional
         */
@@ -380,6 +429,7 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
                        break;
                }
                /* Ignore case (gnu exension) */
+               case 'i':
                case 'I':
                        cflags |= REG_ICASE;
                        break;
@@ -400,8 +450,10 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
        /* compile the match string into a regex */
        if (*match != '\0') {
                /* If match is empty, we use last regex used at runtime */
-               sed_cmd->sub_match = xmalloc(sizeof(regex_t));
+               sed_cmd->sub_match = xzalloc(sizeof(regex_t));
+               dbg("xregcomp('%s',%x)", match, cflags);
                xregcomp(sed_cmd->sub_match, match, cflags);
+               dbg("regcomp ok");
        }
        free(match);
 
@@ -413,13 +465,51 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
  */
 static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
 {
+       static const char cmd_letters[] = "saicrw:btTydDgGhHlnNpPqx={}";
+       enum {
+               IDX_s = 0,
+               IDX_a,
+               IDX_i,
+               IDX_c,
+               IDX_r,
+               IDX_w,
+               IDX_colon,
+               IDX_b,
+               IDX_t,
+               IDX_T,
+               IDX_y,
+               IDX_d,
+               IDX_D,
+               IDX_g,
+               IDX_G,
+               IDX_h,
+               IDX_H,
+               IDX_l,
+               IDX_n,
+               IDX_N,
+               IDX_p,
+               IDX_P,
+               IDX_q,
+               IDX_x,
+               IDX_equal,
+               IDX_lbrace,
+               IDX_rbrace,
+               IDX_nul
+       };
+       struct chk { char chk[sizeof(cmd_letters)-1 == IDX_nul ? 1 : -1]; };
+
+       unsigned idx = strchrnul(cmd_letters, sed_cmd->cmd) - cmd_letters;
+
        /* handle (s)ubstitution command */
-       if (sed_cmd->cmd == 's')
+       if (idx == IDX_s) {
                cmdstr += parse_subst_cmd(sed_cmd, cmdstr);
+       }
        /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */
-       else if (strchr("aic", sed_cmd->cmd)) {
-               if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c')
-                       bb_error_msg_and_die("only a beginning address can be specified for edit commands");
+       else if (idx <= IDX_c) { /* a,i,c */
+               if (idx < IDX_c) { /* a,i */
+                       if (sed_cmd->end_line || sed_cmd->end_match)
+                               bb_error_msg_and_die("command '%c' uses only one address", sed_cmd->cmd);
+               }
                for (;;) {
                        if (*cmdstr == '\n' || *cmdstr == '\\') {
                                cmdstr++;
@@ -433,17 +523,21 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
                /* "\anychar" -> "anychar" */
                parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), '\0', '\0');
                cmdstr += strlen(cmdstr);
+       }
        /* handle file cmds: (r)ead */
-       } else if (strchr("rw", sed_cmd->cmd)) {
-               if (sed_cmd->end_line || sed_cmd->end_match)
-                       bb_error_msg_and_die("command only uses one address");
+       else if (idx <= IDX_w) { /* r,w */
+               if (idx < IDX_w) { /* r */
+                       if (sed_cmd->end_line || sed_cmd->end_match)
+                               bb_error_msg_and_die("command '%c' uses only one address", sed_cmd->cmd);
+               }
                cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string);
                if (sed_cmd->cmd == 'w') {
                        sed_cmd->sw_file = xfopen_for_write(sed_cmd->string);
                        sed_cmd->sw_last_char = '\n';
                }
+       }
        /* handle branch commands */
-       } else if (strchr(":btT", sed_cmd->cmd)) {
+       else if (idx <= IDX_T) { /* :,b,t,T */
                int length;
 
                cmdstr = skip_whitespace(cmdstr);
@@ -454,7 +548,7 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
                }
        }
        /* translation command */
-       else if (sed_cmd->cmd == 'y') {
+       else if (idx == IDX_y) {
                char *match, *replace;
                int i = cmdstr[0];
 
@@ -474,7 +568,7 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
        /* if it wasnt a single-letter command that takes no arguments
         * then it must be an invalid command.
         */
-       else if (strchr("dDgGhHlnNpPqx={}", sed_cmd->cmd) == 0) {
+       else if (idx >= IDX_nul) { /* not d,D,g,G,h,H,l,n,N,p,P,q,x,=,{,} */
                bb_error_msg_and_die("unsupported command %c", sed_cmd->cmd);
        }
 
@@ -536,6 +630,7 @@ static void add_cmd(const char *cmdstr)
 
                /* first part (if present) is an address: either a '$', a number or a /regex/ */
                cmdstr += get_address(cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match);
+               sed_cmd->beg_line_orig = sed_cmd->beg_line;
 
                /* second part (if present) will begin with a comma */
                if (*cmdstr == ',') {
@@ -566,9 +661,15 @@ static void add_cmd(const char *cmdstr)
                sed_cmd->cmd = *cmdstr++;
                cmdstr = parse_cmd_args(sed_cmd, cmdstr);
 
+               /* cmdstr now points past args.
+                * GNU sed requires a separator, if there are more commands,
+                * else it complains "char N: extra characters after command".
+                * Example: "sed 'p;d'". We also allow "sed 'pd'".
+                */
+
                /* Add the command to the command array */
-               G.sed_cmd_tail->next = sed_cmd;
-               G.sed_cmd_tail = G.sed_cmd_tail->next;
+               *G.sed_cmd_tail = sed_cmd;
+               G.sed_cmd_tail = &sed_cmd->next;
        }
 
        /* If we glued multiple lines together, free the memory. */
@@ -596,7 +697,7 @@ static void do_subst_w_backrefs(char *line, char *replace)
 
        /* go through the replacement string */
        for (i = 0; replace[i]; i++) {
-               /* if we find a backreference (\1, \2, etc.) print the backref'ed text */
+               /* if we find a backreference (\1, \2, etc.) print the backref'ed text */
                if (replace[i] == '\\') {
                        unsigned backref = replace[++i] - '0';
                        if (backref <= 9) {
@@ -630,8 +731,10 @@ static void do_subst_w_backrefs(char *line, char *replace)
 static int do_subst_command(sed_cmd_t *sed_cmd, char **line_p)
 {
        char *line = *line_p;
-       int altered = 0;
        unsigned match_count = 0;
+       bool altered = 0;
+       bool prev_match_empty = 1;
+       bool tried_at_eol = 0;
        regex_t *current_regex;
 
        current_regex = sed_cmd->sub_match;
@@ -644,8 +747,12 @@ static int do_subst_command(sed_cmd_t *sed_cmd, char **line_p)
        G.previous_regex_ptr = current_regex;
 
        /* Find the first match */
-       if (REG_NOMATCH == regexec(current_regex, line, 10, G.regmatch, 0))
+       dbg("matching '%s'", line);
+       if (REG_NOMATCH == regexec(current_regex, line, 10, G.regmatch, 0)) {
+               dbg("no match");
                return 0;
+       }
+       dbg("match");
 
        /* Initialize temporary output buffer. */
        G.pipeline.buf = xmalloc(PIPE_GROW);
@@ -654,47 +761,76 @@ static int do_subst_command(sed_cmd_t *sed_cmd, char **line_p)
 
        /* Now loop through, substituting for matches */
        do {
+               int start = G.regmatch[0].rm_so;
+               int end = G.regmatch[0].rm_eo;
                int i;
 
-               /* Work around bug in glibc regexec, demonstrated by:
-                  echo " a.b" | busybox sed 's [^ .]* x g'
-                  The match_count check is so not to break
-                  echo "hi" | busybox sed 's/^/!/g' */
-               if (!G.regmatch[0].rm_so && !G.regmatch[0].rm_eo && match_count) {
-                       pipe_putc(*line++);
-                       continue;
-               }
-
                match_count++;
 
                /* If we aren't interested in this match, output old line to
-                  end of match and continue */
+                * end of match and continue */
                if (sed_cmd->which_match
                 && (sed_cmd->which_match != match_count)
                ) {
-                       for (i = 0; i < G.regmatch[0].rm_eo; i++)
+                       for (i = 0; i < end; i++)
                                pipe_putc(*line++);
-                       continue;
+                       /* Null match? Print one more char */
+                       if (start == end && *line)
+                               pipe_putc(*line++);
+                       goto next;
                }
 
-               /* print everything before the match */
-               for (i = 0; i < G.regmatch[0].rm_so; i++)
+               /* Print everything before the match */
+               for (i = 0; i < start; i++)
                        pipe_putc(line[i]);
 
-               /* then print the substitution string */
-               do_subst_w_backrefs(line, sed_cmd->string);
+               /* Then print the substitution string,
+                * unless we just matched empty string after non-empty one.
+                * Example: string "cccd", pattern "c*", repl "R":
+                * result is "RdR", not "RRdR": first match "ccc",
+                * second is "" before "d", third is "" after "d".
+                * Second match is NOT replaced!
+                */
+               if (prev_match_empty || start != 0 || start != end) {
+                       //dbg("%d %d %d", prev_match_empty, start, end);
+                       dbg("inserting replacement at %d in '%s'", start, line);
+                       do_subst_w_backrefs(line, sed_cmd->string);
+                       /* Flag that something has changed */
+                       altered = 1;
+               } else {
+                       dbg("NOT inserting replacement at %d in '%s'", start, line);
+               }
+
+               /* If matched string is empty (f.e. "c*" pattern),
+                * copy verbatim one char after it before attempting more matches
+                */
+               prev_match_empty = (start == end);
+               if (prev_match_empty) {
+                       if (!line[end]) {
+                               tried_at_eol = 1;
+                       } else {
+                               pipe_putc(line[end]);
+                               end++;
+                       }
+               }
 
-               /* advance past the match */
-               line += G.regmatch[0].rm_eo;
-               /* flag that something has changed */
-               altered++;
+               /* Advance past the match */
+               dbg("line += %d", end);
+               line += end;
 
                /* if we're not doing this globally, get out now */
-               if (sed_cmd->which_match)
+               if (sed_cmd->which_match != 0)
                        break;
+ next:
+               /* Exit if we are at EOL and already tried matching at it */
+               if (*line == '\0') {
+                       if (tried_at_eol)
+                               break;
+                       tried_at_eol = 1;
+               }
 
-//maybe (G.regmatch[0].rm_eo ? REG_NOTBOL : 0) instead of unconditional REG_NOTBOL?
-       } while (*line && regexec(current_regex, line, 10, G.regmatch, REG_NOTBOL) != REG_NOMATCH);
+//maybe (end ? REG_NOTBOL : 0) instead of unconditional REG_NOTBOL?
+       } while (regexec(current_regex, line, 10, G.regmatch, REG_NOTBOL) != REG_NOMATCH);
 
        /* Copy rest of string into output pipeline */
        while (1) {
@@ -714,7 +850,7 @@ static sed_cmd_t *branch_to(char *label)
 {
        sed_cmd_t *sed_cmd;
 
-       for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
+       for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
                if (sed_cmd->cmd == ':' && sed_cmd->string && !strcmp(sed_cmd->string, label)) {
                        return sed_cmd;
                }
@@ -724,46 +860,100 @@ static sed_cmd_t *branch_to(char *label)
 
 static void append(char *s)
 {
-       llist_add_to_end(&G.append_head, xstrdup(s));
+       llist_add_to_end(&G.append_head, s);
+}
+
+/* Output line of text. */
+/* Note:
+ * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed.
+ * Without them, we had this:
+ * echo -n thingy >z1
+ * echo -n again >z2
+ * >znull
+ * sed "s/i/z/" z1 z2 znull | hexdump -vC
+ * output:
+ * gnu sed 4.1.5:
+ * 00000000  74 68 7a 6e 67 79 0a 61  67 61 7a 6e              |thzngy.agazn|
+ * bbox:
+ * 00000000  74 68 7a 6e 67 79 61 67  61 7a 6e                 |thzngyagazn|
+ */
+enum {
+       NO_EOL_CHAR = 1,
+       LAST_IS_NUL = 2,
+};
+static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char)
+{
+       char lpc = *last_puts_char;
+
+       /* Need to insert a '\n' between two files because first file's
+        * last line wasn't terminated? */
+       if (lpc != '\n' && lpc != '\0') {
+               fputc('\n', file);
+               lpc = '\n';
+       }
+       fputs(s, file);
+
+       /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */
+       if (s[0])
+               lpc = 'x';
+
+       /* had trailing '\0' and it was last char of file? */
+       if (last_gets_char == LAST_IS_NUL) {
+               fputc('\0', file);
+               lpc = 'x'; /* */
+       } else
+       /* had trailing '\n' or '\0'? */
+       if (last_gets_char != NO_EOL_CHAR) {
+               fputc(last_gets_char, file);
+               lpc = last_gets_char;
+       }
+
+       if (ferror(file)) {
+               xfunc_error_retval = 4;  /* It's what gnu sed exits with... */
+               bb_error_msg_and_die(bb_msg_write_error);
+       }
+       *last_puts_char = lpc;
 }
 
-static void flush_append(void)
+static void flush_append(char *last_puts_char, char last_gets_char)
 {
        char *data;
 
        /* Output appended lines. */
        while ((data = (char *)llist_pop(&G.append_head))) {
-               fprintf(G.nonstdout, "%s\n", data);
+               puts_maybe_newline(data, G.nonstdout, last_puts_char, last_gets_char);
                free(data);
        }
 }
 
-static void add_input_file(FILE *file)
-{
-       G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count);
-       G.input_file_list[G.input_file_count++] = file;
-}
-
 /* Get next line of input from G.input_file_list, flushing append buffer and
  * noting if we ran out of files without a newline on the last line we read.
  */
-enum {
-       NO_EOL_CHAR = 1,
-       LAST_IS_NUL = 2,
-};
-static char *get_next_line(char *gets_char)
+static char *get_next_line(char *gets_char, char *last_puts_char, char last_gets_char)
 {
        char *temp = NULL;
        int len;
        char gc;
 
-       flush_append();
+       flush_append(last_puts_char, last_gets_char);
 
        /* will be returned if last line in the file
         * doesn't end with either '\n' or '\0' */
        gc = NO_EOL_CHAR;
-       while (G.current_input_file < G.input_file_count) {
-               FILE *fp = G.input_file_list[G.current_input_file];
+       for (; G.current_input_file <= G.last_input_file; G.current_input_file++) {
+               FILE *fp = G.current_fp;
+               if (!fp) {
+                       const char *path = G.input_file_list[G.current_input_file];
+                       fp = stdin;
+                       if (path != bb_msg_standard_input) {
+                               fp = fopen_or_warn(path, "r");
+                               if (!fp) {
+                                       G.exitcode = EXIT_FAILURE;
+                                       continue;
+                               }
+                       }
+                       G.current_fp = fp;
+               }
                /* Read line up to a newline or NUL byte, inclusive,
                 * return malloc'ed char[]. length of the chunk read
                 * is stored in len. NULL if EOF/error */
@@ -794,61 +984,13 @@ static char *get_next_line(char *gets_char)
                 * (note: *no* newline after "b bang"!) */
                }
                /* Close this file and advance to next one */
-               fclose(fp);
-               G.current_input_file++;
+               fclose_if_not_stdin(fp);
+               G.current_fp = NULL;
        }
        *gets_char = gc;
        return temp;
 }
 
-/* Output line of text. */
-/* Note:
- * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed.
- * Without them, we had this:
- * echo -n thingy >z1
- * echo -n again >z2
- * >znull
- * sed "s/i/z/" z1 z2 znull | hexdump -vC
- * output:
- * gnu sed 4.1.5:
- * 00000000  74 68 7a 6e 67 79 0a 61  67 61 7a 6e              |thzngy.agazn|
- * bbox:
- * 00000000  74 68 7a 6e 67 79 61 67  61 7a 6e                 |thzngyagazn|
- */
-static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char)
-{
-       char lpc = *last_puts_char;
-
-       /* Need to insert a '\n' between two files because first file's
-        * last line wasn't terminated? */
-       if (lpc != '\n' && lpc != '\0') {
-               fputc('\n', file);
-               lpc = '\n';
-       }
-       fputs(s, file);
-
-       /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */
-       if (s[0])
-               lpc = 'x';
-
-       /* had trailing '\0' and it was last char of file? */
-       if (last_gets_char == LAST_IS_NUL) {
-               fputc('\0', file);
-               lpc = 'x'; /* */
-       } else
-       /* had trailing '\n' or '\0'? */
-       if (last_gets_char != NO_EOL_CHAR) {
-               fputc(last_gets_char, file);
-               lpc = last_gets_char;
-       }
-
-       if (ferror(file)) {
-               xfunc_error_retval = 4;  /* It's what gnu sed exits with... */
-               bb_error_msg_and_die(bb_msg_write_error);
-       }
-       *last_puts_char = lpc;
-}
-
 #define sed_puts(s, n) (puts_maybe_newline(s, G.nonstdout, &last_puts_char, n))
 
 static int beg_match(sed_cmd_t *sed_cmd, const char *pattern_space)
@@ -871,7 +1013,7 @@ static void process_files(void)
        int substituted;
 
        /* Prime the pump */
-       next_line = get_next_line(&next_gets_char);
+       next_line = get_next_line(&next_gets_char, &last_puts_char, '\n' /*last_gets_char*/);
 
        /* Go through every line in each file */
  again:
@@ -885,29 +1027,29 @@ static void process_files(void)
 
        /* Read one line in advance so we can act on the last line,
         * the '$' address */
-       next_line = get_next_line(&next_gets_char);
+       next_line = get_next_line(&next_gets_char, &last_puts_char, last_gets_char);
        linenum++;
 
        /* For every line, go through all the commands */
  restart:
-       for (sed_cmd = G.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
+       for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
                int old_matched, matched;
 
                old_matched = sed_cmd->in_match;
 
                /* Determine if this command matches this line: */
 
-               //bb_error_msg("match1:%d", sed_cmd->in_match);
-               //bb_error_msg("match2:%d", (!sed_cmd->beg_line && !sed_cmd->end_line
-               //              && !sed_cmd->beg_match && !sed_cmd->end_match));
-               //bb_error_msg("match3:%d", (sed_cmd->beg_line > 0
-               //      && (sed_cmd->end_line || sed_cmd->end_match
-               //          ? (sed_cmd->beg_line <= linenum)
-               //          : (sed_cmd->beg_line == linenum)
-               //          )
-               //      )
-               //bb_error_msg("match4:%d", (beg_match(sed_cmd, pattern_space)));
-               //bb_error_msg("match5:%d", (sed_cmd->beg_line == -1 && next_line == NULL));
+               dbg("match1:%d", sed_cmd->in_match);
+               dbg("match2:%d", (!sed_cmd->beg_line && !sed_cmd->end_line
+                               && !sed_cmd->beg_match && !sed_cmd->end_match));
+               dbg("match3:%d", (sed_cmd->beg_line > 0
+                       && (sed_cmd->end_line || sed_cmd->end_match
+                           ? (sed_cmd->beg_line <= linenum)
+                           : (sed_cmd->beg_line == linenum)
+                           )
+                       ));
+               dbg("match4:%d", (beg_match(sed_cmd, pattern_space)));
+               dbg("match5:%d", (sed_cmd->beg_line == -1 && next_line == NULL));
 
                /* Are we continuing a previous multi-line match? */
                sed_cmd->in_match = sed_cmd->in_match
@@ -918,7 +1060,14 @@ static void process_files(void)
                        || (sed_cmd->beg_line > 0
                            && (sed_cmd->end_line || sed_cmd->end_match
                                  /* note: even if end is numeric and is < linenum too,
-                                  * GNU sed matches! We match too */
+                                  * GNU sed matches! We match too, therefore we don't
+                                  * check here that linenum <= end.
+                                  * Example:
+                                  * printf '1\n2\n3\n4\n' | sed -n '1{N;N;d};1p;2,3p;3p;4p'
+                                  * first three input lines are deleted;
+                                  * 4th line is matched and printed
+                                  * by "2,3" (!) and by "4" ranges
+                                  */
                                ? (sed_cmd->beg_line <= linenum)    /* N,end */
                                : (sed_cmd->beg_line == linenum)    /* N */
                                )
@@ -931,27 +1080,29 @@ static void process_files(void)
                /* Snapshot the value */
                matched = sed_cmd->in_match;
 
-               //bb_error_msg("cmd:'%c' matched:%d beg_line:%d end_line:%d linenum:%d",
-               //sed_cmd->cmd, matched, sed_cmd->beg_line, sed_cmd->end_line, linenum);
+               dbg("cmd:'%c' matched:%d beg_line:%d end_line:%d linenum:%d",
+                       sed_cmd->cmd, matched, sed_cmd->beg_line, sed_cmd->end_line, linenum);
 
                /* Is this line the end of the current match? */
 
                if (matched) {
                        /* once matched, "n,xxx" range is dead, disabling it */
-                       if (sed_cmd->beg_line > 0)
+                       if (sed_cmd->beg_line > 0) {
                                sed_cmd->beg_line = -2;
+                       }
                        sed_cmd->in_match = !(
                                /* has the ending line come, or is this a single address command? */
-                               (sed_cmd->end_line ?
-                                       sed_cmd->end_line == -1 ?
-                                               !next_line
+                               (sed_cmd->end_line
+                                       ? sed_cmd->end_line == -1
+                                               !next_line
                                                : (sed_cmd->end_line <= linenum)
                                        : !sed_cmd->end_match
                                )
                                /* or does this line matches our last address regex */
                                || (sed_cmd->end_match && old_matched
                                     && (regexec(sed_cmd->end_match,
-                                                pattern_space, 0, NULL, 0) == 0))
+                                               pattern_space, 0, NULL, 0) == 0)
+                               )
                        );
                }
 
@@ -985,6 +1136,8 @@ static void process_files(void)
                }
 
                /* actual sedding */
+               dbg("pattern_space:'%s' next_line:'%s' cmd:%c",
+                               pattern_space, next_line, sed_cmd->cmd);
                switch (sed_cmd->cmd) {
 
                /* Print line number */
@@ -1031,6 +1184,7 @@ static void process_files(void)
                case 's':
                        if (!do_subst_command(sed_cmd, &pattern_space))
                                break;
+                       dbg("do_subst_command succeeded:'%s'", pattern_space);
                        substituted |= 1;
 
                        /* handle p option */
@@ -1045,7 +1199,7 @@ static void process_files(void)
 
                /* Append line to linked list to be printed later */
                case 'a':
-                       append(sed_cmd->string);
+                       append(xstrdup(sed_cmd->string));
                        break;
 
                /* Insert text before this line */
@@ -1067,11 +1221,10 @@ static void process_files(void)
                        rfile = fopen_for_read(sed_cmd->string);
                        if (rfile) {
                                char *line;
-
                                while ((line = xmalloc_fgetline(rfile))
                                                != NULL)
                                        append(line);
-                               xprint_and_close_file(rfile);
+                               fclose(rfile);
                        }
 
                        break;
@@ -1092,7 +1245,7 @@ static void process_files(void)
                                free(pattern_space);
                                pattern_space = next_line;
                                last_gets_char = next_gets_char;
-                               next_line = get_next_line(&next_gets_char);
+                               next_line = get_next_line(&next_gets_char, &last_puts_char, last_gets_char);
                                substituted = 0;
                                linenum++;
                                break;
@@ -1111,10 +1264,16 @@ static void process_files(void)
                {
                        int len;
                        /* If no next line, jump to end of script and exit. */
+                       /* http://www.gnu.org/software/sed/manual/sed.html:
+                        * "Most versions of sed exit without printing anything
+                        * when the N command is issued on the last line of
+                        * a file. GNU sed prints pattern space before exiting
+                        * unless of course the -n command switch has been
+                        * specified. This choice is by design."
+                        */
                        if (next_line == NULL) {
-                               free(next_line);
-                               next_line = NULL;
-                               goto discard_line;
+                               //goto discard_line;
+                               goto discard_commands; /* GNU behavior */
                        }
                        /* Append next_line, read new next_line. */
                        len = strlen(pattern_space);
@@ -1122,7 +1281,7 @@ static void process_files(void)
                        pattern_space[len] = '\n';
                        strcpy(pattern_space + len+1, next_line);
                        last_gets_char = next_gets_char;
-                       next_line = get_next_line(&next_gets_char);
+                       next_line = get_next_line(&next_gets_char, &last_puts_char, last_gets_char);
                        linenum++;
                        break;
                }
@@ -1226,7 +1385,7 @@ static void process_files(void)
 
        /* Delete and such jump here. */
  discard_line:
-       flush_append();
+       flush_append(&last_puts_char, last_gets_char);
        free(pattern_space);
 
        goto again;
@@ -1235,7 +1394,7 @@ static void process_files(void)
 /* It is possible to have a command line argument with embedded
  * newlines.  This counts as multiple command lines.
  * However, newline can be escaped: 's/e/z\<newline>z/'
- * We check for this.
+ * add_cmd() handles this.
  */
 
 static void add_cmd_block(char *cmdstr)
@@ -1245,22 +1404,8 @@ static void add_cmd_block(char *cmdstr)
        cmdstr = sv = xstrdup(cmdstr);
        do {
                eol = strchr(cmdstr, '\n');
- next:
-               if (eol) {
-                       /* Count preceding slashes */
-                       int slashes = 0;
-                       char *sl = eol;
-
-                       while (sl != cmdstr && *--sl == '\\')
-                               slashes++;
-                       /* Odd number of preceding slashes - newline is escaped */
-                       if (slashes & 1) {
-                               overlapping_strcpy(eol - 1, eol);
-                               eol = strchr(eol, '\n');
-                               goto next;
-                       }
+               if (eol)
                        *eol = '\0';
-               }
                add_cmd(cmdstr);
                cmdstr = eol + 1;
        } while (eol);
@@ -1270,12 +1415,20 @@ static void add_cmd_block(char *cmdstr)
 int sed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int sed_main(int argc UNUSED_PARAM, char **argv)
 {
-       enum {
-               OPT_in_place = 1 << 0,
-       };
        unsigned opt;
        llist_t *opt_e, *opt_f;
-       int status = EXIT_SUCCESS;
+       char *opt_i;
+
+#if ENABLE_LONG_OPTS
+       static const char sed_longopts[] ALIGN1 =
+               /* name             has_arg             short */
+               "in-place\0"        Optional_argument   "i"
+               "regexp-extended\0" No_argument         "r"
+               "quiet\0"           No_argument         "n"
+               "silent\0"          No_argument         "n"
+               "expression\0"      Required_argument   "e"
+               "file\0"            Required_argument   "f";
+#endif
 
        INIT_G();
 
@@ -1283,24 +1436,35 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
        if (ENABLE_FEATURE_CLEAN_UP) atexit(sed_free_and_close_stuff);
 
        /* Lie to autoconf when it starts asking stupid questions. */
-       if (argv[1] && !strcmp(argv[1], "--version")) {
+       if (argv[1] && strcmp(argv[1], "--version") == 0) {
                puts("This is not GNU sed version 4.0");
                return 0;
        }
 
        /* do normal option parsing */
        opt_e = opt_f = NULL;
+       opt_i = NULL;
        opt_complementary = "e::f::" /* can occur multiple times */
                            "nn"; /* count -n */
-       opt = getopt32(argv, "irne:f:", &opt_e, &opt_f,
+
+       IF_LONG_OPTS(applet_long_options = sed_longopts);
+
+       /* -i must be first, to match OPT_in_place definition */
+       /* -E is a synonym of -r:
+        * GNU sed 4.2.1 mentions it in neither --help
+        * nor manpage, but does recognize it.
+        */
+       opt = getopt32(argv, "i::rEne:f:", &opt_i, &opt_e, &opt_f,
                            &G.be_quiet); /* counter for -n */
        //argc -= optind;
        argv += optind;
        if (opt & OPT_in_place) { // -i
                atexit(cleanup_outname);
        }
-       if (opt & 0x2) G.regex_type |= REG_EXTENDED; // -r
-       //if (opt & 0x4) G.be_quiet++; // -n
+       if (opt & (2|4))
+               G.regex_type |= REG_EXTENDED; // -r or -E
+       //if (opt & 8)
+       //      G.be_quiet++; // -n (implemented with a counter instead)
        while (opt_e) { // -e
                add_cmd_block(llist_pop(&opt_e));
        }
@@ -1315,7 +1479,7 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
                fclose(cmdfile);
        }
        /* if we didn't get a pattern from -e or -f, use argv[0] */
-       if (!(opt & 0x18)) {
+       if (!(opt & 0x30)) {
                if (!*argv)
                        bb_show_usage();
                add_cmd_block(*argv++);
@@ -1329,62 +1493,70 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
        /* argv[0..(argc-1)] should be names of file to process. If no
         * files were specified or '-' was specified, take input from stdin.
         * Otherwise, we process all the files specified. */
-       if (argv[0] == NULL) {
+       G.input_file_list = argv;
+       if (!argv[0]) {
                if (opt & OPT_in_place)
                        bb_error_msg_and_die(bb_msg_requires_arg, "-i");
-               add_input_file(stdin);
+               argv[0] = (char*)bb_msg_standard_input;
+               /* G.last_input_file = 0; - already is */
        } else {
-               int i;
-               FILE *file;
+               goto start;
 
-               for (i = 0; argv[i]; i++) {
+               for (; *argv; argv++) {
                        struct stat statbuf;
                        int nonstdoutfd;
+                       sed_cmd_t *sed_cmd;
 
-                       if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) {
-                               add_input_file(stdin);
-                               process_files();
-                               continue;
-                       }
-                       file = fopen_or_warn(argv[i], "r");
-                       if (!file) {
-                               status = EXIT_FAILURE;
-                               continue;
-                       }
+                       G.last_input_file++;
+ start:
                        if (!(opt & OPT_in_place)) {
-                               add_input_file(file);
+                               if (LONE_DASH(*argv)) {
+                                       *argv = (char*)bb_msg_standard_input;
+                                       process_files();
+                               }
                                continue;
                        }
 
-                       G.outname = xasprintf("%sXXXXXX", argv[i]);
-                       nonstdoutfd = mkstemp(G.outname);
-                       if (-1 == nonstdoutfd)
-                               bb_perror_msg_and_die("can't create temp file %s", G.outname);
+                       /* -i: process each FILE separately: */
+
+                       G.outname = xasprintf("%sXXXXXX", *argv);
+                       nonstdoutfd = xmkstemp(G.outname);
                        G.nonstdout = xfdopen_for_write(nonstdoutfd);
 
                        /* Set permissions/owner of output file */
-                       fstat(fileno(file), &statbuf);
+                       stat(*argv, &statbuf);
                        /* chmod'ing AFTER chown would preserve suid/sgid bits,
                         * but GNU sed 4.2.1 does not preserve them either */
                        fchmod(nonstdoutfd, statbuf.st_mode);
                        fchown(nonstdoutfd, statbuf.st_uid, statbuf.st_gid);
-                       add_input_file(file);
+
                        process_files();
                        fclose(G.nonstdout);
-
                        G.nonstdout = stdout;
-                       /* unlink(argv[i]); */
-                       xrename(G.outname, argv[i]);
+
+                       if (opt_i) {
+                               char *backupname = xasprintf("%s%s", *argv, opt_i);
+                               xrename(*argv, backupname);
+                               free(backupname);
+                       }
+                       /* else unlink(*argv); - rename below does this */
+                       xrename(G.outname, *argv); //TODO: rollback backup on error?
                        free(G.outname);
                        G.outname = NULL;
+
+                       /* Re-enable disabled range matches */
+                       for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
+                               sed_cmd->beg_line = sed_cmd->beg_line_orig;
+                       }
                }
                /* Here, to handle "sed 'cmds' nonexistent_file" case we did:
-                * if (G.current_input_file >= G.input_file_count)
-                *      return status;
+                * if (G.current_input_file[G.current_input_file] == NULL)
+                *      return G.exitcode;
                 * but it's not needed since process_files() works correctly
                 * in this case too. */
        }
+
        process_files();
 
-       return status;
+       return G.exitcode;
 }
index 73e095c..097f309 100644 (file)
@@ -3,7 +3,7 @@
  * tiny vi.c: A small 'vi' clone
  * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*
  *     add :help command
  *     :map macros
  *     if mark[] values were line numbers rather than pointers
- *        it would be easier to change the mark when add/delete lines
+ *      it would be easier to change the mark when add/delete lines
  *     More intelligence in refresh()
  *     ":r !cmd"  and  "!cmd"  to filter text through an external command
  *     A true "undo" facility
  *     An "ex" line oriented mode- maybe using "cmdedit"
  */
 
+//config:config VI
+//config:      bool "vi"
+//config:      default y
+//config:      help
+//config:        'vi' is a text editor. More specifically, it is the One True
+//config:        text editor <grin>. It does, however, have a rather steep
+//config:        learning curve. If you are not already comfortable with 'vi'
+//config:        you may wish to use something else.
+//config:
+//config:config FEATURE_VI_MAX_LEN
+//config:      int "Maximum screen width in vi"
+//config:      range 256 16384
+//config:      default 4096
+//config:      depends on VI
+//config:      help
+//config:        Contrary to what you may think, this is not eating much.
+//config:        Make it smaller than 4k only if you are very limited on memory.
+//config:
+//config:config FEATURE_VI_8BIT
+//config:      bool "Allow vi to display 8-bit chars (otherwise shows dots)"
+//config:      default n
+//config:      depends on VI
+//config:      help
+//config:        If your terminal can display characters with high bit set,
+//config:        you may want to enable this. Note: vi is not Unicode-capable.
+//config:        If your terminal combines several 8-bit bytes into one character
+//config:        (as in Unicode mode), this will not work properly.
+//config:
+//config:config FEATURE_VI_COLON
+//config:      bool "Enable \":\" colon commands (no \"ex\" mode)"
+//config:      default y
+//config:      depends on VI
+//config:      help
+//config:        Enable a limited set of colon commands for vi. This does not
+//config:        provide an "ex" mode.
+//config:
+//config:config FEATURE_VI_YANKMARK
+//config:      bool "Enable yank/put commands and mark cmds"
+//config:      default y
+//config:      depends on VI
+//config:      help
+//config:        This will enable you to use yank and put, as well as mark in
+//config:        busybox vi.
+//config:
+//config:config FEATURE_VI_SEARCH
+//config:      bool "Enable search and replace cmds"
+//config:      default y
+//config:      depends on VI
+//config:      help
+//config:        Select this if you wish to be able to do search and replace in
+//config:        busybox vi.
+//config:
+//config:config FEATURE_VI_REGEX_SEARCH
+//config:      bool "Enable regex in search and replace"
+//config:      default n   # Uses GNU regex, which may be unavailable. FIXME
+//config:      depends on FEATURE_VI_SEARCH
+//config:      help
+//config:        Use extended regex search.
+//config:
+//config:config FEATURE_VI_USE_SIGNALS
+//config:      bool "Catch signals"
+//config:      default y
+//config:      depends on VI
+//config:      help
+//config:        Selecting this option will make busybox vi signal aware. This will
+//config:        make busybox vi support SIGWINCH to deal with Window Changes, catch
+//config:        Ctrl-Z and Ctrl-C and alarms.
+//config:
+//config:config FEATURE_VI_DOT_CMD
+//config:      bool "Remember previous cmd and \".\" cmd"
+//config:      default y
+//config:      depends on VI
+//config:      help
+//config:        Make busybox vi remember the last command and be able to repeat it.
+//config:
+//config:config FEATURE_VI_READONLY
+//config:      bool "Enable -R option and \"view\" mode"
+//config:      default y
+//config:      depends on VI
+//config:      help
+//config:        Enable the read-only command line option, which allows the user to
+//config:        open a file in read-only mode.
+//config:
+//config:config FEATURE_VI_SETOPTS
+//config:      bool "Enable set-able options, ai ic showmatch"
+//config:      default y
+//config:      depends on VI
+//config:      help
+//config:        Enable the editor to set some (ai, ic, showmatch) options.
+//config:
+//config:config FEATURE_VI_SET
+//config:      bool "Support for :set"
+//config:      default y
+//config:      depends on VI
+//config:      help
+//config:        Support for ":set".
+//config:
+//config:config FEATURE_VI_WIN_RESIZE
+//config:      bool "Handle window resize"
+//config:      default y
+//config:      depends on VI
+//config:      help
+//config:        Make busybox vi behave nicely with terminals that get resized.
+//config:
+//config:config FEATURE_VI_ASK_TERMINAL
+//config:      bool "Use 'tell me cursor position' ESC sequence to measure window"
+//config:      default y
+//config:      depends on VI
+//config:      help
+//config:        If terminal size can't be retrieved and $LINES/$COLUMNS are not set,
+//config:        this option makes vi perform a last-ditch effort to find it:
+//config:        position cursor to 999,999 and ask terminal to report real
+//config:        cursor position using "ESC [ 6 n" escape sequence, then read stdin.
+//config:
+//config:        This is not clean but helps a lot on serial lines and such.
+
+//applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_VI) += vi.o
+
+//usage:#define vi_trivial_usage
+//usage:       "[OPTIONS] [FILE]..."
+//usage:#define vi_full_usage "\n\n"
+//usage:       "Edit FILE\n"
+//usage:       IF_FEATURE_VI_COLON(
+//usage:     "\n       -c CMD  Initial command to run ($EXINIT also available)"
+//usage:       )
+//usage:       IF_FEATURE_VI_READONLY(
+//usage:     "\n       -R      Read-only"
+//usage:       )
+//usage:     "\n       -H      List available features"
+
 #include "libbb.h"
+/* Should be after libbb.h: on some systems regex.h needs sys/types.h: */
+#if ENABLE_FEATURE_VI_REGEX_SEARCH
+# include <regex.h>
+#endif
 
 /* the CRASHME code is unmaintained, and doesn't currently build */
 #define ENABLE_FEATURE_VI_CRASHME 0
 
 /* 0x9b is Meta-ESC */
 #if ENABLE_FEATURE_VI_8BIT
-#define Isprint(c) ((unsigned char)(c) >= ' ' && (c) != 0x7f && (unsigned char)(c) != 0x9b)
+# define Isprint(c) ((unsigned char)(c) >= ' ' && (c) != 0x7f && (unsigned char)(c) != 0x9b)
 #else
-#define Isprint(c) ((unsigned char)(c) >= ' ' && (unsigned char)(c) < 0x7f)
+# define Isprint(c) ((unsigned char)(c) >= ' ' && (unsigned char)(c) < 0x7f)
 #endif
 
 #endif
@@ -58,20 +194,29 @@ enum {
        MAX_SCR_ROWS = CONFIG_FEATURE_VI_MAX_LEN,
 };
 
-/* vt102 typical ESC sequence */
-/* terminal standout start/normal ESC sequence */
-#define SOs "\033[7m"
-#define SOn "\033[0m"
-/* terminal bell sequence */
-#define bell "\007"
-/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
-#define Ceol "\033[K"
-#define Ceos "\033[J"
-/* Cursor motion arbitrary destination ESC sequence */
-#define CMrc "\033[%u;%uH"
-/* Cursor motion up and down ESC sequence */
-#define CMup "\033[A"
-#define CMdown "\n"
+/* VT102 ESC sequences.
+ * See "Xterm Control Sequences"
+ * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+ */
+/* Inverse/Normal text */
+#define ESC_BOLD_TEXT "\033[7m"
+#define ESC_NORM_TEXT "\033[0m"
+/* Bell */
+#define ESC_BELL "\007"
+/* Clear-to-end-of-line */
+#define ESC_CLEAR2EOL "\033[K"
+/* Clear-to-end-of-screen.
+ * (We use default param here.
+ * Full sequence is "ESC [ <num> J",
+ * <num> is 0/1/2 = "erase below/above/all".)
+ */
+#define ESC_CLEAR2EOS "\033[J"
+/* Cursor to given coordinate (1,1: top left) */
+#define ESC_SET_CURSOR_POS "\033[%u;%uH"
+//UNUSED
+///* Cursor up and down */
+//#define ESC_CURSOR_UP "\033[A"
+//#define ESC_CURSOR_DOWN "\n"
 
 #if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
 // cmds modifying text[]
@@ -134,7 +279,6 @@ struct globals {
        smallint cmd_mode;       // 0=command  1=insert 2=replace
        int file_modified;       // buffer contents changed (counter, not flag!)
        int last_file_modified;  // = -1;
-       int fn_start;            // index of first cmd line file name
        int save_argc;           // how many file names on cmd line
        int cmdcnt;              // repetition count
        unsigned rows, columns;  // the terminal screen is this size
@@ -160,9 +304,6 @@ struct globals {
        int lmc_len;             // length of last_modifying_cmd
        char *ioq, *ioq_start;   // pointer to string for get_one_char to "read"
 #endif
-#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
-       int last_row;            // where the cursor was last moved to
-#endif
 #if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
        int my_pid;
 #endif
@@ -219,7 +360,6 @@ struct globals {
 #define cmd_mode                (G.cmd_mode           )
 #define file_modified           (G.file_modified      )
 #define last_file_modified      (G.last_file_modified )
-#define fn_start                (G.fn_start           )
 #define save_argc               (G.save_argc          )
 #define cmdcnt                  (G.cmdcnt             )
 #define rows                    (G.rows               )
@@ -247,7 +387,6 @@ struct globals {
 #define lmc_len                 (G.lmc_len            )
 #define ioq                     (G.ioq                )
 #define ioq_start               (G.ioq_start          )
-#define last_row                (G.last_row           )
 #define my_pid                  (G.my_pid             )
 #define last_search_pattern     (G.last_search_pattern)
 
@@ -328,10 +467,7 @@ static int file_size(const char *);   // what is the byte size of "fn"
 // file_insert might reallocate text[]!
 static int file_insert(const char *, char *, int);
 static int file_write(char *, char *, char *);
-#if !ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
-#define place_cursor(a, b, optimize) place_cursor(a, b)
-#endif
-static void place_cursor(int, int, int);
+static void place_cursor(int, int);
 static void screen_erase(void);
 static void clear_to_eol(void);
 static void clear_to_eos(void);
@@ -342,6 +478,7 @@ static void flash(int);             // flash the terminal screen
 static void show_status_line(void);    // put a message on the bottom line
 static void status_line(const char *, ...);     // print to status buf
 static void status_line_bold(const char *, ...);
+static void status_line_bold_errno(const char *fn);
 static void not_implemented(const char *); // display "Not implemented" message
 static int format_edit_status(void);   // format file status on status line
 static void redraw(int);       // force a full screen refresh
@@ -354,7 +491,6 @@ static void Hit_Return(void);
 
 #if ENABLE_FEATURE_VI_SEARCH
 static char *char_search(char *, const char *, int, int);      // search for pattern starting at p
-static int mycmp(const char *, const char *, int);     // string cmp based in "ignorecase"
 #endif
 #if ENABLE_FEATURE_VI_COLON
 static char *get_one_address(char *, int *);   // get colon addr, if present
@@ -417,7 +553,8 @@ int vi_main(int argc, char **argv)
        }
 #endif
 
-       vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE;
+       // autoindent is not default in vim 7.3
+       vi_setops = /*VI_AUTOINDENT |*/ VI_SHOWMATCH | VI_IGNORECASE;
        //  1-  process $HOME/.exrc file (not inplemented yet)
        //  2-  process EXINIT variable from environment
        //  3-  process command line args
@@ -443,7 +580,7 @@ int vi_main(int argc, char **argv)
 #if ENABLE_FEATURE_VI_COLON
                case 'c':               // cmd line vi command
                        if (*optarg)
-                               initial_cmds[initial_cmds[0] != 0] = xstrndup(optarg, MAX_INPUT_LEN);
+                               initial_cmds[initial_cmds[0] != NULL] = xstrndup(optarg, MAX_INPUT_LEN);
                        break;
 #endif
                case 'H':
@@ -456,16 +593,21 @@ int vi_main(int argc, char **argv)
        }
 
        // The argv array can be used by the ":next"  and ":rewind" commands
-       // save optind.
-       fn_start = optind;      // remember first file name for :next and :rew
-       save_argc = argc;
+       argv += optind;
+       argc -= optind;
 
        //----- This is the main file handling loop --------------
+       save_argc = argc;
+       optind = 0;
+       // "Save cursor, use alternate screen buffer, clear screen"
+       write1("\033[?1049h");
        while (1) {
                edit_file(argv[optind]); /* param might be NULL */
                if (++optind >= argc)
                        break;
        }
+       // "Use normal screen buffer, restore cursor"
+       write1("\033[?1049l");
        //-----------------------------------------------------------
 
        return 0;
@@ -523,7 +665,6 @@ static void edit_file(char *fn)
 #define cur_line edit_file__cur_line
 #endif
        int c;
-       int size;
 #if ENABLE_FEATURE_VI_USE_SIGNALS
        int sig;
 #endif
@@ -532,7 +673,6 @@ static void edit_file(char *fn)
        rawmode();
        rows = 24;
        columns = 80;
-       size = 0;
        IF_FEATURE_VI_ASK_TERMINAL(G.get_rowcol_error =) query_screen_dimensions();
 #if ENABLE_FEATURE_VI_ASK_TERMINAL
        if (G.get_rowcol_error /* TODO? && no input on stdin */) {
@@ -880,7 +1020,7 @@ static void colon(char *buf)
        } else if (strncmp(cmd, "edit", i) == 0) {      // Edit a file
                // don't edit, if the current file has been modified
                if (file_modified && !useforce) {
-                       status_line_bold("No write since last change (:edit! overrides)");
+                       status_line_bold("No write since last change (:%s! overrides)", cmd);
                        goto ret;
                }
                if (args[0]) {
@@ -899,18 +1039,18 @@ static void colon(char *buf)
                        goto ret;
 
 #if ENABLE_FEATURE_VI_YANKMARK
-               if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
+               if (Ureg >= 0 && Ureg < 28) {
                        free(reg[Ureg]);        //   free orig line reg- for 'U'
-                       reg[Ureg]= 0;
+                       reg[Ureg] = NULL;
                }
-               if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
+               if (YDreg >= 0 && YDreg < 28) {
                        free(reg[YDreg]);       //   free default yank/delete register
-                       reg[YDreg]= 0;
+                       reg[YDreg] = NULL;
                }
 #endif
                // how many lines in text[]?
                li = count_lines(text, end - 1);
-               status_line("\"%s\"%s"
+               status_line("'%s'%s"
                        IF_FEATURE_VI_READONLY("%s")
                        " %dL, %dC", current_filename,
                        (file_size(fn) < 0 ? " [New file]" : ""),
@@ -970,11 +1110,12 @@ static void colon(char *buf)
                Hit_Return();
        } else if (strncmp(cmd, "quit", i) == 0 // quit
                || strncmp(cmd, "next", i) == 0 // edit next file
+               || strncmp(cmd, "prev", i) == 0 // edit previous file
        ) {
                int n;
                if (useforce) {
-                       // force end of argv list
                        if (*cmd == 'q') {
+                               // force end of argv list
                                optind = save_argc;
                        }
                        editing = 0;
@@ -982,8 +1123,7 @@ static void colon(char *buf)
                }
                // don't exit if the file been modified
                if (file_modified) {
-                       status_line_bold("No write since last change (:%s! overrides)",
-                                (*cmd == 'q' ? "quit" : "next"));
+                       status_line_bold("No write since last change (:%s! overrides)", cmd);
                        goto ret;
                }
                // are there other file to edit
@@ -996,6 +1136,14 @@ static void colon(char *buf)
                        status_line_bold("No more files to edit");
                        goto ret;
                }
+               if (*cmd == 'p') {
+                       // are there previous files to edit
+                       if (optind < 1) {
+                               status_line_bold("No previous files to edit");
+                               goto ret;
+                       }
+                       optind -= 2;
+               }
                editing = 0;
        } else if (strncmp(cmd, "read", i) == 0) {      // read file into text[]
                fn = args;
@@ -1018,7 +1166,7 @@ static void colon(char *buf)
                        goto ret;       // nothing was inserted
                // how many lines in text[]?
                li = count_lines(q, q + ch - 1);
-               status_line("\"%s\""
+               status_line("'%s'"
                        IF_FEATURE_VI_READONLY("%s")
                        " %dL, %dC", fn,
                        IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
@@ -1031,10 +1179,10 @@ static void colon(char *buf)
                }
        } else if (strncmp(cmd, "rewind", i) == 0) {    // rewind cmd line args
                if (file_modified && !useforce) {
-                       status_line_bold("No write since last change (:rewind! overrides)");
+                       status_line_bold("No write since last change (:%s! overrides)", cmd);
                } else {
                        // reset the filenames to edit
-                       optind = fn_start - 1;
+                       optind = -1; /* start from 0th file */
                        editing = 0;
                }
 #if ENABLE_FEATURE_VI_SET
@@ -1043,7 +1191,7 @@ static void colon(char *buf)
                char *argp;
 #endif
                i = 0;                  // offset into args
-               // only blank is regarded as args delmiter. What about tab '\t' ?
+               // only blank is regarded as args delimiter. What about tab '\t'?
                if (!args[0] || strcasecmp(args, "all") == 0) {
                        // print out values of all options
 #if ENABLE_FEATURE_VI_SETOPTS
@@ -1084,51 +1232,53 @@ static void colon(char *buf)
 #endif /* FEATURE_VI_SET */
 #if ENABLE_FEATURE_VI_SEARCH
        } else if (cmd[0] == 's') {     // substitute a pattern with a replacement pattern
-               char *ls, *F, *R;
-               int gflag;
+               char *F, *R, *flags;
+               size_t len_F, len_R;
+               int gflag;              // global replace flag
 
                // F points to the "find" pattern
                // R points to the "replace" pattern
-               // replace the cmd line delimiters "/" with NULLs
-               gflag = 0;              // global replace flag
+               // replace the cmd line delimiters "/" with NULs
                c = orig_buf[1];        // what is the delimiter
                F = orig_buf + 2;       // start of "find"
                R = strchr(F, c);       // middle delimiter
                if (!R)
                        goto colon_s_fail;
+               len_F = R - F;
                *R++ = '\0';    // terminate "find"
-               buf1 = strchr(R, c);
-               if (!buf1)
+               flags = strchr(R, c);
+               if (!flags)
                        goto colon_s_fail;
-               *buf1++ = '\0'; // terminate "replace"
-               if (*buf1 == 'g') {     // :s/foo/bar/g
-                       buf1++;
-                       gflag++;        // turn on gflag
-               }
+               len_R = flags - R;
+               *flags++ = '\0';        // terminate "replace"
+               gflag = *flags;
+
                q = begin_line(q);
                if (b < 0) {    // maybe :s/foo/bar/
-                       q = begin_line(dot);    // start with cur line
-                       b = count_lines(text, q);       // cur line number
+                       q = begin_line(dot);      // start with cur line
+                       b = count_lines(text, q); // cur line number
                }
                if (e < 0)
                        e = b;          // maybe :.s/foo/bar/
+
                for (i = b; i <= e; i++) {      // so, :20,23 s \0 find \0 replace \0
-                       ls = q;         // orig line start
+                       char *ls = q;           // orig line start
+                       char *found;
  vc4:
-                       buf1 = char_search(q, F, FORWARD, LIMITED);     // search cur line only for "find"
-                       if (buf1) {
+                       found = char_search(q, F, FORWARD, LIMITED);    // search cur line only for "find"
+                       if (found) {
                                uintptr_t bias;
                                // we found the "find" pattern - delete it
-                               text_hole_delete(buf1, buf1 + strlen(F) - 1);
+                               text_hole_delete(found, found + len_F - 1);
                                // inset the "replace" patern
-                               bias = string_insert(buf1, R);  // insert the string
-                               buf1 += bias;
+                               bias = string_insert(found, R); // insert the string
+                               found += bias;
                                ls += bias;
                                /*q += bias; - recalculated anyway */
                                // check for "global"  :s/foo/bar/g
-                               if (gflag == 1) {
-                                       if ((buf1 + strlen(R)) < end_line(ls)) {
-                                               q = buf1 + strlen(R);
+                               if (gflag == 'g') {
+                                       if ((found + len_R) < end_line(ls)) {
+                                               q = found + len_R;
                                                goto vc4;       // don't let q move past cur line
                                        }
                                }
@@ -1149,7 +1299,7 @@ static void colon(char *buf)
                }
 #if ENABLE_FEATURE_VI_READONLY
                if (readonly_mode && !useforce) {
-                       status_line_bold("\"%s\" File is read only", fn);
+                       status_line_bold("'%s' is read only", fn);
                        goto ret;
                }
 #endif
@@ -1172,9 +1322,9 @@ static void colon(char *buf)
                }
                if (l < 0) {
                        if (l == -1)
-                               status_line_bold("\"%s\" %s", fn, strerror(errno));
+                               status_line_bold_errno(fn);
                } else {
-                       status_line("\"%s\" %dL, %dC", fn, li, l);
+                       status_line("'%s' %dL, %dC", fn, li, l);
                        if (q == text && r == end - 1 && l == ch) {
                                file_modified = 0;
                                last_file_modified = -1;
@@ -1551,24 +1701,84 @@ static char *new_screen(int ro, int co)
 }
 
 #if ENABLE_FEATURE_VI_SEARCH
+
+# if ENABLE_FEATURE_VI_REGEX_SEARCH
+
+// search for pattern starting at p
+static char *char_search(char *p, const char *pat, int dir, int range)
+{
+       struct re_pattern_buffer preg;
+       const char *err;
+       char *q;
+       int i;
+       int size;
+
+       re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
+       if (ignorecase)
+               re_syntax_options = RE_SYNTAX_POSIX_EXTENDED | RE_ICASE;
+
+       memset(&preg, 0, sizeof(preg));
+       err = re_compile_pattern(pat, strlen(pat), &preg);
+       if (err != NULL) {
+               status_line_bold("bad search pattern '%s': %s", pat, err);
+               return p;
+       }
+
+       // assume a LIMITED forward search
+       q = end - 1;
+       if (dir == BACK)
+               q = text;
+       // RANGE could be negative if we are searching backwards
+       range = q - p;
+       q = p;
+       size = range;
+       if (range < 0) {
+               size = -size;
+               q = p - size;
+               if (q < text)
+                       q = text;
+       }
+       // search for the compiled pattern, preg, in p[]
+       // range < 0: search backward
+       // range > 0: search forward
+       // 0 < start < size
+       // re_search() < 0: not found or error
+       // re_search() >= 0: index of found pattern
+       //           struct pattern   char     int   int    int    struct reg
+       // re_search(*pattern_buffer, *string, size, start, range, *regs)
+       i = re_search(&preg, q, size, /*start:*/ 0, range, /*struct re_registers*:*/ NULL);
+       regfree(&preg);
+       if (i < 0)
+               return NULL;
+       if (dir == FORWARD)
+               p = p + i;
+       else
+               p = p - i;
+       return p;
+}
+
+# else
+
+#  if ENABLE_FEATURE_VI_SETOPTS
 static int mycmp(const char *s1, const char *s2, int len)
 {
-       if (ENABLE_FEATURE_VI_SETOPTS && ignorecase) {
+       if (ignorecase) {
                return strncasecmp(s1, s2, len);
        }
        return strncmp(s1, s2, len);
 }
+#  else
+#   define mycmp strncmp
+#  endif
 
-// search for pattern starting at p
 static char *char_search(char *p, const char *pat, int dir, int range)
 {
-#ifndef REGEX_SEARCH
        char *start, *stop;
        int len;
 
        len = strlen(pat);
        if (dir == FORWARD) {
-               stop = end - 1; // assume range is p - end-1
+               stop = end - 1; // assume range is p..end-1
                if (range == LIMITED)
                        stop = next_line(p);    // range is to next line
                for (start = p; start < stop; start++) {
@@ -1577,7 +1787,7 @@ static char *char_search(char *p, const char *pat, int dir, int range)
                        }
                }
        } else if (dir == BACK) {
-               stop = text;    // assume range is text - p
+               stop = text;    // assume range is text..p
                if (range == LIMITED)
                        stop = prev_line(p);    // range is to prev line
                for (start = p - len; start >= stop; start--) {
@@ -1588,69 +1798,10 @@ static char *char_search(char *p, const char *pat, int dir, int range)
        }
        // pattern not found
        return NULL;
-#else /* REGEX_SEARCH */
-       char *q;
-       struct re_pattern_buffer preg;
-       int i;
-       int size, range;
-
-       re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
-       preg.translate = 0;
-       preg.fastmap = 0;
-       preg.buffer = 0;
-       preg.allocated = 0;
-
-       // assume a LIMITED forward search
-       q = next_line(p);
-       q = end_line(q);
-       q = end - 1;
-       if (dir == BACK) {
-               q = prev_line(p);
-               q = text;
-       }
-       // count the number of chars to search over, forward or backward
-       size = q - p;
-       if (size < 0)
-               size = p - q;
-       // RANGE could be negative if we are searching backwards
-       range = q - p;
+}
 
-       q = re_compile_pattern(pat, strlen(pat), &preg);
-       if (q != 0) {
-               // The pattern was not compiled
-               status_line_bold("bad search pattern: \"%s\": %s", pat, q);
-               i = 0;                  // return p if pattern not compiled
-               goto cs1;
-       }
+# endif
 
-       q = p;
-       if (range < 0) {
-               q = p - size;
-               if (q < text)
-                       q = text;
-       }
-       // search for the compiled pattern, preg, in p[]
-       // range < 0-  search backward
-       // range > 0-  search forward
-       // 0 < start < size
-       // re_search() < 0  not found or error
-       // re_search() > 0  index of found pattern
-       //            struct pattern    char     int    int    int     struct reg
-       // re_search (*pattern_buffer,  *string, size,  start, range,  *regs)
-       i = re_search(&preg, q, size, 0, range, 0);
-       if (i == -1) {
-               p = 0;
-               i = 0;                  // return NULL if pattern not found
-       }
- cs1:
-       if (dir == FORWARD) {
-               p = p + i;
-       } else {
-               p = p - i;
-       }
-       return p;
-#endif /* REGEX_SEARCH */
-}
 #endif /* FEATURE_VI_SEARCH */
 
 static char *char_insert(char *p, char c) // insert the char c at 'p'
@@ -1677,12 +1828,16 @@ static char *char_insert(char *p, char c) // insert the char c at 'p'
                        p = text_hole_delete(p, p);     // shrink buffer 1 char
                }
        } else {
+#if ENABLE_FEATURE_VI_SETOPTS
                // insert a char into text[]
                char *sp;               // "save p"
+#endif
 
                if (c == 13)
                        c = '\n';       // translate \r to \n
+#if ENABLE_FEATURE_VI_SETOPTS
                sp = p;                 // remember addr of insert
+#endif
                p += 1 + stupid_insert(p, c);   // insert the char
 #if ENABLE_FEATURE_VI_SETOPTS
                if (showmatch && strchr(")]}", *sp) != NULL) {
@@ -1763,11 +1918,11 @@ static int find_range(char **start, char **stop, char c)
                dot_end();              // find NL
                q = dot;
        } else {
-           // nothing -- this causes any other values of c to
-           // represent the one-character range under the
-           // cursor.  this is correct for ' ' and 'l', but
-           // perhaps no others.
-           //
+               // nothing -- this causes any other values of c to
+               // represent the one-character range under the
+               // cursor.  this is correct for ' ' and 'l', but
+               // perhaps no others.
+               //
        }
        if (q < p) {
                t = q;
@@ -1915,6 +2070,14 @@ static uintptr_t text_hole_make(char *p, int size)       // at "p", make a 'size' byte
                dot         += bias;
                end         += bias;
                p           += bias;
+#if ENABLE_FEATURE_VI_YANKMARK
+               {
+                       int i;
+                       for (i = 0; i < ARRAY_SIZE(mark); i++)
+                               if (mark[i])
+                                       mark[i] += bias;
+               }
+#endif
                text = new_text;
        }
        memmove(p + size, p, end - size - p);
@@ -2001,18 +2164,18 @@ static void show_help(void)
        "\n\tPattern searches with / and ?"
 #endif
 #if ENABLE_FEATURE_VI_DOT_CMD
-       "\n\tLast command repeat with \'.\'"
+       "\n\tLast command repeat with ."
 #endif
 #if ENABLE_FEATURE_VI_YANKMARK
        "\n\tLine marking with 'x"
        "\n\tNamed buffers with \"x"
 #endif
 #if ENABLE_FEATURE_VI_READONLY
-       "\n\tReadonly if vi is called as \"view\""
-       "\n\tReadonly with -R command line arg"
+       //not implemented: "\n\tReadonly if vi is called as \"view\""
+       //redundant: usage text says this too: "\n\tReadonly with -R command line arg"
 #endif
 #if ENABLE_FEATURE_VI_SET
-       "\n\tSome colon mode commands with \':\'"
+       "\n\tSome colon mode commands with :"
 #endif
 #if ENABLE_FEATURE_VI_SETOPTS
        "\n\tSettable options with \":set\""
@@ -2146,7 +2309,7 @@ static void rawmode(void)
 {
        tcgetattr(0, &term_orig);
        term_vi = term_orig;
-       term_vi.c_lflag &= (~ICANON & ~ECHO);   // leave ISIG ON- allow intr's
+       term_vi.c_lflag &= (~ICANON & ~ECHO);   // leave ISIG on - allow intr's
        term_vi.c_iflag &= (~IXON & ~ICRNL);
        term_vi.c_oflag &= (~ONLCR);
        term_vi.c_cc[VMIN] = 1;
@@ -2329,12 +2492,12 @@ static int file_insert(const char *fn, char *p, int update_ro_status)
 
        /* Validate file */
        if (stat(fn, &statbuf) < 0) {
-               status_line_bold("\"%s\" %s", fn, strerror(errno));
+               status_line_bold_errno(fn);
                goto fi0;
        }
        if (!S_ISREG(statbuf.st_mode)) {
                // This is not a regular file
-               status_line_bold("\"%s\" Not a regular file", fn);
+               status_line_bold("'%s' is not a regular file", fn);
                goto fi0;
        }
        if (p < text || p > end) {
@@ -2345,19 +2508,19 @@ static int file_insert(const char *fn, char *p, int update_ro_status)
        // read file to buffer
        fd = open(fn, O_RDONLY);
        if (fd < 0) {
-               status_line_bold("\"%s\" %s", fn, strerror(errno));
+               status_line_bold_errno(fn);
                goto fi0;
        }
-       size = statbuf.st_size;
+       size = (statbuf.st_size < INT_MAX ? (int)statbuf.st_size : INT_MAX);
        p += text_hole_make(p, size);
        cnt = safe_read(fd, p, size);
        if (cnt < 0) {
-               status_line_bold("\"%s\" %s", fn, strerror(errno));
+               status_line_bold_errno(fn);
                p = text_hole_delete(p, p + size - 1);  // un-do buffer insert
        } else if (cnt < size) {
                // There was a partial read, shrink unused space text[]
-               p = text_hole_delete(p + cnt, p + (size - cnt) - 1);    // un-do buffer insert
-               status_line_bold("can't read all of file \"%s\"", fn);
+               p = text_hole_delete(p + cnt, p + size - 1);    // un-do buffer insert
+               status_line_bold("can't read '%s'", fn);
        }
        if (cnt >= size)
                file_modified++;
@@ -2417,107 +2580,56 @@ static int file_write(char *fn, char *first, char *last)
 //  23,0    ...     23,79   <- status line
 
 //----- Move the cursor to row x col (count from 0, not 1) -------
-static void place_cursor(int row, int col, int optimize)
+static void place_cursor(int row, int col)
 {
-       char cm1[sizeof(CMrc) + sizeof(int)*3 * 2];
-#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
-       enum {
-               SZ_UP = sizeof(CMup),
-               SZ_DN = sizeof(CMdown),
-               SEQ_SIZE = SZ_UP > SZ_DN ? SZ_UP : SZ_DN,
-       };
-       char cm2[SEQ_SIZE * 5 + 32]; // bigger than worst case size
-#endif
-       char *cm;
+       char cm1[sizeof(ESC_SET_CURSOR_POS) + sizeof(int)*3 * 2];
 
        if (row < 0) row = 0;
        if (row >= rows) row = rows - 1;
        if (col < 0) col = 0;
        if (col >= columns) col = columns - 1;
 
-       //----- 1.  Try the standard terminal ESC sequence
-       sprintf(cm1, CMrc, row + 1, col + 1);
-       cm = cm1;
-
-#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
-       if (optimize && col < 16) {
-               char *screenp;
-               int Rrow = last_row;
-               int diff = Rrow - row;
-
-               if (diff < -5 || diff > 5)
-                       goto skip;
-
-               //----- find the minimum # of chars to move cursor -------------
-               //----- 2.  Try moving with discreet chars (Newline, [back]space, ...)
-               cm2[0] = '\0';
-
-               // move to the correct row
-               while (row < Rrow) {
-                       // the cursor has to move up
-                       strcat(cm2, CMup);
-                       Rrow--;
-               }
-               while (row > Rrow) {
-                       // the cursor has to move down
-                       strcat(cm2, CMdown);
-                       Rrow++;
-               }
-
-               // now move to the correct column
-               strcat(cm2, "\r");                      // start at col 0
-               // just send out orignal source char to get to correct place
-               screenp = &screen[row * columns];       // start of screen line
-               strncat(cm2, screenp, col);
-
-               // pick the shortest cursor motion to send out
-               if (strlen(cm2) < strlen(cm)) {
-                       cm = cm2;
-               }
- skip: ;
-       }
-       last_row = row;
-#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
-       write1(cm);
+       sprintf(cm1, ESC_SET_CURSOR_POS, row + 1, col + 1);
+       write1(cm1);
 }
 
 //----- Erase from cursor to end of line -----------------------
 static void clear_to_eol(void)
 {
-       write1(Ceol);   // Erase from cursor to end of line
+       write1(ESC_CLEAR2EOL);
 }
 
 static void go_bottom_and_clear_to_eol(void)
 {
-       place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
-       clear_to_eol(); // erase to end of line
+       place_cursor(rows - 1, 0);
+       clear_to_eol();
 }
 
 //----- Erase from cursor to end of screen -----------------------
 static void clear_to_eos(void)
 {
-       write1(Ceos);   // Erase from cursor to end of screen
+       write1(ESC_CLEAR2EOS);
 }
 
 //----- Start standout mode ------------------------------------
-static void standout_start(void) // send "start reverse video" sequence
+static void standout_start(void)
 {
-       write1(SOs);     // Start reverse video mode
+       write1(ESC_BOLD_TEXT);
 }
 
 //----- End standout mode --------------------------------------
-static void standout_end(void) // send "end reverse video" sequence
+static void standout_end(void)
 {
-       write1(SOn);     // End reverse video mode
+       write1(ESC_NORM_TEXT);
 }
 
 //----- Flash the screen  --------------------------------------
 static void flash(int h)
 {
-       standout_start();       // send "start reverse video" sequence
+       standout_start();
        redraw(TRUE);
        mysleep(h);
-       standout_end();         // send "end reverse video" sequence
+       standout_end();
        redraw(TRUE);
 }
 
@@ -2528,7 +2640,7 @@ static void Indicate_Error(void)
                return;                 // generate a random command
 #endif
        if (!err_method) {
-               write1(bell);   // send out a bell character
+               write1(ESC_BELL);
        } else {
                flash(10);
        }
@@ -2574,7 +2686,7 @@ static void show_status_line(void)
                        }
                        have_status_msg = 0;
                }
-               place_cursor(crow, ccol, FALSE);        // put cursor back in correct place
+               place_cursor(crow, ccol);  // put cursor back in correct place
        }
        fflush_all();
 }
@@ -2586,12 +2698,17 @@ static void status_line_bold(const char *format, ...)
        va_list args;
 
        va_start(args, format);
-       strcpy(status_buffer, SOs);     // Terminal standout mode on
-       vsprintf(status_buffer + sizeof(SOs)-1, format, args);
-       strcat(status_buffer, SOn);     // Terminal standout mode off
+       strcpy(status_buffer, ESC_BOLD_TEXT);
+       vsprintf(status_buffer + sizeof(ESC_BOLD_TEXT)-1, format, args);
+       strcat(status_buffer, ESC_NORM_TEXT);
        va_end(args);
 
-       have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
+       have_status_msg = 1 + sizeof(ESC_BOLD_TEXT) + sizeof(ESC_NORM_TEXT) - 2;
+}
+
+static void status_line_bold_errno(const char *fn)
+{
+       status_line_bold("'%s' %s", fn, strerror(errno));
 }
 
 // format status buffer
@@ -2623,8 +2740,8 @@ static void print_literal(char *buf, const char *s)
                c = *s;
                c_is_no_print = (c & 0x80) && !Isprint(c);
                if (c_is_no_print) {
-                       strcpy(d, SOn);
-                       d += sizeof(SOn)-1;
+                       strcpy(d, ESC_NORM_TEXT);
+                       d += sizeof(ESC_NORM_TEXT)-1;
                        c = '.';
                }
                if (c < ' ' || c == 0x7f) {
@@ -2636,8 +2753,8 @@ static void print_literal(char *buf, const char *s)
                *d++ = c;
                *d = '\0';
                if (c_is_no_print) {
-                       strcpy(d, SOs);
-                       d += sizeof(SOs)-1;
+                       strcpy(d, ESC_BOLD_TEXT);
+                       d += sizeof(ESC_BOLD_TEXT)-1;
                }
                if (*s == '\n') {
                        *d++ = '$';
@@ -2719,8 +2836,8 @@ static int format_edit_status(void)
 //----- Force refresh of all Lines -----------------------------
 static void redraw(int full_screen)
 {
-       place_cursor(0, 0, FALSE);      // put cursor in correct place
-       clear_to_eos();         // tell terminal to erase display
+       place_cursor(0, 0);
+       clear_to_eos();
        screen_erase();         // erase the internal screen buffer
        last_status_cksum = 0;  // force status update
        refresh(full_screen);   // this will redraw the entire display
@@ -2860,22 +2977,13 @@ static void refresh(int full_screen)
                if (changed) {
                        // copy changed part of buffer to virtual screen
                        memcpy(sp+cs, out_buf+cs, ce-cs+1);
-
-                       // move cursor to column of first change
-                       //if (offset != old_offset) {
-                       //      // place_cursor is still too stupid
-                       //      // to handle offsets correctly
-                       //      place_cursor(li, cs, FALSE);
-                       //} else {
-                               place_cursor(li, cs, TRUE);
-                       //}
-
+                       place_cursor(li, cs);
                        // write line out to terminal
                        fwrite(&sp[cs], ce - cs + 1, 1, stdout);
                }
        }
 
-       place_cursor(crow, ccol, TRUE);
+       place_cursor(crow, ccol);
 
        old_offset = offset;
 #undef old_offset
@@ -2905,7 +3013,6 @@ static void refresh(int full_screen)
 //----- Execute a Vi Command -----------------------------------
 static void do_cmd(int c)
 {
-       const char *msg = msg; // for compiler
        char *p, *q, *save_dot;
        char buf[12];
        int dir;
@@ -2914,8 +3021,8 @@ static void do_cmd(int c)
 
 //     c1 = c; // quiet the compiler
 //     cnt = yf = 0; // quiet the compiler
-//     msg = p = q = save_dot = buf; // quiet the compiler
-       memset(buf, '\0', 12);
+//     p = q = save_dot = buf; // quiet the compiler
+       memset(buf, '\0', sizeof(buf));
 
        show_status_line();
 
@@ -3031,36 +3138,34 @@ static void do_cmd(int c)
        case KEYCODE_LEFT:      // cursor key Left
        case 8:         // ctrl-H- move left    (This may be ERASE char)
        case 0x7f:      // DEL- move left   (This may be ERASE char)
-               if (--cmdcnt > 0) {
-                       do_cmd(c);
-               }
-               dot_left();
+               do {
+                       dot_left();
+               } while (--cmdcnt > 0);
                break;
        case 10:                        // Newline ^J
        case 'j':                       // j- goto next line, same col
        case KEYCODE_DOWN:      // cursor key Down
-               if (--cmdcnt > 0) {
-                       do_cmd(c);
-               }
-               dot_next();             // go to next B-o-l
-               dot = move_to_col(dot, ccol + offset);  // try stay in same col
+               do {
+                       dot_next();             // go to next B-o-l
+                       // try stay in same col
+                       dot = move_to_col(dot, ccol + offset);
+               } while (--cmdcnt > 0);
                break;
        case 12:                        // ctrl-L  force redraw whole screen
        case 18:                        // ctrl-R  force redraw
-               place_cursor(0, 0, FALSE);      // put cursor in correct place
-               clear_to_eos(); // tel terminal to erase display
-               mysleep(10);
+               place_cursor(0, 0);
+               clear_to_eos();
+               //mysleep(10); // why???
                screen_erase(); // erase the internal screen buffer
                last_status_cksum = 0;  // force status update
                refresh(TRUE);  // this will redraw the entire display
                break;
        case 13:                        // Carriage Return ^M
        case '+':                       // +- goto next line
-               if (--cmdcnt > 0) {
-                       do_cmd(c);
-               }
-               dot_next();
-               dot_skip_over_ws();
+               do {
+                       dot_next();
+                       dot_skip_over_ws();
+               } while (--cmdcnt > 0);
                break;
        case 21:                        // ctrl-U  scroll up   half screen
                dot_scroll((rows - 2) / 2, -1);
@@ -3078,10 +3183,9 @@ static void do_cmd(int c)
        case ' ':                       // move right
        case 'l':                       // move right
        case KEYCODE_RIGHT:     // Cursor Key Right
-               if (--cmdcnt > 0) {
-                       do_cmd(c);
-               }
-               dot_right();
+               do {
+                       dot_right();
+               } while (--cmdcnt > 0);
                break;
 #if ENABLE_FEATURE_VI_YANKMARK
        case '"':                       // "- name a register to use for Delete/Yank
@@ -3151,7 +3255,7 @@ static void do_cmd(int c)
                end_cmd_q();    // stop adding to q
                break;
        case 'U':                       // U- Undo; replace current line with original version
-               if (reg[Ureg] != 0) {
+               if (reg[Ureg] != NULL) {
                        p = begin_line(dot);
                        q = end_line(dot);
                        p = text_hole_delete(p, q);     // delete cur line
@@ -3163,11 +3267,12 @@ static void do_cmd(int c)
 #endif /* FEATURE_VI_YANKMARK */
        case '$':                       // $- goto end of line
        case KEYCODE_END:               // Cursor Key End
-               if (--cmdcnt > 0) {
+               for (;;) {
+                       dot = end_line(dot);
+                       if (--cmdcnt <= 0)
+                               break;
                        dot_next();
-                       do_cmd(c);
                }
-               dot = end_line(dot);
                break;
        case '%':                       // %- find matching char of pair () [] {}
                for (q = dot; q < end && *q != '\n'; q++) {
@@ -3192,38 +3297,35 @@ static void do_cmd(int c)
                //
                //**** fall through to ... ';'
        case ';':                       // ;- look at rest of line for last forward char
-               if (--cmdcnt > 0) {
-                       do_cmd(';');
-               }
-               if (last_forward_char == 0)
-                       break;
-               q = dot + 1;
-               while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
-                       q++;
-               }
-               if (*q == last_forward_char)
-                       dot = q;
+               do {
+                       if (last_forward_char == 0)
+                               break;
+                       q = dot + 1;
+                       while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
+                               q++;
+                       }
+                       if (*q == last_forward_char)
+                               dot = q;
+               } while (--cmdcnt > 0);
                break;
        case ',':           // repeat latest 'f' in opposite direction
-               if (--cmdcnt > 0) {
-                       do_cmd(',');
-               }
                if (last_forward_char == 0)
                        break;
-               q = dot - 1;
-               while (q >= text && *q != '\n' && *q != last_forward_char) {
-                       q--;
-               }
-               if (q >= text && *q == last_forward_char)
-                       dot = q;
+               do {
+                       q = dot - 1;
+                       while (q >= text && *q != '\n' && *q != last_forward_char) {
+                               q--;
+                       }
+                       if (q >= text && *q == last_forward_char)
+                               dot = q;
+               } while (--cmdcnt > 0);
                break;
 
        case '-':                       // -- goto prev line
-               if (--cmdcnt > 0) {
-                       do_cmd(c);
-               }
-               dot_prev();
-               dot_skip_over_ws();
+               do {
+                       dot_prev();
+                       dot_skip_over_ws();
+               } while (--cmdcnt > 0);
                break;
 #if ENABLE_FEATURE_VI_DOT_CMD
        case '.':                       // .- repeat the last modifying command
@@ -3255,9 +3357,6 @@ static void do_cmd(int c)
                // user changed mind and erased the "/"-  do nothing
                break;
        case 'N':                       // N- backward search for last pattern
-               if (--cmdcnt > 0) {
-                       do_cmd(c);
-               }
                dir = BACK;             // assume BACKWARD search
                p = dot - 1;
                if (last_search_pattern[0] == '?') {
@@ -3269,41 +3368,41 @@ static void do_cmd(int c)
        case 'n':                       // n- repeat search for last pattern
                // search rest of text[] starting at next char
                // if search fails return orignal "p" not the "p+1" address
-               if (--cmdcnt > 0) {
-                       do_cmd(c);
-               }
+               do {
+                       const char *msg;
  dc3:
-               dir = FORWARD;  // assume FORWARD search
-               p = dot + 1;
-               if (last_search_pattern[0] == '?') {
-                       dir = BACK;
-                       p = dot - 1;
-               }
+                       dir = FORWARD;  // assume FORWARD search
+                       p = dot + 1;
+                       if (last_search_pattern[0] == '?') {
+                               dir = BACK;
+                               p = dot - 1;
+                       }
  dc4:
-               q = char_search(p, last_search_pattern + 1, dir, FULL);
-               if (q != NULL) {
-                       dot = q;        // good search, update "dot"
-                       msg = "";
-                       goto dc2;
-               }
-               // no pattern found between "dot" and "end"- continue at top
-               p = text;
-               if (dir == BACK) {
-                       p = end - 1;
-               }
-               q = char_search(p, last_search_pattern + 1, dir, FULL);
-               if (q != NULL) {        // found something
-                       dot = q;        // found new pattern- goto it
-                       msg = "search hit BOTTOM, continuing at TOP";
+                       q = char_search(p, last_search_pattern + 1, dir, FULL);
+                       if (q != NULL) {
+                               dot = q;        // good search, update "dot"
+                               msg = NULL;
+                               goto dc2;
+                       }
+                       // no pattern found between "dot" and "end"- continue at top
+                       p = text;
                        if (dir == BACK) {
-                               msg = "search hit TOP, continuing at BOTTOM";
+                               p = end - 1;
+                       }
+                       q = char_search(p, last_search_pattern + 1, dir, FULL);
+                       if (q != NULL) {        // found something
+                               dot = q;        // found new pattern- goto it
+                               msg = "search hit BOTTOM, continuing at TOP";
+                               if (dir == BACK) {
+                                       msg = "search hit TOP, continuing at BOTTOM";
+                               }
+                       } else {
+                               msg = "Pattern not found";
                        }
-               } else {
-                       msg = "Pattern not found";
-               }
  dc2:
-               if (*msg)
-                       status_line_bold("%s", msg);
+                       if (msg)
+                               status_line_bold("%s", msg);
+               } while (--cmdcnt > 0);
                break;
        case '{':                       // {- move backward paragraph
                q = char_search(dot, "\n\n", BACK, FULL);
@@ -3348,7 +3447,7 @@ static void do_cmd(int c)
                 || strncmp(p, "q!", cnt) == 0   // delete lines
                ) {
                        if (file_modified && p[1] != '!') {
-                               status_line_bold("No write since last change (:quit! overrides)");
+                               status_line_bold("No write since last change (:%s! overrides)", p);
                        } else {
                                editing = 0;
                        }
@@ -3364,7 +3463,7 @@ static void do_cmd(int c)
                        } else {
                                file_modified = 0;
                                last_file_modified = -1;
-                               status_line("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
+                               status_line("'%s' %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
                                if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
                                 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
                                ) {
@@ -3422,18 +3521,17 @@ static void do_cmd(int c)
        case 'B':                       // B- back a blank-delimited Word
        case 'E':                       // E- end of a blank-delimited word
        case 'W':                       // W- forward a blank-delimited word
-               if (--cmdcnt > 0) {
-                       do_cmd(c);
-               }
                dir = FORWARD;
                if (c == 'B')
                        dir = BACK;
-               if (c == 'W' || isspace(dot[dir])) {
-                       dot = skip_thing(dot, 1, dir, S_TO_WS);
-                       dot = skip_thing(dot, 2, dir, S_OVER_WS);
-               }
-               if (c != 'W')
-                       dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
+               do {
+                       if (c == 'W' || isspace(dot[dir])) {
+                               dot = skip_thing(dot, 1, dir, S_TO_WS);
+                               dot = skip_thing(dot, 2, dir, S_OVER_WS);
+                       }
+                       if (c != 'W')
+                               dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
+               } while (--cmdcnt > 0);
                break;
        case 'C':                       // C- Change to e-o-l
        case 'D':                       // D- delete to e-o-l
@@ -3484,20 +3582,19 @@ static void do_cmd(int c)
        case 'i':                       // i- insert before current char
        case KEYCODE_INSERT:    // Cursor Key Insert
  dc_i:
-               cmd_mode = 1;   // start insrting
+               cmd_mode = 1;   // start inserting
                break;
        case 'J':                       // J- join current and next lines together
-               if (--cmdcnt > 1) {
-                       do_cmd(c);
-               }
-               dot_end();              // move to NL
-               if (dot < end - 1) {    // make sure not last char in text[]
-                       *dot++ = ' ';   // replace NL with space
-                       file_modified++;
-                       while (isblank(*dot)) { // delete leading WS
-                               dot_delete();
+               do {
+                       dot_end();              // move to NL
+                       if (dot < end - 1) {    // make sure not last char in text[]
+                               *dot++ = ' ';   // replace NL with space
+                               file_modified++;
+                               while (isblank(*dot)) { // delete leading WS
+                                       dot_delete();
+                               }
                        }
-               }
+               } while (--cmdcnt > 0);
                end_cmd_q();    // stop adding to q
                break;
        case 'L':                       // L- goto bottom line on screen
@@ -3541,20 +3638,19 @@ static void do_cmd(int c)
        case 'X':                       // X- delete char before dot
        case 'x':                       // x- delete the current char
        case 's':                       // s- substitute the current char
-               if (--cmdcnt > 0) {
-                       do_cmd(c);
-               }
                dir = 0;
                if (c == 'X')
                        dir = -1;
-               if (dot[dir] != '\n') {
-                       if (c == 'X')
-                               dot--;  // delete prev char
-                       dot = yank_delete(dot, dot, 0, YANKDEL);        // delete char
-               }
-               if (c == 's')
-                       goto dc_i;      // start insrting
+               do {
+                       if (dot[dir] != '\n') {
+                               if (c == 'X')
+                                       dot--;  // delete prev char
+                               dot = yank_delete(dot, dot, 0, YANKDEL);        // delete char
+                       }
+               } while (--cmdcnt > 0);
                end_cmd_q();    // stop adding to q
+               if (c == 's')
+                       goto dc_i;      // start inserting
                break;
        case 'Z':                       // Z- if modified, {write}; exit
                // ZZ means to save file (if necessary), then exit
@@ -3565,7 +3661,7 @@ static void do_cmd(int c)
                }
                if (file_modified) {
                        if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
-                               status_line_bold("\"%s\" File is read only", current_filename);
+                               status_line_bold("'%s' is read only", current_filename);
                                break;
                        }
                        cnt = file_write(current_filename, text, end - 1);
@@ -3585,23 +3681,22 @@ static void do_cmd(int c)
                break;
        case 'b':                       // b- back a word
        case 'e':                       // e- end of word
-               if (--cmdcnt > 0) {
-                       do_cmd(c);
-               }
                dir = FORWARD;
                if (c == 'b')
                        dir = BACK;
-               if ((dot + dir) < text || (dot + dir) > end - 1)
-                       break;
-               dot += dir;
-               if (isspace(*dot)) {
-                       dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
-               }
-               if (isalnum(*dot) || *dot == '_') {
-                       dot = skip_thing(dot, 1, dir, S_END_ALNUM);
-               } else if (ispunct(*dot)) {
-                       dot = skip_thing(dot, 1, dir, S_END_PUNCT);
-               }
+               do {
+                       if ((dot + dir) < text || (dot + dir) > end - 1)
+                               break;
+                       dot += dir;
+                       if (isspace(*dot)) {
+                               dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
+                       }
+                       if (isalnum(*dot) || *dot == '_') {
+                               dot = skip_thing(dot, 1, dir, S_END_ALNUM);
+                       } else if (ispunct(*dot)) {
+                               dot = skip_thing(dot, 1, dir, S_END_PUNCT);
+                       }
+               } while (--cmdcnt > 0);
                break;
        case 'c':                       // c- change something
        case 'd':                       // d- delete something
@@ -3686,11 +3781,10 @@ static void do_cmd(int c)
        }
        case 'k':                       // k- goto prev line, same col
        case KEYCODE_UP:                // cursor key Up
-               if (--cmdcnt > 0) {
-                       do_cmd(c);
-               }
-               dot_prev();
-               dot = move_to_col(dot, ccol + offset);  // try stay in same col
+               do {
+                       dot_prev();
+                       dot = move_to_col(dot, ccol + offset);  // try stay in same col
+               } while (--cmdcnt > 0);
                break;
        case 'r':                       // r- replace the current char with user input
                c1 = get_one_char();    // get the replacement char
@@ -3708,19 +3802,18 @@ static void do_cmd(int c)
                last_forward_char = 0;
                break;
        case 'w':                       // w- forward a word
-               if (--cmdcnt > 0) {
-                       do_cmd(c);
-               }
-               if (isalnum(*dot) || *dot == '_') {     // we are on ALNUM
-                       dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
-               } else if (ispunct(*dot)) {     // we are on PUNCT
-                       dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
-               }
-               if (dot < end - 1)
-                       dot++;          // move over word
-               if (isspace(*dot)) {
-                       dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
-               }
+               do {
+                       if (isalnum(*dot) || *dot == '_') {     // we are on ALNUM
+                               dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
+                       } else if (ispunct(*dot)) {     // we are on PUNCT
+                               dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
+                       }
+                       if (dot < end - 1)
+                               dot++;          // move over word
+                       if (isspace(*dot)) {
+                               dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
+                       }
+               } while (--cmdcnt > 0);
                break;
        case 'z':                       // z-
                c1 = get_one_char();    // get the replacement char
@@ -3736,17 +3829,16 @@ static void do_cmd(int c)
                dot = move_to_col(dot, cmdcnt - 1);     // try to move to column
                break;
        case '~':                       // ~- flip the case of letters   a-z -> A-Z
-               if (--cmdcnt > 0) {
-                       do_cmd(c);
-               }
-               if (islower(*dot)) {
-                       *dot = toupper(*dot);
-                       file_modified++;
-               } else if (isupper(*dot)) {
-                       *dot = tolower(*dot);
-                       file_modified++;
-               }
-               dot_right();
+               do {
+                       if (islower(*dot)) {
+                               *dot = toupper(*dot);
+                               file_modified++;
+                       } else if (isupper(*dot)) {
+                               *dot = tolower(*dot);
+                               file_modified++;
+                       }
+                       dot_right();
+               } while (--cmdcnt > 0);
                end_cmd_q();    // stop adding to q
                break;
                //----- The Cursor and Function Keys -----------------------------
@@ -3982,7 +4074,7 @@ static void crash_test()
 
        if (msg[0]) {
                printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
-                       totalcmds, last_input_char, msg, SOs, SOn);
+                       totalcmds, last_input_char, msg, ESC_BOLD_TEXT, ESC_NORM_TEXT);
                fflush_all();
                while (safe_read(STDIN_FILENO, d, 1) > 0) {
                        if (d[0] == '\n' || d[0] == '\r')
diff --git a/examples/android-build b/examples/android-build
new file mode 100755 (executable)
index 0000000..89f3b63
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+# Build Busybox against Android's bionic
+# Originally by Dan Fandrich
+#
+# Configure with "make android_defconfig"
+#
+# This file has been tested on Android Froyo (the lack of ttyname_r in
+# the android libc must be patched around) and Gingerbread.
+
+# Point this to the Android root directory; it's used in the defconfig CFLAGS
+export A="$HOME/android"
+
+# Android product being built
+P=zoom2
+
+# Toolchain version in use by this version of Android
+GCCVER=4.4.3
+
+export PATH="$A/prebuilt/linux-x86/toolchain/arm-eabi-$GCCVER/bin:$PATH"
+
+# Set the linker flags; compiler flags are in the defconfig file
+if grep "^CONFIG_STATIC=y" .config >/dev/null ; then
+       # Static linking
+       LDFLAGS="-static -Xlinker -z -Xlinker muldefs -nostdlib $A/out/target/product/$P/obj/lib/crtbegin_static.o $A/out/target/product/$P/obj/lib/crtend_android.o -L$A/out/target/product/$P/obj/lib -L$A/out/target/product/$P/obj/STATIC_LIBRARIES/libm_intermediates -L$A/out/target/product/$P/obj/STATIC_LIBRARIES/libc_intermediates"
+       LDLIBS="m c gcc"
+else
+       # Dynamic linking
+       LDFLAGS="-Xlinker -z -Xlinker muldefs -nostdlib -Bdynamic -Xlinker -T$A/build/core/armelf.x -Xlinker -dynamic-linker -Xlinker /system/bin/linker -Xlinker -z -Xlinker nocopyreloc -Xlinker --no-undefined $A/out/target/product/$P/obj/lib/crtbegin_dynamic.o $A/out/target/product/$P/obj/lib/crtend_android.o -L$A/out/target/product/$P/obj/lib"
+       LDLIBS="dl m c gcc"
+fi
+
+make EXTRA_LDFLAGS="$LDFLAGS" LDLIBS="$LDLIBS" "$@"
index 399d326..7cae48b 100644 (file)
@@ -1,4 +1,3 @@
 
 This boot floppy is made with Busybox, uClibc, and the Linux kernel.
 Hit RETURN to boot or enter boot parameters at the prompt below.
-
index ef14ca2..b31f602 100644 (file)
@@ -1,2 +1 @@
 proc           /proc   proc    defaults    0   0
-
index eb3e979..1ac9f68 100644 (file)
@@ -2,4 +2,3 @@
 ::respawn:-/bin/sh
 tty2::askfirst:-/bin/sh
 ::ctrlaltdel:/bin/umount -a -r
-
index 8a7c77d..cf68d33 100644 (file)
@@ -5,4 +5,3 @@ echo -n "Processing /etc/profile... "
 # no-op
 echo "Done"
 echo
-
index 5cdff21..a7fc48b 100755 (executable)
@@ -102,4 +102,3 @@ then
        rmdir $TARGET_DIR
        gzip -9 rootfs
 fi
-
index d8c4cc5..d769590 100755 (executable)
@@ -4,7 +4,7 @@
 #
 # Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
 #
-# Licensed under GPLv2
+# Licensed under GPLv2, see file LICENSE in this source tree.
 #
 
 local BASE="${1:-/usr/lib/modules}"
index 8c6548d..809dc08 100755 (executable)
@@ -173,6 +173,9 @@ sub add_mod_deps
 
        $depth .= " ";
        warn "${depth}loading deps of module: $this_module\n" if $verbose;
+       if (length($depth) > 50) {
+               die "too much recursion (circular dependencies in modules?)";
+       }
 
        foreach my $md (keys %{$mod->{$this_module}}) {
                add_mod_deps ($depth, $mod, $mod2, $module, $md);
@@ -196,7 +199,7 @@ if ($stdout == 0) {
     open(STDOUT, ">$basedir/modules.dep")
                              or die "cannot open $basedir/modules.dep: $!";
 }
-my $kseries = $basedir =~ m,/2\.6\.[^/]*, ? '2.6' : '2.4';
+my $kseries = $basedir =~ m,/2\.4\.[^/]*, ? '2.4' : 'others';
 
 foreach my $module ( keys %$mod ) {
     if($kseries eq '2.4') {
index 64fc4fc..01ceaef 100644 (file)
 #      the specified process to run on.  The contents of this field are
 #      appended to "/dev/" and used as-is.  There is no need for this field to
 #      be unique, although if it isn't you may have strange results.  If this
-#      field is left blank, it is completely ignored.  Also note that if
-#      BusyBox detects that a serial console is in use, then all entries
-#      containing non-empty id fields will be ignored.  BusyBox init does
-#      nothing with utmp.  We don't need no stinkin' utmp.
+#      field is left blank, then the init's stdin/out will be used.
 #
 # <runlevels>: The runlevels field is completely ignored.
 #
@@ -43,9 +40,6 @@
 #         ::shutdown:/sbin/swapoff -a
 #         ::shutdown:/bin/umount -a -r
 #         ::restart:/sbin/init
-#
-# if it detects that /dev/console is _not_ a serial console, it will
-# also run:
 #         tty2::askfirst:/bin/sh
 #         tty3::askfirst:/bin/sh
 #         tty4::askfirst:/bin/sh
@@ -87,4 +81,3 @@ tty5::respawn:/sbin/getty 38400 tty6
 ::ctrlaltdel:/sbin/reboot
 ::shutdown:/bin/umount -a -r
 ::shutdown:/sbin/swapoff -a
-
index cdbb4fc..5179569 100644 (file)
@@ -7,8 +7,14 @@
 # instead of the default 0:0 660.
 #
 # Syntax:
-# %s %d:%d %s
-# devicename_regex user:group mode
+# [-]devicename_regex user:group mode [=path]|[>path]|[!] [@|$|*cmd args...]
+# [-]$ENVVAR=regex    user:group mode [=path]|[>path]|[!] [@|$|*cmd args...]
+# [-]@maj,min[-min2]  user:group mode [=path]|[>path]|[!] [@|$|*cmd args...]
+#
+# [-]: do not stop on this match, continue reading mdev.conf
+# =: move, >: move and create a symlink
+# !: do not create device node
+# @|$|*: run@cmd if $ACTION=add,  $cmd if $ACTION=remove, *cmd in all cases
 
 null           0:0 666
 zero           0:0 666
diff --git a/examples/mdev.conf.change_blockdev.sh b/examples/mdev.conf.change_blockdev.sh
new file mode 100755 (executable)
index 0000000..512e43f
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# Seconds to try to reread partition table
+cnt=60
+
+exec </dev/null
+exec >"/tmp/${0##*/}.$$.out"
+exec 2>&1
+
+(
+echo "Running: $0"
+echo "Env:"
+env | sort
+
+while sleep 1; test $cnt != 0; do
+       echo "Trying to reread partition table on $DEVNAME ($cnt)"
+       : $((cnt--))
+       # If device node doesn't exist, it means the device was removed.
+       # Stop trying.
+       test -e "$DEVNAME" || { echo "$DEVNAME doesn't exist, aborting"; exit 1; }
+       #echo "$DEVNAME exists"
+       if blockdev --rereadpt "$DEVNAME"; then
+               echo "blockdev --rereadpt succeeded"
+               exit 0
+       fi
+       echo "blockdev --rereadpt failed, exit code: $?"
+done
+echo "Timed out"
+) &
index df329b4..f2a15f3 100644 (file)
@@ -7,10 +7,14 @@
 # instead of the default 0:0 660.
 #
 # Syntax:
-# [-]devicename_regex user:group mode [>|=path] [@|$|*cmd args...]
+# [-][ENVVAR=regex;]...devicename_regex user:group mode [=path]|[>path]|[!] [@|$|*cmd args...]
+# [-][ENVVAR=regex;]...@maj,min[-min2]  user:group mode [=path]|[>path]|[!] [@|$|*cmd args...]
+# [-]$ENVVAR=regex                      user:group mode [=path]|[>path]|[!] [@|$|*cmd args...]
 #
+# [-]: do not stop on this match, continue reading mdev.conf
 # =: move, >: move and create a symlink
-# @|$|*: run $cmd on delete, @cmd on create, *cmd on both
+# !: do not create device node
+# @|$|*: run cmd if $ACTION=remove, @cmd if $ACTION=add, *cmd in all cases
 
 # support module loading on hotplug
 $MODALIAS=.*   root:root 660 @modprobe "$MODALIAS"
@@ -49,7 +53,7 @@ sr[0-9]*      root:cdrom 660 @ln -sf $MDEV cdrom
 fd[0-9]*       root:floppy 660
 
 # net devices
--net/.*                root:root 600 @nameif
+SUBSYSTEM=net;.* root:root 600 @nameif
 tun[0-9]*      root:root 600 =net/
 tap[0-9]*      root:root 600 =net/
 
@@ -108,3 +112,33 @@ usbdev[0-9].[0-9]_.*       root:root 660
 # zaptel devices
 zap(.*)                root:dialout 660 =zap/%1
 dahdi!(.*)     root:dialout 660 =dahdi/%1
+
+# HTC Android
+# Attaching it via USB cable causes a flurry of activity:
+#
+# mdev[1271]: 48.639459 ACTION:add SUBSYSTEM:usb DEVNAME:bus/usb/001/009 DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5
+# mdev[1271]: mknod bus/usb/001/009 (189,8) 20660 0:0
+# mdev[1272]: 48.642354 ACTION:add SUBSYSTEM:usb DEVNAME:(null) DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0
+# mdev[1272]: running: modprobe "$MODALIAS"
+# mdev[1273]: 48.650078 ACTION:add SUBSYSTEM:scsi DEVNAME:(null) DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host11
+# mdev[1274]: 48.651297 ACTION:add SUBSYSTEM:scsi_host DEVNAME:(null) DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host11/scsi_host/host11
+# mdev[1275]: 49.649422 ACTION:add SUBSYSTEM:scsi DEVNAME:(null) DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host11/target11:0:0
+# mdev[1276]: 49.650703 ACTION:add SUBSYSTEM:scsi DEVNAME:(null) DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host11/target11:0:0/11:0:0:0
+# mdev[1276]: running: modprobe "$MODALIAS"
+# modprobe: module scsi:t-0x00 not found in modules.dep
+# mdev[1277]: 49.658002 ACTION:add SUBSYSTEM:scsi_disk DEVNAME:(null) DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host11/target11:0:0/11:0:0:0/scsi_disk/11:0:0:0
+# mdev[1278]: 49.659244 ACTION:add SUBSYSTEM:scsi_device DEVNAME:(null) DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host11/target11:0:0/11:0:0:0/scsi_device/11:0:0:
+# mdev[1279]: 49.660535 ACTION:add SUBSYSTEM:bsg DEVNAME:bsg/11:0:0:0 DEVPATH:/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host11/target11:0:0/11:0:0:0/bsg/11:0:0:0
+# mdev[1279]: mknod bsg/11:0:0:0 (253,1) 20660 0:0
+# mdev[1280]: 49.663516 ACTION:add SUBSYSTEM:bdi DEVNAME:(null) DEVPATH:/devices/virtual/bdi/8:16
+# mdev[1281]: 49.664748 ACTION:add SUBSYSTEM:block DEVNAME:sdb DEVPATH:/block/sdb
+# mdev[1281]: mknod sdb (8,16) 60660 0:0
+# mdev[1282]: 49.677597 ACTION:change SUBSYSTEM:block DEVNAME:sdb DEVPATH:/block/sdb
+# mdev[1283]: 50.692936 ACTION:change SUBSYSTEM:block DEVNAME:sdb DEVPATH:/block/sdb
+#
+# We are hooking to the last events. To avoid having two scripts running,
+# we check for DISK_MEDIA_CHANGE=1 (prev to last event has it,
+# and it's the _only_ difference in env for these two events as of kernel 3.7.7)
+# Unfortunately, there is no event for "user pressed [Turn USB storage] btn"!
+# Script merely backgrounds and tries to rescan partition table for 1 minute:
+ACTION=change;SUBSYSTEM=block;DISK_MEDIA_CHANGE=1;.*    0:0 660 */etc/mdev.conf.change_blockdev.sh
index 2a95d8b..bd3569c 100755 (executable)
@@ -28,4 +28,4 @@ for i in $dns
 do
        echo adding dns $i
        echo nameserver $i >> $RESOLV_CONF
-done
\ No newline at end of file
+done
index 842bafe..ea368fc 100755 (executable)
@@ -28,4 +28,4 @@ for i in $dns
 do
        echo adding dns $i
        echo nameserver $i >> $RESOLV_CONF
-done
\ No newline at end of file
+done
index 40ee738..2a917eb 100755 (executable)
@@ -29,18 +29,23 @@ case "$1" in
                        metric=0
                        for i in $router ; do
                                echo "Adding router $i"
-                               route add default gw $i dev $interface metric $((metric++))
+                               route add default gw $i dev $interface metric $metric
+                               : $(( metric += 1 ))
                        done
                fi
 
                echo "Recreating $RESOLV_CONF"
-               echo -n > $RESOLV_CONF-$$
-               [ -n "$domain" ] && echo "search $domain" >> $RESOLV_CONF-$$
+               # If the file is a symlink somewhere (like /etc/resolv.conf
+               # pointing to /run/resolv.conf), make sure things work.
+               realconf=$(readlink -f "$RESOLV_CONF" 2>/dev/null || echo "$RESOLV_CONF")
+               tmpfile="$realconf-$$"
+               > "$tmpfile"
+               [ -n "$domain" ] && echo "search $domain" >> "$tmpfile"
                for i in $dns ; do
                        echo " Adding DNS server $i"
-                       echo "nameserver $i" >> $RESOLV_CONF-$$
+                       echo "nameserver $i" >> "$tmpfile"
                done
-               mv $RESOLV_CONF-$$ $RESOLV_CONF
+               mv "$tmpfile" "$realconf"
                ;;
 esac
 
index 23fc834..eca44c0 100644 (file)
@@ -14,11 +14,6 @@ interface    eth0
 # smaller than lease block.
 #max_leases    254
 
-# The time period at which udhcpd will write out a dhcpd.leases
-# file. If this is 0, udhcpd will never automatically write a
-# lease file. Specified in seconds.
-#auto_time     7200
-
 # The amount of time that an IP will be reserved (leased to nobody)
 # if a DHCP decline message is received (seconds)
 #decline_time  3600
@@ -34,11 +29,16 @@ interface   eth0
 # to this value (seconds)
 #min_lease     60
 
+# The location of the pid file
+#pidfile       /var/run/udhcpd.pid
+
 # The location of the leases file
 #lease_file    /var/lib/misc/udhcpd.leases
 
-# The location of the pid file
-#pidfile       /var/run/udhcpd.pid
+# The time period at which udhcpd will write out leases file.
+# If this is 0, udhcpd will never automatically write leases file.
+# Specified in seconds.
+#auto_time     7200
 
 # Every time udhcpd writes a leases file, the below script will be called
 #notify_file                   # default: no script
@@ -68,6 +68,8 @@ opt   wins    192.168.10.10
 option dns     129.219.13.81   # appended to above DNS servers for a total of 3
 option domain  local
 option lease   864000          # default: 10 days
+option msstaticroutes  10.0.0.0/8 10.127.0.1           # single static route
+option staticroutes    10.0.0.0/8 10.127.0.1, 10.11.12.0/24 10.11.12.1
 # Arbitrary option in hex form:
 option 0x08    01020304        # option 8: "cookie server IP addr: 1.2.3.4"
 
@@ -90,6 +92,8 @@ option        0x08    01020304        # option 8: "cookie server IP addr: 1.2.3.4"
 #opt wpad       STRING
 #opt serverid   IP             # default: server's IP
 #opt message    STRING         # error message (udhcpd sends it on success too)
+#opt vlanid     NUM            # 802.1P VLAN ID
+#opt vlanpriority NUM          # 802.1Q VLAN priority
 # Options specifying server(s)
 #opt dns        IP_LIST
 #opt wins       IP_LIST
@@ -97,8 +101,15 @@ option      0x08    01020304        # option 8: "cookie server IP addr: 1.2.3.4"
 #opt ntpsrv     IP_LIST
 #opt lprsrv     IP_LIST
 #opt swapsrv    IP
+# Options specifying routes
+#opt routes     IP_PAIR_LIST
+#opt staticroutes   STATIC_ROUTES # RFC 3442 classless static route option
+#opt msstaticroutes STATIC_ROUTES # same, using MS option number
 # Obsolete options, no longer supported
 #opt logsrv     IP_LIST        # 704/UDP log server (not syslog!)
 #opt namesrv    IP_LIST        # IEN 116 name server, obsolete (August 1979!!!)
 #opt cookiesrv  IP_LIST        # RFC 865 "quote of the day" server, rarely (never?) used
 #opt timesrv    IP_LIST        # RFC 868 time server, rarely (never?) used
+# TODO: in development
+#opt userclass  STRING         # RFC 3004. set of LASCII strings. "I am a printer" etc
+#opt sipserv    STRING LIST    # RFC 3361. flag byte, then: 0: domain names, 1: IP addrs
diff --git a/examples/var_service/README b/examples/var_service/README
new file mode 100644 (file)
index 0000000..06817c8
--- /dev/null
@@ -0,0 +1,59 @@
+In many cases, network configuration makes it necessary to run several daemons:
+dhcp, zeroconf, ppp, openvpn and such. They need to be controlled,
+and in many cases you also want to babysit them. runsvdir is a good tool for this.
+examples/var_service directory provides a few examples. It is meant to be used
+this way: copy it somewhere (say, /var/service) and run something like
+
+env - PATH=... <other vars=...> runsvdir /var/service &
+
+from one of system startup scripts. (Google "man runsvdir" and "man runsv"
+for more info about these tools).
+
+Some existing examples:
+
+var_service/dhcp_if -
+controls a udhcpc instance which provides dhpc-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). When IP address is obtained or lost,
+var_service/dhcp_if/dhcp_handler is run. It saves new config data to
+/var/run/service/fw/dhcp_if.ipconf and (re)starts /var/service/fw service.
+This example can be used as a template for other dynamic network link services
+(ppp/vpn/zcip).
+
+var_service/ifplugd_if -
+watches link status of interface if. Downs and ups /var/service/dhcp_if
+service accordingly. In effect, it allows you to unplug/plug-to-different-network
+and have your IP properly re-negotiated at once.
+
+var_service/dhcp_if_pinger -
+Uses var_service/dhcp_if's data (/var/service/dhcp_if/dhcp_if.out file)
+to determine router IP. Pings it. If ping fails, restarts /var/service/dhcp_if
+service. Basically, an example of watchdog service for networks
+which are not reliable and need babysitting.
+
+var_service/fw -
+A *one-shot* service which reconfigures network based on current known state
+of ALL interfaces. Uses conf/*.ipconf (static config) and /var/run/service/fw/*.ipconf
+(dynamic config from dhcp/ppp/vpn/etc) to determine what to do.
+One-shot-ness of this service means that it shuts itself off after single run.
+IOW: it is not a constantly running daemon sort of thing.
+It starts, it configures the network, it shuts down, all done
+(unlike infamous NetworkManagers which sit in RAM forever, doing hell knows what).
+
+However, any dhcp/ppp/vpn or similar service can restart it anytime
+when it senses the change in network configuration.
+This even works while fw service runs: if dhcp signals fw to (re)start
+while fw runs, fw will not stop after its execution, but will re-execute once,
+picking up dhcp's new configuration.
+This is achieved very simply by having
+# Make ourself one-shot
+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.
+
+System administrators are expected to edit fw/run script, since
+network configuration needs are likely to be very complex and different
+for non-trivial installations.
index 988e542..e543c30 100755 (executable)
@@ -20,9 +20,9 @@ config)
                exit 1
        fi
        # remember $ip for $interface, to use on restart
-       if [ "x$IP" != x -a -w "$IP.$interface" ]
+       if [ "x$ip" != x -a -w "$ip.$interface" ]
        then
-               echo $ip > "$IP.$interface"
+               echo $ip > "$ip.$interface"
        fi
        exec ip address add dev $interface \
                scope link local "$ip/16" broadcast +
diff --git a/findutils/.gitignore b/findutils/.gitignore
deleted file mode 100644 (file)
index 7a30caf..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# Config.in and Kbuild are auto-generated
-Config.in
-Kbuild
index 771789f..6b4fb74 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
index 2970814..5d5e24b 100644 (file)
@@ -7,7 +7,7 @@
  * Reworked by David Douthitt <n9ubh@callsign.net> and
  *  Matt Kraai <kraai@alumni.carnegiemellon.edu>.
  *
- * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /* findutils-4.1.20:
  * diff -u /tmp/std_find /tmp/bb_find && echo Identical
  */
 
-//applet:IF_FIND(APPLET_NOEXEC(find, find, _BB_DIR_USR_BIN, _BB_SUID_DROP, find))
-
-//kbuild:lib-$(CONFIG_FIND) += find.o
-
 //config:config FIND
 //config:      bool "find"
 //config:      default y
 //config:        This option allows find to restrict searches to a single filesystem.
 //config:
 //config:config FEATURE_FIND_MAXDEPTH
-//config:      bool "Enable -maxdepth N"
+//config:      bool "Enable -mindepth N and -maxdepth N"
 //config:      default y
 //config:      depends on FIND
 //config:      help
-//config:        This option enables -maxdepth N option.
+//config:        This option enables -mindepth N and -maxdepth N option.
 //config:
 //config:config FEATURE_FIND_NEWER
 //config:      bool "Enable -newer: compare file modification times"
 //config:      depends on FIND
 //config:      help
 //config:        Support the 'find -newer' option for finding any files which have
-//config:        a modified time that is more recent than the specified FILE.
+//config:        modification time that is more recent than the specified FILE.
 //config:
 //config:config FEATURE_FIND_INUM
 //config:      bool "Enable -inum: inode number matching"
 //config:      help
 //config:        Support the 'find -links' option for matching number of links.
 
+//applet:IF_FIND(APPLET_NOEXEC(find, find, BB_DIR_USR_BIN, BB_SUID_DROP, find))
+
+//kbuild:lib-$(CONFIG_FIND) += find.o
+
+//usage:#define find_trivial_usage
+//usage:       "[-HL] [PATH]... [OPTIONS] [ACTIONS]"
+//usage:#define find_full_usage "\n\n"
+//usage:       "Search for files and perform actions on them.\n"
+//usage:       "First failed action stops processing of current file.\n"
+//usage:       "Defaults: PATH is current directory, action is '-print'\n"
+//usage:     "\n       -L,-follow      Follow symlinks"
+//usage:     "\n       -H              ...on command line only"
+//usage:       IF_FEATURE_FIND_XDEV(
+//usage:     "\n       -xdev           Don't descend directories on other filesystems"
+//usage:       )
+//usage:       IF_FEATURE_FIND_MAXDEPTH(
+//usage:     "\n       -maxdepth N     Descend at most N levels. -maxdepth 0 applies"
+//usage:     "\n                       actions to command line arguments only"
+//usage:     "\n       -mindepth N     Don't act on first N levels"
+//usage:       )
+//usage:       IF_FEATURE_FIND_DEPTH(
+//usage:     "\n       -depth          Act on directory *after* traversing it"
+//usage:       )
+//usage:     "\n"
+//usage:     "\nActions:"
+//usage:       IF_FEATURE_FIND_PAREN(
+//usage:     "\n       ( ACTIONS )     Group actions for -o / -a"
+//usage:       )
+//usage:       IF_FEATURE_FIND_NOT(
+//usage:     "\n       ! ACT           Invert ACT's success/failure"
+//usage:       )
+//usage:     "\n       ACT1 [-a] ACT2  If ACT1 fails, stop, else do ACT2"
+//usage:     "\n       ACT1 -o ACT2    If ACT1 succeeds, stop, else do ACT2"
+//usage:     "\n                       Note: -a has higher priority than -o"
+//usage:     "\n       -name PATTERN   Match file name (w/o directory name) to PATTERN"
+//usage:     "\n       -iname PATTERN  Case insensitive -name"
+//usage:       IF_FEATURE_FIND_PATH(
+//usage:     "\n       -path PATTERN   Match path to PATTERN"
+//usage:     "\n       -ipath PATTERN  Case insensitive -path"
+//usage:       )
+//usage:       IF_FEATURE_FIND_REGEX(
+//usage:     "\n       -regex PATTERN  Match path to regex PATTERN"
+//usage:       )
+//usage:       IF_FEATURE_FIND_TYPE(
+//usage:     "\n       -type X         File type is X (one of: f,d,l,b,c,...)"
+//usage:       )
+//usage:       IF_FEATURE_FIND_PERM(
+//usage:     "\n       -perm MASK      At least one mask bit (+MASK), all bits (-MASK),"
+//usage:     "\n                       or exactly MASK bits are set in file's mode"
+//usage:       )
+//usage:       IF_FEATURE_FIND_MTIME(
+//usage:     "\n       -mtime DAYS     mtime is greater than (+N), less than (-N),"
+//usage:     "\n                       or exactly N days in the past"
+//usage:       )
+//usage:       IF_FEATURE_FIND_MMIN(
+//usage:     "\n       -mmin MINS      mtime is greater than (+N), less than (-N),"
+//usage:     "\n                       or exactly N minutes in the past"
+//usage:       )
+//usage:       IF_FEATURE_FIND_NEWER(
+//usage:     "\n       -newer FILE     mtime is more recent than FILE's"
+//usage:       )
+//usage:       IF_FEATURE_FIND_INUM(
+//usage:     "\n       -inum N         File has inode number N"
+//usage:       )
+//usage:       IF_FEATURE_FIND_USER(
+//usage:     "\n       -user NAME/ID   File is owned by given user"
+//usage:       )
+//usage:       IF_FEATURE_FIND_GROUP(
+//usage:     "\n       -group NAME/ID  File is owned by given group"
+//usage:       )
+//usage:       IF_FEATURE_FIND_SIZE(
+//usage:     "\n       -size N[bck]    File size is N (c:bytes,k:kbytes,b:512 bytes(def.))"
+//usage:     "\n                       +/-N: file size is bigger/smaller than N"
+//usage:       )
+//usage:       IF_FEATURE_FIND_LINKS(
+//usage:     "\n       -links N        Number of links is greater than (+N), less than (-N),"
+//usage:     "\n                       or exactly N"
+//usage:       )
+//usage:       IF_FEATURE_FIND_CONTEXT(
+//usage:     "\n       -context CTX    File has specified security context"
+//usage:       )
+//usage:       IF_FEATURE_FIND_PRUNE(
+//usage:     "\n       -prune          If current file is directory, don't descend into it"
+//usage:       )
+//usage:     "\nIf none of the following actions is specified, -print is assumed"
+//usage:     "\n       -print          Print file name"
+//usage:       IF_FEATURE_FIND_PRINT0(
+//usage:     "\n       -print0         Print file name, NUL terminated"
+//usage:       )
+//usage:       IF_FEATURE_FIND_EXEC(
+//usage:     "\n       -exec CMD ARG ; Run CMD with all instances of {} replaced by"
+//usage:     "\n                       file name. Fails if CMD exits with nonzero"
+//usage:       )
+//usage:       IF_FEATURE_FIND_DELETE(
+//usage:     "\n       -delete         Delete current file/directory. Turns on -depth option"
+//usage:       )
+//usage:
+//usage:#define find_example_usage
+//usage:       "$ find / -name passwd\n"
+//usage:       "/etc/passwd\n"
+
 #include <fnmatch.h>
 #include "libbb.h"
 #if ENABLE_FEATURE_FIND_REGEX
-#include "xregex.h"
+# include "xregex.h"
 #endif
+/* GNUism: */
+#ifndef FNM_CASEFOLD
+# define FNM_CASEFOLD 0
+#endif
+
+#define dbg(...) ((void)0)
+/* #define dbg(...) bb_error_msg(__VA_ARGS__) */
 
 /* This is a NOEXEC applet. Be very careful! */
 
@@ -256,7 +360,7 @@ typedef struct {
 
                         ACTS(print)
                         ACTS(name,  const char *pattern; bool iname;)
-IF_FEATURE_FIND_PATH(   ACTS(path,  const char *pattern;))
+IF_FEATURE_FIND_PATH(   ACTS(path,  const char *pattern; bool ipath;))
 IF_FEATURE_FIND_REGEX(  ACTS(regex, regex_t compiled_pattern;))
 IF_FEATURE_FIND_PRINT0( ACTS(print0))
 IF_FEATURE_FIND_TYPE(   ACTS(type,  int type_mask;))
@@ -278,8 +382,12 @@ IF_FEATURE_FIND_LINKS(  ACTS(links, char links_char; int links_count;))
 struct globals {
        IF_FEATURE_FIND_XDEV(dev_t *xdev_dev;)
        IF_FEATURE_FIND_XDEV(int xdev_count;)
+#if ENABLE_FEATURE_FIND_MAXDEPTH
+       int minmaxdepth[2];
+#endif
        action ***actions;
-       bool need_print;
+       smallint need_print;
+       smallint xdev_on;
        recurse_flags_t recurse_flags;
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
@@ -288,7 +396,8 @@ struct globals {
                char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
        }; \
        /* we have to zero it out because of NOEXEC */ \
-       memset(&G, 0, offsetof(struct globals, need_print)); \
+       memset(&G, 0, sizeof(G)); \
+       IF_FEATURE_FIND_MAXDEPTH(G.minmaxdepth[1] = INT_MAX;) \
        G.need_print = 1; \
        G.recurse_flags = ACTION_RECURSE; \
 } while (0)
@@ -364,14 +473,32 @@ static int exec_actions(action ***appp, const char *fileName, const struct stat
 #if ENABLE_FEATURE_FIND_NOT
                        if (ap->invert) rc ^= TRUE;
 #endif
+                       dbg("grp %d action %d rc:0x%x", cur_group, cur_action, rc);
                        if (rc & TRUE) /* current group failed, try next */
                                break;
                }
        }
+       dbg("returning:0x%x", rc ^ TRUE);
        return rc ^ TRUE; /* restore TRUE bit */
 }
 
 
+#if !FNM_CASEFOLD
+static char *strcpy_upcase(char *dst, const char *src)
+{
+       char *d = dst;
+       while (1) {
+               unsigned char ch = *src++;
+               if (ch >= 'a' && ch <= 'z')
+                       ch -= ('a' - 'A');
+               *d++ = ch;
+               if (ch == '\0')
+                       break;
+       }
+       return dst;
+}
+#endif
+
 ACTF(name)
 {
        const char *tmp = bb_basename(fileName);
@@ -387,13 +514,25 @@ ACTF(name)
         * but somewhere between 4.1.20 and 4.4.0 GNU find stopped using it.
         * find -name '*foo' should match .foo too:
         */
+#if FNM_CASEFOLD
        return fnmatch(ap->pattern, tmp, (ap->iname ? FNM_CASEFOLD : 0)) == 0;
+#else
+       if (ap->iname)
+               tmp = strcpy_upcase(alloca(strlen(tmp) + 1), tmp);
+       return fnmatch(ap->pattern, tmp, 0) == 0;
+#endif
 }
 
 #if ENABLE_FEATURE_FIND_PATH
 ACTF(path)
 {
+# if FNM_CASEFOLD
+       return fnmatch(ap->pattern, fileName, (ap->ipath ? FNM_CASEFOLD : 0)) == 0;
+# else
+       if (ap->ipath)
+               fileName = strcpy_upcase(alloca(strlen(fileName) + 1), fileName);
        return fnmatch(ap->pattern, fileName, 0) == 0;
+# endif
 }
 #endif
 #if ENABLE_FEATURE_FIND_REGEX
@@ -586,16 +725,32 @@ ACTF(links)
 
 static int FAST_FUNC fileAction(const char *fileName,
                struct stat *statbuf,
-               void *userData IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM),
+               void *userData UNUSED_PARAM,
                int depth IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM))
 {
        int r;
-#if ENABLE_FEATURE_FIND_MAXDEPTH
-#define minmaxdepth ((int*)userData)
+       int same_fs = 1;
+
+#if ENABLE_FEATURE_FIND_XDEV
+       if (S_ISDIR(statbuf->st_mode) && G.xdev_count) {
+               int i;
+               for (i = 0; i < G.xdev_count; i++) {
+                       if (G.xdev_dev[i] == statbuf->st_dev)
+                               goto found;
+               }
+               //bb_error_msg("'%s': not same fs", fileName);
+               same_fs = 0;
+ found: ;
+       }
+#endif
 
-       if (depth < minmaxdepth[0])
-               return TRUE; /* skip this, continue recursing */
-       if (depth > minmaxdepth[1])
+#if ENABLE_FEATURE_FIND_MAXDEPTH
+       if (depth < G.minmaxdepth[0]) {
+               if (same_fs)
+                       return TRUE; /* skip this, continue recursing */
+               return SKIP; /* stop recursing */
+       }
+       if (depth > G.minmaxdepth[1])
                return SKIP; /* stop recursing */
 #endif
 
@@ -606,30 +761,19 @@ static int FAST_FUNC fileAction(const char *fileName,
 
 #if ENABLE_FEATURE_FIND_MAXDEPTH
        if (S_ISDIR(statbuf->st_mode)) {
-               if (depth == minmaxdepth[1])
+               if (depth == G.minmaxdepth[1])
                        return SKIP;
        }
 #endif
-#if ENABLE_FEATURE_FIND_XDEV
        /* -xdev stops on mountpoints, but AFTER mountpoit itself
         * is processed as usual */
-       if (S_ISDIR(statbuf->st_mode)) {
-               if (G.xdev_count) {
-                       int i;
-                       for (i = 0; i < G.xdev_count; i++) {
-                               if (G.xdev_dev[i] == statbuf->st_dev)
-                                       goto found;
-                       }
-                       return SKIP;
- found: ;
-               }
+       if (!same_fs) {
+               return SKIP;
        }
-#endif
 
        /* Cannot return 0: our caller, recursive_action(),
         * will perror() and skip dirs (if called on dir) */
        return (r & SKIP) ? SKIP : TRUE;
-#undef minmaxdepth
 }
 
 
@@ -671,9 +815,37 @@ static const char* plus_minus_num(const char* str)
 }
 #endif
 
+/* Say no to GCCism */
+#define USE_NESTED_FUNCTION 0
+
+#if !USE_NESTED_FUNCTION
+struct pp_locals {
+       action*** appp;
+       unsigned cur_group;
+       unsigned cur_action;
+       IF_FEATURE_FIND_NOT( bool invert_flag; )
+};
+static action* alloc_action(struct pp_locals *ppl, int sizeof_struct, action_fp f)
+{
+       action *ap = xzalloc(sizeof_struct);
+       action **app;
+       action ***group = &ppl->appp[ppl->cur_group];
+       *group = app = xrealloc(*group, (ppl->cur_action+2) * sizeof(ppl->appp[0][0]));
+       app[ppl->cur_action++] = ap;
+       app[ppl->cur_action] = NULL;
+       ap->f = f;
+       IF_FEATURE_FIND_NOT( ap->invert = ppl->invert_flag; )
+       IF_FEATURE_FIND_NOT( ppl->invert_flag = 0; )
+       return ap;
+}
+#endif
+
 static action*** parse_params(char **argv)
 {
        enum {
+                               OPT_FOLLOW     ,
+       IF_FEATURE_FIND_XDEV(   OPT_XDEV       ,)
+       IF_FEATURE_FIND_DEPTH(  OPT_DEPTH      ,)
                                PARM_a         ,
                                PARM_o         ,
        IF_FEATURE_FIND_NOT(    PARM_char_not  ,)
@@ -684,15 +856,20 @@ static action*** parse_params(char **argv)
 #endif
                                PARM_print     ,
        IF_FEATURE_FIND_PRINT0( PARM_print0    ,)
-       IF_FEATURE_FIND_DEPTH(  PARM_depth     ,)
        IF_FEATURE_FIND_PRUNE(  PARM_prune     ,)
        IF_FEATURE_FIND_DELETE( PARM_delete    ,)
        IF_FEATURE_FIND_EXEC(   PARM_exec      ,)
        IF_FEATURE_FIND_PAREN(  PARM_char_brace,)
-       /* All options starting from here require argument */
+       /* All options/actions starting from here require argument */
                                PARM_name      ,
                                PARM_iname     ,
        IF_FEATURE_FIND_PATH(   PARM_path      ,)
+#if ENABLE_DESKTOP
+       /* -wholename is a synonym for -path */
+       /* We support it because Linux kernel's "make tags" uses it */
+       IF_FEATURE_FIND_PATH(   PARM_wholename ,)
+#endif
+       IF_FEATURE_FIND_PATH(   PARM_ipath     ,)
        IF_FEATURE_FIND_REGEX(  PARM_regex     ,)
        IF_FEATURE_FIND_TYPE(   PARM_type      ,)
        IF_FEATURE_FIND_PERM(   PARM_perm      ,)
@@ -705,28 +882,35 @@ static action*** parse_params(char **argv)
        IF_FEATURE_FIND_SIZE(   PARM_size      ,)
        IF_FEATURE_FIND_CONTEXT(PARM_context   ,)
        IF_FEATURE_FIND_LINKS(  PARM_links     ,)
+       IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,OPT_MAXDEPTH,)
        };
 
        static const char params[] ALIGN1 =
-                                "-a\0"
-                                "-o\0"
+                               "-follow\0"
+       IF_FEATURE_FIND_XDEV(   "-xdev\0"                 )
+       IF_FEATURE_FIND_DEPTH(  "-depth\0"                )
+                               "-a\0"
+                               "-o\0"
        IF_FEATURE_FIND_NOT(    "!\0"       )
 #if ENABLE_DESKTOP
-                                "-and\0"
-                                "-or\0"
-       IF_FEATURE_FIND_NOT(     "-not\0"    )
+                               "-and\0"
+                               "-or\0"
+       IF_FEATURE_FIND_NOT(    "-not\0"    )
 #endif
-                                "-print\0"
+                               "-print\0"
        IF_FEATURE_FIND_PRINT0( "-print0\0" )
-       IF_FEATURE_FIND_DEPTH(  "-depth\0"  )
        IF_FEATURE_FIND_PRUNE(  "-prune\0"  )
        IF_FEATURE_FIND_DELETE( "-delete\0" )
        IF_FEATURE_FIND_EXEC(   "-exec\0"   )
        IF_FEATURE_FIND_PAREN(  "(\0"       )
-       /* All options starting from here require argument */
-                                "-name\0"
-                                "-iname\0"
+       /* All options/actions starting from here require argument */
+                               "-name\0"
+                               "-iname\0"
        IF_FEATURE_FIND_PATH(   "-path\0"   )
+#if ENABLE_DESKTOP
+       IF_FEATURE_FIND_PATH(   "-wholename\0")
+#endif
+       IF_FEATURE_FIND_PATH(   "-ipath\0"  )
        IF_FEATURE_FIND_REGEX(  "-regex\0"  )
        IF_FEATURE_FIND_TYPE(   "-type\0"   )
        IF_FEATURE_FIND_PERM(   "-perm\0"   )
@@ -739,54 +923,54 @@ static action*** parse_params(char **argv)
        IF_FEATURE_FIND_SIZE(   "-size\0"   )
        IF_FEATURE_FIND_CONTEXT("-context\0")
        IF_FEATURE_FIND_LINKS(  "-links\0"  )
-                                ;
+       IF_FEATURE_FIND_MAXDEPTH("-mindepth\0""-maxdepth\0")
+       ;
 
+#if !USE_NESTED_FUNCTION
+       struct pp_locals ppl;
+#define appp        (ppl.appp       )
+#define cur_group   (ppl.cur_group  )
+#define cur_action  (ppl.cur_action )
+#define invert_flag (ppl.invert_flag)
+#define ALLOC_ACTION(name) (action_##name*)alloc_action(&ppl, sizeof(action_##name), (action_fp) func_##name)
+#else
        action*** appp;
-       unsigned cur_group = 0;
-       unsigned cur_action = 0;
-       IF_FEATURE_FIND_NOT( bool invert_flag = 0; )
+       unsigned cur_group;
+       unsigned cur_action;
+       IF_FEATURE_FIND_NOT( bool invert_flag; )
 
        /* This is the only place in busybox where we use nested function.
         * So far more standard alternatives were bigger. */
-       /* Suppress a warning "func without a prototype" */
+       /* Auto decl suppresses "func without a prototype" warning: */
        auto action* alloc_action(int sizeof_struct, action_fp f);
        action* alloc_action(int sizeof_struct, action_fp f)
        {
                action *ap;
-               appp[cur_group] = xrealloc(appp[cur_group], (cur_action+2) * sizeof(*appp));
-               appp[cur_group][cur_action++] = ap = xmalloc(sizeof_struct);
+               appp[cur_group] = xrealloc(appp[cur_group], (cur_action+2) * sizeof(appp[0][0]));
+               appp[cur_group][cur_action++] = ap = xzalloc(sizeof_struct);
                appp[cur_group][cur_action] = NULL;
                ap->f = f;
                IF_FEATURE_FIND_NOT( ap->invert = invert_flag; )
                IF_FEATURE_FIND_NOT( invert_flag = 0; )
                return ap;
        }
-
 #define ALLOC_ACTION(name) (action_##name*)alloc_action(sizeof(action_##name), (action_fp) func_##name)
+#endif
 
+       cur_group = 0;
+       cur_action = 0;
+       IF_FEATURE_FIND_NOT( invert_flag = 0; )
        appp = xzalloc(2 * sizeof(appp[0])); /* appp[0],[1] == NULL */
 
-/* Actions have side effects and return a true or false value
- * We implement: -print, -print0, -exec
- *
- * The rest are tests.
- *
- * Tests and actions are grouped by operators
- * ( expr )              Force precedence
- * ! expr                True if expr is false
- * -not expr             Same as ! expr
- * expr1 [-a[nd]] expr2  And; expr2 is not evaluated if expr1 is false
- * expr1 -o[r] expr2     Or; expr2 is not evaluated if expr1 is true
- * expr1 , expr2         List; both expr1 and expr2 are always evaluated
- * We implement: (), -a, -o
- */
        while (*argv) {
                const char *arg = argv[0];
                int parm = index_in_strings(params, arg);
                const char *arg1 = argv[1];
 
+               dbg("arg:'%s' arg1:'%s' parm:%d PARM_type:%d", arg, arg1, parm, PARM_type);
+
                if (parm >= PARM_name) {
-                       /* All options starting from -name require argument */
+                       /* All options/actions starting from -name require argument */
                        if (!arg1)
                                bb_error_msg_and_die(bb_msg_requires_arg, arg);
                        argv++;
@@ -795,14 +979,52 @@ static action*** parse_params(char **argv)
                /* We can use big switch() here, but on i386
                 * it doesn't give smaller code. Other arches? */
 
-       /* --- Operators --- */
-               if (parm == PARM_a IF_DESKTOP(|| parm == PARM_and)) {
+/* Options always return true. They always take effect
+ * rather than being processed only when their place in the
+ * expression is reached.
+ */
+               /* Options */
+               if (parm == OPT_FOLLOW) {
+                       dbg("follow enabled: %d", __LINE__);
+                       G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK;
+               }
+#if ENABLE_FEATURE_FIND_XDEV
+               else if (parm == OPT_XDEV) {
+                       dbg("%d", __LINE__);
+                       G.xdev_on = 1;
+               }
+#endif
+#if ENABLE_FEATURE_FIND_MAXDEPTH
+               else if (parm == OPT_MINDEPTH || parm == OPT_MINDEPTH + 1) {
+                       dbg("%d", __LINE__);
+                       G.minmaxdepth[parm - OPT_MINDEPTH] = xatoi_positive(arg1);
+               }
+#endif
+#if ENABLE_FEATURE_FIND_DEPTH
+               else if (parm == OPT_DEPTH) {
+                       dbg("%d", __LINE__);
+                       G.recurse_flags |= ACTION_DEPTHFIRST;
+               }
+#endif
+/* Actions are grouped by operators
+ * ( expr )              Force precedence
+ * ! expr                True if expr is false
+ * -not expr             Same as ! expr
+ * expr1 [-a[nd]] expr2  And; expr2 is not evaluated if expr1 is false
+ * expr1 -o[r] expr2     Or; expr2 is not evaluated if expr1 is true
+ * expr1 , expr2         List; both expr1 and expr2 are always evaluated
+ * We implement: (), -a, -o
+ */
+               /* Operators */
+               else if (parm == PARM_a IF_DESKTOP(|| parm == PARM_and)) {
+                       dbg("%d", __LINE__);
                        /* no further special handling required */
                }
                else if (parm == PARM_o IF_DESKTOP(|| parm == PARM_or)) {
+                       dbg("%d", __LINE__);
                        /* start new OR group */
                        cur_group++;
-                       appp = xrealloc(appp, (cur_group+2) * sizeof(*appp));
+                       appp = xrealloc(appp, (cur_group+2) * sizeof(appp[0]));
                        /*appp[cur_group] = NULL; - already NULL */
                        appp[cur_group+1] = NULL;
                        cur_action = 0;
@@ -811,36 +1033,31 @@ static action*** parse_params(char **argv)
                else if (parm == PARM_char_not IF_DESKTOP(|| parm == PARM_not)) {
                        /* also handles "find ! ! -name 'foo*'" */
                        invert_flag ^= 1;
+                       dbg("invert_flag:%d", invert_flag);
                }
 #endif
-
-       /* --- Tests and actions --- */
+               /* Actions */
                else if (parm == PARM_print) {
+                       dbg("%d", __LINE__);
                        G.need_print = 0;
-                       /* GNU find ignores '!' here: "find ! -print" */
-                       IF_FEATURE_FIND_NOT( invert_flag = 0; )
                        (void) ALLOC_ACTION(print);
                }
 #if ENABLE_FEATURE_FIND_PRINT0
                else if (parm == PARM_print0) {
+                       dbg("%d", __LINE__);
                        G.need_print = 0;
-                       IF_FEATURE_FIND_NOT( invert_flag = 0; )
                        (void) ALLOC_ACTION(print0);
                }
 #endif
-#if ENABLE_FEATURE_FIND_DEPTH
-               else if (parm == PARM_depth) {
-                       G.recurse_flags |= ACTION_DEPTHFIRST;
-               }
-#endif
 #if ENABLE_FEATURE_FIND_PRUNE
                else if (parm == PARM_prune) {
-                       IF_FEATURE_FIND_NOT( invert_flag = 0; )
+                       dbg("%d", __LINE__);
                        (void) ALLOC_ACTION(prune);
                }
 #endif
 #if ENABLE_FEATURE_FIND_DELETE
                else if (parm == PARM_delete) {
+                       dbg("%d", __LINE__);
                        G.need_print = 0;
                        G.recurse_flags |= ACTION_DEPTHFIRST;
                        (void) ALLOC_ACTION(delete);
@@ -850,20 +1067,24 @@ static action*** parse_params(char **argv)
                else if (parm == PARM_exec) {
                        int i;
                        action_exec *ap;
+                       dbg("%d", __LINE__);
                        G.need_print = 0;
-                       IF_FEATURE_FIND_NOT( invert_flag = 0; )
                        ap = ALLOC_ACTION(exec);
                        ap->exec_argv = ++argv; /* first arg after -exec */
-                       ap->exec_argc = 0;
+                       /*ap->exec_argc = 0; - ALLOC_ACTION did it */
                        while (1) {
                                if (!*argv) /* did not see ';' or '+' until end */
                                        bb_error_msg_and_die(bb_msg_requires_arg, "-exec");
-                               if (LONE_CHAR(argv[0], ';'))
+                               // find -exec echo Foo ">{}<" ";"
+                               // executes "echo Foo >FILENAME<",
+                               // find -exec echo Foo ">{}<" "+"
+                               // executes "echo Foo FILENAME1 FILENAME2 FILENAME3...".
+                               // TODO (so far we treat "+" just like ";")
+                               if ((argv[0][0] == ';' || argv[0][0] == '+')
+                                && argv[0][1] == '\0'
+                               ) {
                                        break;
-                               //TODO: implement {} + (like xargs)
-                               // See:
-                               // find findutils/ -exec echo ">"{}"<" \;
-                               // find findutils/ -exec echo ">"{}"<" +
+                               }
                                argv++;
                                ap->exec_argc++;
                        }
@@ -881,6 +1102,7 @@ static action*** parse_params(char **argv)
                        char **endarg;
                        unsigned nested = 1;
 
+                       dbg("%d", __LINE__);
                        endarg = argv;
                        while (1) {
                                if (!*++endarg)
@@ -900,20 +1122,24 @@ static action*** parse_params(char **argv)
 #endif
                else if (parm == PARM_name || parm == PARM_iname) {
                        action_name *ap;
+                       dbg("%d", __LINE__);
                        ap = ALLOC_ACTION(name);
                        ap->pattern = arg1;
                        ap->iname = (parm == PARM_iname);
                }
 #if ENABLE_FEATURE_FIND_PATH
-               else if (parm == PARM_path) {
+               else if (parm == PARM_path IF_DESKTOP(|| parm == PARM_wholename) || parm == PARM_ipath) {
                        action_path *ap;
+                       dbg("%d", __LINE__);
                        ap = ALLOC_ACTION(path);
                        ap->pattern = arg1;
+                       ap->ipath = (parm == PARM_ipath);
                }
 #endif
 #if ENABLE_FEATURE_FIND_REGEX
                else if (parm == PARM_regex) {
                        action_regex *ap;
+                       dbg("%d", __LINE__);
                        ap = ALLOC_ACTION(regex);
                        xregcomp(&ap->compiled_pattern, arg1, 0 /*cflags*/);
                }
@@ -923,20 +1149,22 @@ static action*** parse_params(char **argv)
                        action_type *ap;
                        ap = ALLOC_ACTION(type);
                        ap->type_mask = find_type(arg1);
+                       dbg("created:type mask:%x", ap->type_mask);
                }
 #endif
 #if ENABLE_FEATURE_FIND_PERM
-/* -perm mode   File's permission bits are exactly mode (octal or symbolic).
+/* -perm BITS   File's mode bits are exactly BITS (octal or symbolic).
  *              Symbolic modes use mode 0 as a point of departure.
- * -perm -mode  All of the permission bits mode are set for the file.
- * -perm +mode  Any of the permission bits mode are set for the file.
+ * -perm -BITS  All of the BITS are set in file's mode.
+ * -perm +BITS  At least one of the BITS is set in file's mode.
  */
                else if (parm == PARM_perm) {
                        action_perm *ap;
+                       dbg("%d", __LINE__);
                        ap = ALLOC_ACTION(perm);
                        ap->perm_char = arg1[0];
                        arg1 = plus_minus_num(arg1);
-                       ap->perm_mask = 0;
+                       /*ap->perm_mask = 0; - ALLOC_ACTION did it */
                        if (!bb_parse_mode(arg1, &ap->perm_mask))
                                bb_error_msg_and_die("invalid mode '%s'", arg1);
                }
@@ -944,6 +1172,7 @@ static action*** parse_params(char **argv)
 #if ENABLE_FEATURE_FIND_MTIME
                else if (parm == PARM_mtime) {
                        action_mtime *ap;
+                       dbg("%d", __LINE__);
                        ap = ALLOC_ACTION(mtime);
                        ap->mtime_char = arg1[0];
                        ap->mtime_days = xatoul(plus_minus_num(arg1));
@@ -952,6 +1181,7 @@ static action*** parse_params(char **argv)
 #if ENABLE_FEATURE_FIND_MMIN
                else if (parm == PARM_mmin) {
                        action_mmin *ap;
+                       dbg("%d", __LINE__);
                        ap = ALLOC_ACTION(mmin);
                        ap->mmin_char = arg1[0];
                        ap->mmin_mins = xatoul(plus_minus_num(arg1));
@@ -961,6 +1191,7 @@ static action*** parse_params(char **argv)
                else if (parm == PARM_newer) {
                        struct stat stat_newer;
                        action_newer *ap;
+                       dbg("%d", __LINE__);
                        ap = ALLOC_ACTION(newer);
                        xstat(arg1, &stat_newer);
                        ap->newer_mtime = stat_newer.st_mtime;
@@ -969,6 +1200,7 @@ static action*** parse_params(char **argv)
 #if ENABLE_FEATURE_FIND_INUM
                else if (parm == PARM_inum) {
                        action_inum *ap;
+                       dbg("%d", __LINE__);
                        ap = ALLOC_ACTION(inum);
                        ap->inode_num = xatoul(arg1);
                }
@@ -976,6 +1208,7 @@ static action*** parse_params(char **argv)
 #if ENABLE_FEATURE_FIND_USER
                else if (parm == PARM_user) {
                        action_user *ap;
+                       dbg("%d", __LINE__);
                        ap = ALLOC_ACTION(user);
                        ap->uid = bb_strtou(arg1, NULL, 10);
                        if (errno)
@@ -985,6 +1218,7 @@ static action*** parse_params(char **argv)
 #if ENABLE_FEATURE_FIND_GROUP
                else if (parm == PARM_group) {
                        action_group *ap;
+                       dbg("%d", __LINE__);
                        ap = ALLOC_ACTION(group);
                        ap->gid = bb_strtou(arg1, NULL, 10);
                        if (errno)
@@ -1013,6 +1247,7 @@ static action*** parse_params(char **argv)
                                { "", 0 }
                        };
                        action_size *ap;
+                       dbg("%d", __LINE__);
                        ap = ALLOC_ACTION(size);
                        ap->size_char = arg1[0];
                        ap->size = XATOU_SFX(plus_minus_num(arg1), find_suffixes);
@@ -1021,8 +1256,9 @@ static action*** parse_params(char **argv)
 #if ENABLE_FEATURE_FIND_CONTEXT
                else if (parm == PARM_context) {
                        action_context *ap;
+                       dbg("%d", __LINE__);
                        ap = ALLOC_ACTION(context);
-                       ap->context = NULL;
+                       /*ap->context = NULL; - ALLOC_ACTION did it */
                        /* SELinux headers erroneously declare non-const parameter */
                        if (selinux_raw_to_trans_context((char*)arg1, &ap->context))
                                bb_simple_perror_msg(arg1);
@@ -1031,6 +1267,7 @@ static action*** parse_params(char **argv)
 #if ENABLE_FEATURE_FIND_LINKS
                else if (parm == PARM_links) {
                        action_links *ap;
+                       dbg("%d", __LINE__);
                        ap = ALLOC_ACTION(links);
                        ap->links_char = arg1[0];
                        ap->links_count = xatoul(plus_minus_num(arg1));
@@ -1042,197 +1279,94 @@ static action*** parse_params(char **argv)
                }
                argv++;
        }
+       dbg("exiting %s", __func__);
        return appp;
 #undef ALLOC_ACTION
+#undef appp
+#undef cur_action
+#undef invert_flag
 }
 
-//usage:#define find_trivial_usage
-//usage:       "[PATH]... [EXPRESSION]"
-//usage:#define find_full_usage "\n\n"
-//usage:       "Search for files. The default PATH is the current directory,\n"
-//usage:       "default EXPRESSION is '-print'\n"
-//usage:     "\nEXPRESSION may consist of:"
-//usage:     "\n       -follow         Follow symlinks"
-//usage:       IF_FEATURE_FIND_XDEV(
-//usage:     "\n       -xdev           Don't descend directories on other filesystems"
-//usage:       )
-//usage:       IF_FEATURE_FIND_MAXDEPTH(
-//usage:     "\n       -maxdepth N     Descend at most N levels. -maxdepth 0 applies"
-//usage:     "\n                       tests/actions to command line arguments only"
-//usage:       )
-//usage:     "\n       -mindepth N     Don't act on first N levels"
-//usage:     "\n       -name PATTERN   File name (w/o directory name) matches PATTERN"
-//usage:     "\n       -iname PATTERN  Case insensitive -name"
-//usage:       IF_FEATURE_FIND_PATH(
-//usage:     "\n       -path PATTERN   Path matches PATTERN"
-//usage:       )
-//usage:       IF_FEATURE_FIND_REGEX(
-//usage:     "\n       -regex PATTERN  Path matches regex PATTERN"
-//usage:       )
-//usage:       IF_FEATURE_FIND_TYPE(
-//usage:     "\n       -type X         File type is X (X is one of: f,d,l,b,c,...)"
-//usage:       )
-//usage:       IF_FEATURE_FIND_PERM(
-//usage:     "\n       -perm NNN       Permissions match any of (+NNN), all of (-NNN),"
-//usage:     "\n                       or exactly NNN"
-//usage:       )
-//usage:       IF_FEATURE_FIND_MTIME(
-//usage:     "\n       -mtime DAYS     Modified time is greater than (+N), less than (-N),"
-//usage:     "\n                       or exactly N days"
-//usage:       )
-//usage:       IF_FEATURE_FIND_MMIN(
-//usage:     "\n       -mmin MINS      Modified time is greater than (+N), less than (-N),"
-//usage:     "\n                       or exactly N minutes"
-//usage:       )
-//usage:       IF_FEATURE_FIND_NEWER(
-//usage:     "\n       -newer FILE     Modified time is more recent than FILE's"
-//usage:       )
-//usage:       IF_FEATURE_FIND_INUM(
-//usage:     "\n       -inum N         File has inode number N"
-//usage:       )
-//usage:       IF_FEATURE_FIND_USER(
-//usage:     "\n       -user NAME      File is owned by user NAME (numeric user ID allowed)"
-//usage:       )
-//usage:       IF_FEATURE_FIND_GROUP(
-//usage:     "\n       -group NAME     File belongs to group NAME (numeric group ID allowed)"
-//usage:       )
-//usage:       IF_FEATURE_FIND_DEPTH(
-//usage:     "\n       -depth          Process directory name after traversing it"
-//usage:       )
-//usage:       IF_FEATURE_FIND_SIZE(
-//usage:     "\n       -size N[bck]    File size is N (c:bytes,k:kbytes,b:512 bytes(def.))"
-//usage:     "\n                       +/-N: file size is bigger/smaller than N"
-//usage:       )
-//usage:       IF_FEATURE_FIND_LINKS(
-//usage:     "\n       -links N        Number of links is greater than (+N), less than (-N),"
-//usage:     "\n                       or exactly N"
-//usage:       )
-//usage:     "\n       -print          Print (default and assumed)"
-//usage:       IF_FEATURE_FIND_PRINT0(
-//usage:     "\n       -print0         Delimit output with null characters rather than"
-//usage:     "\n                       newlines"
-//usage:       )
-//usage:       IF_FEATURE_FIND_CONTEXT(
-//usage:     "\n       -context        File has specified security context"
-//usage:       )
-//usage:       IF_FEATURE_FIND_EXEC(
-//usage:     "\n       -exec CMD ARG ; Run CMD with all instances of {} replaced by the"
-//usage:     "\n                       matching files"
-//usage:       )
-//usage:       IF_FEATURE_FIND_PRUNE(
-//usage:     "\n       -prune          Stop traversing current subtree"
-//usage:       )
-//usage:       IF_FEATURE_FIND_DELETE(
-//usage:     "\n       -delete         Delete files, turns on -depth option"
-//usage:       )
-//usage:       IF_FEATURE_FIND_PAREN(
-//usage:     "\n       (EXPR)          Group an expression"
-//usage:       )
-//usage:
-//usage:#define find_example_usage
-//usage:       "$ find / -name passwd\n"
-//usage:       "/etc/passwd\n"
-
 int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int find_main(int argc UNUSED_PARAM, char **argv)
 {
-       static const char options[] ALIGN1 =
-                         "-follow\0"
-IF_FEATURE_FIND_XDEV(    "-xdev\0"    )
-IF_FEATURE_FIND_MAXDEPTH("-mindepth\0""-maxdepth\0")
-                         ;
-       enum {
-                         OPT_FOLLOW,
-IF_FEATURE_FIND_XDEV(    OPT_XDEV    ,)
-IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,)
-       };
-
-       char *arg;
-       char **argp;
        int i, firstopt, status = EXIT_SUCCESS;
-#if ENABLE_FEATURE_FIND_MAXDEPTH
-       int minmaxdepth[2] = { 0, INT_MAX };
-#else
-#define minmaxdepth NULL
-#endif
+       char **past_HLP, *saved;
 
        INIT_G();
 
-       for (firstopt = 1; argv[firstopt]; firstopt++) {
+       /* "find -type f" + getopt("+HLP") => disaster.
+        * Need to avoid getopt running into a non-HLP option.
+        * Do this by temporarily storing NULL there:
+        */
+       past_HLP = argv;
+       for (;;) {
+               saved = *++past_HLP;
+               if (!saved)
+                       break;
+               if (saved[0] != '-')
+                       break;
+               if (!saved[1])
+                       break; /* it is "-" */
+               if ((saved+1)[strspn(saved+1, "HLP")] != '\0')
+                       break;
+       }
+       *past_HLP = NULL;
+       /* "+": stop on first non-option */
+       i = getopt32(argv, "+HLP");
+       if (i & (1<<0))
+               G.recurse_flags |= ACTION_FOLLOWLINKS_L0 | ACTION_DANGLING_OK;
+       if (i & (1<<1))
+               G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK;
+       /* -P is default and is ignored */
+       argv = past_HLP; /* same result as "argv += optind;" */
+       *past_HLP = saved;
+
+       for (firstopt = 0; argv[firstopt]; firstopt++) {
                if (argv[firstopt][0] == '-')
                        break;
                if (ENABLE_FEATURE_FIND_NOT && LONE_CHAR(argv[firstopt], '!'))
                        break;
-#if ENABLE_FEATURE_FIND_PAREN
-               if (LONE_CHAR(argv[firstopt], '('))
+               if (ENABLE_FEATURE_FIND_PAREN && LONE_CHAR(argv[firstopt], '('))
                        break;
-#endif
        }
-       if (firstopt == 1) {
-               argv[0] = (char*)".";
-               argv--;
+       if (firstopt == 0) {
+               *--argv = (char*)".";
                firstopt++;
        }
 
-/* All options always return true. They always take effect
- * rather than being processed only when their place in the
- * expression is reached.
- * We implement: -follow, -xdev, -maxdepth
- */
-       /* Process options, and replace then with -a */
-       /* (-a will be ignored by recursive parser later) */
-       argp = &argv[firstopt];
-       while ((arg = argp[0])) {
-               int opt = index_in_strings(options, arg);
-               if (opt == OPT_FOLLOW) {
-                       G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK;
-                       argp[0] = (char*)"-a";
-               }
+       G.actions = parse_params(&argv[firstopt]);
+       argv[firstopt] = NULL;
+
 #if ENABLE_FEATURE_FIND_XDEV
-               if (opt == OPT_XDEV) {
-                       struct stat stbuf;
-                       if (!G.xdev_count) {
-                               G.xdev_count = firstopt - 1;
-                               G.xdev_dev = xzalloc(G.xdev_count * sizeof(G.xdev_dev[0]));
-                               for (i = 1; i < firstopt; i++) {
-                                       /* not xstat(): shouldn't bomb out on
-                                        * "find not_exist exist -xdev" */
-                                       if (stat(argv[i], &stbuf) == 0)
-                                               G.xdev_dev[i-1] = stbuf.st_dev;
-                                       /* else G.xdev_dev[i-1] stays 0 and
-                                        * won't match any real device dev_t */
-                               }
-                       }
-                       argp[0] = (char*)"-a";
-               }
-#endif
-#if ENABLE_FEATURE_FIND_MAXDEPTH
-               if (opt == OPT_MINDEPTH || opt == OPT_MINDEPTH + 1) {
-                       if (!argp[1])
-                               bb_show_usage();
-                       minmaxdepth[opt - OPT_MINDEPTH] = xatoi_u(argp[1]);
-                       argp[0] = (char*)"-a";
-                       argp[1] = (char*)"-a";
-                       argp++;
+       if (G.xdev_on) {
+               struct stat stbuf;
+
+               G.xdev_count = firstopt;
+               G.xdev_dev = xzalloc(G.xdev_count * sizeof(G.xdev_dev[0]));
+               for (i = 0; argv[i]; i++) {
+                       /* not xstat(): shouldn't bomb out on
+                        * "find not_exist exist -xdev" */
+                       if (stat(argv[i], &stbuf) == 0)
+                               G.xdev_dev[i] = stbuf.st_dev;
+                       /* else G.xdev_dev[i] stays 0 and
+                        * won't match any real device dev_t
+                        */
                }
-#endif
-               argp++;
        }
+#endif
 
-       G.actions = parse_params(&argv[firstopt]);
-
-       for (i = 1; i < firstopt; i++) {
+       for (i = 0; argv[i]; i++) {
                if (!recursive_action(argv[i],
                                G.recurse_flags,/* flags */
                                fileAction,     /* file action */
                                fileAction,     /* dir action */
-#if ENABLE_FEATURE_FIND_MAXDEPTH
-                               minmaxdepth,    /* user data */
-#else
                                NULL,           /* user data */
-#endif
-                               0))             /* depth */
+                               0)              /* depth */
+               ) {
                        status = EXIT_FAILURE;
+               }
        }
+
        return status;
 }
index ac290a9..a7bc4ca 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley
  * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 /* BB_AUDIT SUSv3 defects - unsupported option -x "match whole line only". */
 /* BB_AUDIT GNU defects - always acts as -a.  */
@@ -18,9 +18,9 @@
  * (C) 2006 Jac Goudsmit added -o option
  */
 
-//applet:IF_GREP(APPLET(grep, _BB_DIR_BIN, _BB_SUID_DROP))
-//applet:IF_FEATURE_GREP_EGREP_ALIAS(APPLET_ODDNAME(egrep, grep, _BB_DIR_BIN, _BB_SUID_DROP, egrep))
-//applet:IF_FEATURE_GREP_FGREP_ALIAS(APPLET_ODDNAME(fgrep, grep, _BB_DIR_BIN, _BB_SUID_DROP, fgrep))
+//applet:IF_GREP(APPLET(grep, BB_DIR_BIN, BB_SUID_DROP))
+//applet:IF_FEATURE_GREP_EGREP_ALIAS(APPLET_ODDNAME(egrep, grep, BB_DIR_BIN, BB_SUID_DROP, egrep))
+//applet:IF_FEATURE_GREP_FGREP_ALIAS(APPLET_ODDNAME(fgrep, grep, BB_DIR_BIN, BB_SUID_DROP, fgrep))
 
 //kbuild:lib-$(CONFIG_GREP) += grep.o
 
@@ -72,7 +72,6 @@
 //usage:       "PATTERN/-e PATTERN.../-f FILE [FILE]..."
 //usage:#define grep_full_usage "\n\n"
 //usage:       "Search for PATTERN in FILEs (or stdin)\n"
-//usage:     "\nOptions:"
 //usage:     "\n       -H      Add 'filename:' prefix"
 //usage:     "\n       -h      Do not add 'filename:' prefix"
 //usage:     "\n       -n      Add 'line_no:' prefix"
@@ -86,6 +85,7 @@
 //usage:     "\n       -r      Recurse"
 //usage:     "\n       -i      Ignore case"
 //usage:     "\n       -w      Match whole words only"
+//usage:     "\n       -x      Match whole lines only"
 //usage:     "\n       -F      PATTERN is a literal (not regexp)"
 //usage:       IF_FEATURE_GREP_EGREP_ALIAS(
 //usage:     "\n       -E      PATTERN is an extended regexp"
 //usage:#define fgrep_full_usage ""
 
 #define OPTSTR_GREP \
-       "lnqvscFiHhe:f:Lorm:w" \
+       "lnqvscFiHhe:f:Lorm:wx" \
        IF_FEATURE_GREP_CONTEXT("A:B:C:") \
        IF_FEATURE_GREP_EGREP_ALIAS("E") \
        IF_EXTRA_COMPAT("z") \
@@ -139,6 +139,7 @@ enum {
        OPTBIT_r, /* recurse dirs */
        OPTBIT_m, /* -m MAX_MATCHES */
        OPTBIT_w, /* -w whole word match */
+       OPTBIT_x, /* -x whole line match */
        IF_FEATURE_GREP_CONTEXT(    OPTBIT_A ,) /* -A NUM: after-match context */
        IF_FEATURE_GREP_CONTEXT(    OPTBIT_B ,) /* -B NUM: before-match context */
        IF_FEATURE_GREP_CONTEXT(    OPTBIT_C ,) /* -C NUM: -A and -B combined */
@@ -161,6 +162,7 @@ enum {
        OPT_r = 1 << OPTBIT_r,
        OPT_m = 1 << OPTBIT_m,
        OPT_w = 1 << OPTBIT_w,
+       OPT_x = 1 << OPTBIT_x,
        OPT_A = IF_FEATURE_GREP_CONTEXT(    (1 << OPTBIT_A)) + 0,
        OPT_B = IF_FEATURE_GREP_CONTEXT(    (1 << OPTBIT_B)) + 0,
        OPT_C = IF_FEATURE_GREP_CONTEXT(    (1 << OPTBIT_C)) + 0,
@@ -342,11 +344,40 @@ static int grep_file(FILE *file)
                while (pattern_ptr) {
                        gl = (grep_list_data_t *)pattern_ptr->data;
                        if (FGREP_FLAG) {
-                               found |= (((option_mask32 & OPT_i)
-                                       ? strcasestr(line, gl->pattern)
-                                       : strstr(line, gl->pattern)
-                                       ) != NULL);
+                               char *match;
+                               char *str = line;
+ opt_f_again:
+                               match = ((option_mask32 & OPT_i)
+                                       ? strcasestr(str, gl->pattern)
+                                       : strstr(str, gl->pattern)
+                                       );
+                               if (match) {
+                                       if (option_mask32 & OPT_x) {
+                                               if (match != str)
+                                                       goto opt_f_not_found;
+                                               if (str[strlen(gl->pattern)] != '\0')
+                                                       goto opt_f_not_found;
+                                       } else
+                                       if (option_mask32 & OPT_w) {
+                                               char c = (match != str) ? match[-1] : ' ';
+                                               if (!isalnum(c) && c != '_') {
+                                                       c = match[strlen(gl->pattern)];
+                                                       if (!c || (!isalnum(c) && c != '_'))
+                                                               goto opt_f_found;
+                                               }
+                                               str = match + 1;
+                                               goto opt_f_again;
+                                       }
+ opt_f_found:
+                                       found = 1;
+ opt_f_not_found: ;
+                               }
                        } else {
+#if ENABLE_EXTRA_COMPAT
+                               unsigned start_pos;
+#endif
+                               char *match_at;
+
                                if (!(gl->flg_mem_alocated_compiled & COMPILED)) {
                                        gl->flg_mem_alocated_compiled |= COMPILED;
 #if !ENABLE_EXTRA_COMPAT
@@ -361,26 +392,55 @@ static int grep_file(FILE *file)
 #if !ENABLE_EXTRA_COMPAT
                                gl->matched_range.rm_so = 0;
                                gl->matched_range.rm_eo = 0;
+#else
+                               start_pos = 0;
 #endif
+                               match_at = line;
+ opt_w_again:
+//bb_error_msg("'%s' start_pos:%d line_len:%d", match_at, start_pos, line_len);
                                if (
 #if !ENABLE_EXTRA_COMPAT
-                                       regexec(&gl->compiled_regex, line, 1, &gl->matched_range, 0) == 0
+                                       regexec(&gl->compiled_regex, match_at, 1, &gl->matched_range, 0) == 0
 #else
-                                       re_search(&gl->compiled_regex, line, line_len,
-                                                       /*start:*/ 0, /*range:*/ line_len,
+                                       re_search(&gl->compiled_regex, match_at, line_len,
+                                                       start_pos, /*range:*/ line_len,
                                                        &gl->matched_range) >= 0
 #endif
                                ) {
-                                       if (!(option_mask32 & OPT_w))
+                                       if (option_mask32 & OPT_x) {
+                                               found = (gl->matched_range.rm_so == 0
+                                                        && match_at[gl->matched_range.rm_eo] == '\0');
+                                       } else
+                                       if (!(option_mask32 & OPT_w)) {
                                                found = 1;
-                                       else {
+                                       else {
                                                char c = ' ';
                                                if (gl->matched_range.rm_so)
-                                                       c = line[gl->matched_range.rm_so - 1];
+                                                       c = match_at[gl->matched_range.rm_so - 1];
                                                if (!isalnum(c) && c != '_') {
-                                                       c = line[gl->matched_range.rm_eo];
-                                                       if (!c || (!isalnum(c) && c != '_'))
+                                                       c = match_at[gl->matched_range.rm_eo];
+                                                       if (!c || (!isalnum(c) && c != '_')) {
                                                                found = 1;
+                                                       } else {
+                       /*
+                        * Why check gl->matched_range.rm_eo?
+                        * Zero-length match makes -w skip the line:
+                        * "echo foo | grep ^" prints "foo",
+                        * "echo foo | grep -w ^" prints nothing.
+                        * Without such check, we can loop forever.
+                        */
+#if !ENABLE_EXTRA_COMPAT
+                                                               if (gl->matched_range.rm_eo != 0) {
+                                                                       match_at += gl->matched_range.rm_eo;
+                                                                       goto opt_w_again;
+                                                               }
+#else
+                                                               if (gl->matched_range.rm_eo > start_pos) {
+                                                                       start_pos = gl->matched_range.rm_eo;
+                                                                       goto opt_w_again;
+                                                               }
+#endif
+                                                       }
                                                }
                                        }
                                }
@@ -461,15 +521,19 @@ static int grep_file(FILE *file)
                                                if (found)
                                                        print_line(gl->pattern, strlen(gl->pattern), linenum, ':');
                                        } else while (1) {
+                                               unsigned start = gl->matched_range.rm_so;
                                                unsigned end = gl->matched_range.rm_eo;
+                                               unsigned len = end - start;
                                                char old = line[end];
                                                line[end] = '\0';
-                                               print_line(line + gl->matched_range.rm_so,
-                                                               end - gl->matched_range.rm_so,
-                                                               linenum, ':');
+                                               /* Empty match is not printed: try "echo test | grep -o ''" */
+                                               if (len != 0)
+                                                       print_line(line + start, len, linenum, ':');
                                                if (old == '\0')
                                                        break;
                                                line[end] = old;
+                                               if (len == 0)
+                                                       end++;
 #if !ENABLE_EXTRA_COMPAT
                                                if (regexec(&gl->compiled_regex, line + end,
                                                                1, &gl->matched_range, REG_NOTBOL) != 0)
@@ -559,20 +623,20 @@ static char *add_grep_list_data(char *pattern)
 
 static void load_regexes_from_file(llist_t *fopt)
 {
-       char *line;
-       FILE *f;
-
        while (fopt) {
+               char *line;
+               FILE *fp;
                llist_t *cur = fopt;
                char *ffile = cur->data;
 
                fopt = cur->link;
                free(cur);
-               f = xfopen_stdin(ffile);
-               while ((line = xmalloc_fgetline(f)) != NULL) {
+               fp = xfopen_stdin(ffile);
+               while ((line = xmalloc_fgetline(fp)) != NULL) {
                        llist_add_to(&pattern_head,
                                new_grep_list_data(line, ALLOCATED));
                }
+               fclose_if_not_stdin(fp);
        }
 }
 
@@ -617,30 +681,33 @@ int grep_main(int argc UNUSED_PARAM, char **argv)
 
        /* do normal option parsing */
 #if ENABLE_FEATURE_GREP_CONTEXT
-       int Copt;
+       int Copt, opts;
 
        /* -H unsets -h; -C unsets -A,-B; -e,-f are lists;
         * -m,-A,-B,-C have numeric param */
        opt_complementary = "H-h:C-AB:e::f::m+:A+:B+:C+";
-       getopt32(argv,
+       opts = getopt32(argv,
                OPTSTR_GREP,
                &pattern_head, &fopt, &max_matches,
                &lines_after, &lines_before, &Copt);
 
-       if (option_mask32 & OPT_C) {
+       if (opts & OPT_C) {
                /* -C unsets prev -A and -B, but following -A or -B
-                  may override it */
-               if (!(option_mask32 & OPT_A)) /* not overridden */
+                * may override it */
+               if (!(opts & OPT_A)) /* not overridden */
                        lines_after = Copt;
-               if (!(option_mask32 & OPT_B)) /* not overridden */
+               if (!(opts & OPT_B)) /* not overridden */
                        lines_before = Copt;
        }
        /* sanity checks */
-       if (option_mask32 & (OPT_c|OPT_q|OPT_l|OPT_L)) {
+       if (opts & (OPT_c|OPT_q|OPT_l|OPT_L)) {
                option_mask32 &= ~OPT_n;
                lines_before = 0;
                lines_after = 0;
        } else if (lines_before > 0) {
+               if (lines_before > INT_MAX / sizeof(long long))
+                       lines_before = INT_MAX / sizeof(long long);
+               /* overflow in (lines_before * sizeof(x)) is prevented (above) */
                before_buf = xzalloc(lines_before * sizeof(before_buf[0]));
                IF_EXTRA_COMPAT(before_buf_size = xzalloc(lines_before * sizeof(before_buf_size[0]));)
        }
@@ -653,21 +720,25 @@ int grep_main(int argc UNUSED_PARAM, char **argv)
 #endif
        invert_search = ((option_mask32 & OPT_v) != 0); /* 0 | 1 */
 
-       if (pattern_head != NULL) {
-               /* convert char **argv to grep_list_data_t */
+       {       /* convert char **argv to grep_list_data_t */
                llist_t *cur;
-
                for (cur = pattern_head; cur; cur = cur->link)
                        cur->data = new_grep_list_data(cur->data, 0);
        }
-       if (option_mask32 & OPT_f)
+       if (option_mask32 & OPT_f) {
                load_regexes_from_file(fopt);
+               if (!pattern_head) { /* -f EMPTY_FILE? */
+                       /* GNU grep treats it as "nothing matches" */
+                       llist_add_to(&pattern_head, new_grep_list_data((char*) "", 0));
+                       invert_search ^= 1;
+               }
+       }
 
        if (ENABLE_FEATURE_GREP_FGREP_ALIAS && applet_name[0] == 'f')
                option_mask32 |= OPT_F;
 
 #if !ENABLE_EXTRA_COMPAT
-       if (!(option_mask32 & (OPT_o | OPT_w)))
+       if (!(option_mask32 & (OPT_o | OPT_w | OPT_x)))
                reflags = REG_NOSUB;
 #endif
 
index 7b9f1fb..0d1bb43 100644 (file)
@@ -9,16 +9,12 @@
  * - Mike Rendell <michael@cs.mun.ca>
  * and David MacKenzie <djm@gnu.ai.mit.edu>.
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * xargs is described in the Single Unix Specification v3 at
  * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html
  */
 
-//applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, _BB_DIR_USR_BIN, _BB_SUID_DROP, xargs))
-
-//kbuild:lib-$(CONFIG_XARGS) += xargs.o
-
 //config:config XARGS
 //config:      bool "xargs"
 //config:      default y
 //config:        instead of whitespace, and the quotes and backslash
 //config:        are not special.
 
+//applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, BB_DIR_USR_BIN, BB_SUID_DROP, xargs))
+
+//kbuild:lib-$(CONFIG_XARGS) += xargs.o
+
 #include "libbb.h"
 
 /* This is a NOEXEC applet. Be very careful! */
@@ -89,7 +89,9 @@ struct globals {
        int idx;
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
-#define INIT_G() do { } while (0)
+#define INIT_G() do { \
+       G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \
+} while (0)
 
 
 /*
@@ -347,7 +349,6 @@ static int xargs_ask_confirmation(void)
 //usage:       "[OPTIONS] [PROG ARGS]"
 //usage:#define xargs_full_usage "\n\n"
 //usage:       "Run PROG on every item given by stdin\n"
-//usage:     "\nOptions:"
 //usage:       IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(
 //usage:     "\n       -p      Ask user whether to run each command"
 //usage:       )
@@ -412,7 +413,12 @@ int xargs_main(int argc, char **argv)
 
        INIT_G();
 
-       G.eof_str = NULL;
+#if ENABLE_DESKTOP && ENABLE_LONG_OPTS
+       /* For example, Fedora's build system uses --no-run-if-empty */
+       applet_long_options =
+               "no-run-if-empty\0" No_argument "r"
+               ;
+#endif
        opt = getopt32(argv, OPTION_STR, &max_args, &max_chars, &G.eof_str, &G.eof_str);
 
        /* -E ""? You may wonder why not just omit -E?
index f0ce546..9d9b6c4 100644 (file)
@@ -1,6 +1,10 @@
 /config
 
+/applets.h
 /applet_tables.h
 /autoconf.h
+/bbconfigopts_bz2.h
 /bbconfigopts.h
+/NUM_APPLETS.h
 /usage_compressed.h
+/usage.h
diff --git a/include/applet_metadata.h b/include/applet_metadata.h
new file mode 100644 (file)
index 0000000..566ef35
--- /dev/null
@@ -0,0 +1,30 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#ifndef APPLET_METADATA_H
+#define APPLET_METADATA_H 1
+
+/* Note: can be included by both host and target builds! */
+
+/* order matters: used as index into "install_dir[]" in appletlib.c */
+typedef enum bb_install_loc_t {
+       BB_DIR_ROOT = 0,
+       BB_DIR_BIN,
+       BB_DIR_SBIN,
+#if ENABLE_INSTALL_NO_USR
+       BB_DIR_USR_BIN  = BB_DIR_BIN,
+       BB_DIR_USR_SBIN = BB_DIR_SBIN,
+#else
+       BB_DIR_USR_BIN,
+       BB_DIR_USR_SBIN,
+#endif
+} bb_install_loc_t;
+
+typedef enum bb_suid_t {
+       BB_SUID_DROP = 0,
+       BB_SUID_MAYBE,
+       BB_SUID_REQUIRE
+} bb_suid_t;
+
+#endif
index 9162b66..7dbd4c7 100644 (file)
@@ -12,10 +12,14 @@ name2 - applet name, converted to C (ether-wake: name2 = ether_wake)
 main  - corresponding <applet>_main to call (bzcat: main = bunzip2)
 l     - location to install link to: [/usr]/[s]bin
 s     - suid type:
-        _BB_SUID_REQUIRE: will complain if busybox isn't suid
+        BB_SUID_REQUIRE: will complain if busybox isn't suid
         and is run by non-root (applet_main() will not be called at all)
-        _BB_SUID_DROP: will drop suid prior to applet_main()
-        _BB_SUID_MAYBE: neither of the above
+        BB_SUID_DROP: will drop suid prior to applet_main()
+        BB_SUID_MAYBE: neither of the above
+        (every instance of BB_SUID_REQUIRE and BB_SUID_MAYBE
+        needs to be justified in comment)
+        NB: please update FEATURE_SUID help text whenever you add/remove
+        BB_SUID_REQUIRE or BB_SUID_MAYBE applet.
 */
 
 #if defined(PROTOTYPES)
@@ -48,6 +52,12 @@ s     - suid type:
 # define APPLET_NOEXEC(name,main,l,s,name2)  LINK l name
 # define APPLET_NOFORK(name,main,l,s,name2)  LINK l name
 
+#elif defined(MAKE_SUID)
+# define APPLET(name,l,s)                    SUID s l name
+# define APPLET_ODDNAME(name,main,l,s,name2) SUID s l name
+# define APPLET_NOEXEC(name,main,l,s,name2)  SUID s l name
+# define APPLET_NOFORK(name,main,l,s,name2)  SUID s l name
+
 #else
   static struct bb_applet applets[] = { /*    name, main, location, need_suid */
 # define APPLET(name,l,s)                    { #name, #name, l, s },
@@ -57,385 +67,332 @@ s     - suid type:
 #endif
 
 #if ENABLE_INSTALL_NO_USR
-# define _BB_DIR_USR_BIN _BB_DIR_BIN
-# define _BB_DIR_USR_SBIN _BB_DIR_SBIN
+# define BB_DIR_USR_BIN BB_DIR_BIN
+# define BB_DIR_USR_SBIN BB_DIR_SBIN
 #endif
 
 
 INSERT
-IF_TEST(APPLET_NOFORK([,  test, _BB_DIR_USR_BIN, _BB_SUID_DROP, test))
-IF_TEST(APPLET_NOFORK([[, test, _BB_DIR_USR_BIN, _BB_SUID_DROP, test))
-IF_ACPID(APPLET(acpid, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_ADDGROUP(APPLET(addgroup, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_ADDUSER(APPLET(adduser, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_ADJTIMEX(APPLET(adjtimex, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_AR(APPLET(ar, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_ARP(APPLET(arp, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_ARPING(APPLET(arping, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_ASH(APPLET(ash, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_AWK(APPLET_NOEXEC(awk, awk, _BB_DIR_USR_BIN, _BB_SUID_DROP, awk))
-IF_BASENAME(APPLET_NOFORK(basename, basename, _BB_DIR_USR_BIN, _BB_SUID_DROP, basename))
-IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, _BB_DIR_BIN, _BB_SUID_DROP, bash))
-IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, _BB_DIR_BIN, _BB_SUID_DROP, bash))
-IF_BBCONFIG(APPLET(bbconfig, _BB_DIR_BIN, _BB_SUID_DROP))
-//IF_BBSH(APPLET(bbsh, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_BEEP(APPLET(beep, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_BLKID(APPLET(blkid, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_BOOTCHARTD(APPLET(bootchartd, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_BRCTL(APPLET(brctl, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_BZIP2(APPLET(bzip2, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_CAL(APPLET(cal, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_CAT(APPLET_NOFORK(cat, cat, _BB_DIR_BIN, _BB_SUID_DROP, cat))
-IF_CATV(APPLET(catv, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_CHAT(APPLET(chat, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_CHATTR(APPLET(chattr, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_CHCON(APPLET(chcon, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_CHGRP(APPLET_NOEXEC(chgrp, chgrp, _BB_DIR_BIN, _BB_SUID_DROP, chgrp))
-IF_CHMOD(APPLET_NOEXEC(chmod, chmod, _BB_DIR_BIN, _BB_SUID_DROP, chmod))
-IF_CHOWN(APPLET_NOEXEC(chown, chown, _BB_DIR_BIN, _BB_SUID_DROP, chown))
-IF_CHPASSWD(APPLET(chpasswd, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_CHPST(APPLET(chpst, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_CHROOT(APPLET(chroot, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_CHRT(APPLET(chrt, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_CHVT(APPLET(chvt, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_CKSUM(APPLET(cksum, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_CLEAR(APPLET(clear, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_CMP(APPLET(cmp, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_COMM(APPLET(comm, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_CP(APPLET_NOEXEC(cp, cp, _BB_DIR_BIN, _BB_SUID_DROP, cp))
-IF_CPIO(APPLET(cpio, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_CROND(APPLET(crond, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_CRONTAB(APPLET(crontab, _BB_DIR_USR_BIN, _BB_SUID_REQUIRE))
-IF_CRYPTPW(APPLET(cryptpw, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_CTTYHACK(APPLET(cttyhack, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_CUT(APPLET_NOEXEC(cut, cut, _BB_DIR_USR_BIN, _BB_SUID_DROP, cut))
-IF_DATE(APPLET(date, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_DC(APPLET(dc, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_DD(APPLET_NOEXEC(dd, dd, _BB_DIR_BIN, _BB_SUID_DROP, dd))
-IF_DEALLOCVT(APPLET(deallocvt, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_DELGROUP(APPLET_ODDNAME(delgroup, deluser, _BB_DIR_BIN, _BB_SUID_DROP, delgroup))
-IF_DELUSER(APPLET(deluser, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_DEPMOD(APPLET(depmod, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_MODPROBE_SMALL(APPLET_ODDNAME(depmod, modprobe, _BB_DIR_SBIN, _BB_SUID_DROP, modprobe))
-IF_DEVFSD(APPLET(devfsd, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_DEVMEM(APPLET(devmem, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_DF(APPLET(df, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_DHCPRELAY(APPLET(dhcprelay, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_DIFF(APPLET(diff, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_DIRNAME(APPLET_NOFORK(dirname, dirname, _BB_DIR_USR_BIN, _BB_SUID_DROP, dirname))
-IF_DMESG(APPLET(dmesg, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_DNSD(APPLET(dnsd, _BB_DIR_USR_SBIN, _BB_SUID_REQUIRE))
-IF_HOSTNAME(APPLET_ODDNAME(dnsdomainname, hostname, _BB_DIR_BIN, _BB_SUID_DROP, dnsdomainname))
-IF_DOS2UNIX(APPLET(dos2unix, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_DPKG(APPLET(dpkg, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_DPKG_DEB(APPLET_ODDNAME(dpkg-deb, dpkg_deb, _BB_DIR_USR_BIN, _BB_SUID_DROP, dpkg_deb))
-IF_DU(APPLET(du, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_DUMPKMAP(APPLET(dumpkmap, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_DUMPLEASES(APPLET(dumpleases, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-//IF_E2FSCK(APPLET(e2fsck, _BB_DIR_SBIN, _BB_SUID_DROP))
-//IF_E2LABEL(APPLET_ODDNAME(e2label, tune2fs, _BB_DIR_SBIN, _BB_SUID_DROP, e2label))
-IF_ECHO(APPLET_NOFORK(echo, echo, _BB_DIR_BIN, _BB_SUID_DROP, echo))
-IF_ED(APPLET(ed, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_EJECT(APPLET(eject, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_ENV(APPLET_NOEXEC(env, env, _BB_DIR_USR_BIN, _BB_SUID_DROP, env))
-IF_ENVDIR(APPLET_ODDNAME(envdir, chpst, _BB_DIR_USR_BIN, _BB_SUID_DROP, envdir))
-IF_ENVUIDGID(APPLET_ODDNAME(envuidgid, chpst, _BB_DIR_USR_BIN, _BB_SUID_DROP, envuidgid))
-IF_ETHER_WAKE(APPLET_ODDNAME(ether-wake, ether_wake, _BB_DIR_USR_BIN, _BB_SUID_DROP, ether_wake))
-IF_EXPAND(APPLET(expand, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_EXPR(APPLET(expr, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_FAKEIDENTD(APPLET(fakeidentd, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_FALSE(APPLET_NOFORK(false, false, _BB_DIR_BIN, _BB_SUID_DROP, false))
-IF_FBSET(APPLET(fbset, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_FBSPLASH(APPLET(fbsplash, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_FDFLUSH(APPLET_ODDNAME(fdflush, freeramdisk, _BB_DIR_BIN, _BB_SUID_DROP, fdflush))
-IF_FDFORMAT(APPLET(fdformat, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_FDISK(APPLET(fdisk, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_FGCONSOLE(APPLET(fgconsole, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_FINDFS(APPLET(findfs, _BB_DIR_SBIN, _BB_SUID_MAYBE))
-IF_FLASH_ERASEALL(APPLET(flash_eraseall, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_FLASH_LOCK(APPLET_ODDNAME(flash_lock, flash_lock_unlock, _BB_DIR_USR_SBIN, _BB_SUID_DROP, flash_lock))
-IF_FLASH_UNLOCK(APPLET_ODDNAME(flash_unlock, flash_lock_unlock, _BB_DIR_USR_SBIN, _BB_SUID_DROP, flash_unlock))
-IF_FLASHCP(APPLET(flashcp, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_FLOCK(APPLET(flock, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_FOLD(APPLET(fold, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_FREE(APPLET(free, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_FREERAMDISK(APPLET(freeramdisk, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_FSCK(APPLET(fsck, _BB_DIR_SBIN, _BB_SUID_DROP))
-//IF_E2FSCK(APPLET_ODDNAME(fsck.ext2, e2fsck, _BB_DIR_SBIN, _BB_SUID_DROP, fsck_ext2))
-//IF_E2FSCK(APPLET_ODDNAME(fsck.ext3, e2fsck, _BB_DIR_SBIN, _BB_SUID_DROP, fsck_ext3))
-IF_FSCK_MINIX(APPLET_ODDNAME(fsck.minix, fsck_minix, _BB_DIR_SBIN, _BB_SUID_DROP, fsck_minix))
-IF_FSYNC(APPLET_NOFORK(fsync, fsync, _BB_DIR_BIN, _BB_SUID_DROP, fsync))
-IF_FTPD(APPLET(ftpd, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_FTPGET(APPLET_ODDNAME(ftpget, ftpgetput, _BB_DIR_USR_BIN, _BB_SUID_DROP, ftpget))
-IF_FTPPUT(APPLET_ODDNAME(ftpput, ftpgetput, _BB_DIR_USR_BIN, _BB_SUID_DROP, ftpput))
-IF_FUSER(APPLET(fuser, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_GETENFORCE(APPLET(getenforce, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_GETOPT(APPLET(getopt, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_GETSEBOOL(APPLET(getsebool, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_GETTY(APPLET(getty, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_GUNZIP(APPLET(gunzip, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_GZIP(APPLET(gzip, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_HALT(APPLET(halt, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_HD(APPLET_NOEXEC(hd, hexdump, _BB_DIR_USR_BIN, _BB_SUID_DROP, hd))
-IF_HDPARM(APPLET(hdparm, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_HEAD(APPLET(head, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_HEXDUMP(APPLET_NOEXEC(hexdump, hexdump, _BB_DIR_USR_BIN, _BB_SUID_DROP, hexdump))
-IF_HOSTID(APPLET_NOFORK(hostid, hostid, _BB_DIR_USR_BIN, _BB_SUID_DROP, hostid))
-IF_HOSTNAME(APPLET(hostname, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_HTTPD(APPLET(httpd, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_HUSH(APPLET(hush, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_HWCLOCK(APPLET(hwclock, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_ID(APPLET(id, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_IFCONFIG(APPLET(ifconfig, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_IFUPDOWN(APPLET_ODDNAME(ifdown, ifupdown, _BB_DIR_SBIN, _BB_SUID_DROP, ifdown))
-IF_IFENSLAVE(APPLET(ifenslave, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_IFPLUGD(APPLET(ifplugd, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_IFUPDOWN(APPLET_ODDNAME(ifup, ifupdown, _BB_DIR_SBIN, _BB_SUID_DROP, ifup))
-IF_INETD(APPLET(inetd, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_INIT(APPLET(init, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_INOTIFYD(APPLET(inotifyd, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_INSMOD(APPLET(insmod, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_MODPROBE_SMALL(APPLET_ODDNAME(insmod, modprobe, _BB_DIR_SBIN, _BB_SUID_DROP, modprobe))
-IF_INSTALL(APPLET(install, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_IONICE(APPLET(ionice, _BB_DIR_BIN, _BB_SUID_DROP))
+IF_TEST(APPLET_NOFORK([,  test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
+IF_TEST(APPLET_NOFORK([[, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
+IF_ACPID(APPLET(acpid, BB_DIR_SBIN, BB_SUID_DROP))
+IF_ADDGROUP(APPLET(addgroup, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_ADDUSER(APPLET(adduser, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_ADJTIMEX(APPLET(adjtimex, BB_DIR_SBIN, BB_SUID_DROP))
+IF_ARP(APPLET(arp, BB_DIR_SBIN, BB_SUID_DROP))
+IF_ARPING(APPLET(arping, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_BASENAME(APPLET_NOFORK(basename, basename, BB_DIR_USR_BIN, BB_SUID_DROP, basename))
+IF_BBCONFIG(APPLET(bbconfig, BB_DIR_BIN, BB_SUID_DROP))
+IF_BEEP(APPLET(beep, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_BLKID(APPLET(blkid, BB_DIR_SBIN, BB_SUID_DROP))
+IF_BRCTL(APPLET(brctl, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_CAL(APPLET(cal, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_CAT(APPLET_NOFORK(cat, cat, BB_DIR_BIN, BB_SUID_DROP, cat))
+IF_CATV(APPLET(catv, BB_DIR_BIN, BB_SUID_DROP))
+IF_CHAT(APPLET(chat, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_CHATTR(APPLET(chattr, BB_DIR_BIN, BB_SUID_DROP))
+IF_CHCON(APPLET(chcon, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_CHGRP(APPLET_NOEXEC(chgrp, chgrp, BB_DIR_BIN, BB_SUID_DROP, chgrp))
+IF_CHMOD(APPLET_NOEXEC(chmod, chmod, BB_DIR_BIN, BB_SUID_DROP, chmod))
+IF_CHOWN(APPLET_NOEXEC(chown, chown, BB_DIR_BIN, BB_SUID_DROP, chown))
+IF_CHPASSWD(APPLET(chpasswd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_CHPST(APPLET(chpst, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_CHROOT(APPLET(chroot, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_CHRT(APPLET(chrt, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_CHVT(APPLET(chvt, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_CKSUM(APPLET_NOEXEC(cksum, cksum, BB_DIR_USR_BIN, BB_SUID_DROP, cksum))
+IF_CLEAR(APPLET(clear, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_COMM(APPLET(comm, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_CP(APPLET_NOEXEC(cp, cp, BB_DIR_BIN, BB_SUID_DROP, cp))
+IF_CROND(APPLET(crond, BB_DIR_USR_SBIN, BB_SUID_DROP))
+/* Needs to be run by root or be suid root - needs to change /var/spool/cron* files: */
+IF_CRONTAB(APPLET(crontab, BB_DIR_USR_BIN, BB_SUID_REQUIRE))
+IF_CRYPTPW(APPLET(cryptpw, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_CUT(APPLET_NOEXEC(cut, cut, BB_DIR_USR_BIN, BB_SUID_DROP, cut))
+IF_DC(APPLET(dc, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_DD(APPLET_NOEXEC(dd, dd, BB_DIR_BIN, BB_SUID_DROP, dd))
+IF_DEALLOCVT(APPLET(deallocvt, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_DELGROUP(APPLET_ODDNAME(delgroup, deluser, BB_DIR_USR_SBIN, BB_SUID_DROP, delgroup))
+IF_DELUSER(APPLET(deluser, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_DEVFSD(APPLET(devfsd, BB_DIR_SBIN, BB_SUID_DROP))
+IF_DEVMEM(APPLET(devmem, BB_DIR_SBIN, BB_SUID_DROP))
+IF_DF(APPLET(df, BB_DIR_BIN, BB_SUID_DROP))
+IF_DHCPRELAY(APPLET(dhcprelay, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_DIRNAME(APPLET_NOFORK(dirname, dirname, BB_DIR_USR_BIN, BB_SUID_DROP, dirname))
+IF_DMESG(APPLET(dmesg, BB_DIR_BIN, BB_SUID_DROP))
+IF_DNSD(APPLET(dnsd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_HOSTNAME(APPLET_ODDNAME(dnsdomainname, hostname, BB_DIR_BIN, BB_SUID_DROP, dnsdomainname))
+IF_DOS2UNIX(APPLET_NOEXEC(dos2unix, dos2unix, BB_DIR_USR_BIN, BB_SUID_DROP, dos2unix))
+IF_DU(APPLET(du, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_DUMPKMAP(APPLET(dumpkmap, BB_DIR_BIN, BB_SUID_DROP))
+IF_DUMPLEASES(APPLET(dumpleases, BB_DIR_USR_BIN, BB_SUID_DROP))
+//IF_E2FSCK(APPLET(e2fsck, BB_DIR_SBIN, BB_SUID_DROP))
+//IF_E2LABEL(APPLET_ODDNAME(e2label, tune2fs, BB_DIR_SBIN, BB_SUID_DROP, e2label))
+IF_ECHO(APPLET_NOFORK(echo, echo, BB_DIR_BIN, BB_SUID_DROP, echo))
+IF_EJECT(APPLET(eject, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_ENV(APPLET_NOEXEC(env, env, BB_DIR_USR_BIN, BB_SUID_DROP, env))
+IF_ENVDIR(APPLET_ODDNAME(envdir, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, envdir))
+IF_ENVUIDGID(APPLET_ODDNAME(envuidgid, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, envuidgid))
+IF_ETHER_WAKE(APPLET_ODDNAME(ether-wake, ether_wake, BB_DIR_USR_SBIN, BB_SUID_DROP, ether_wake))
+IF_EXPAND(APPLET(expand, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_EXPR(APPLET(expr, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_FAKEIDENTD(APPLET(fakeidentd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_FALSE(APPLET_NOFORK(false, false, BB_DIR_BIN, BB_SUID_DROP, false))
+IF_FBSET(APPLET(fbset, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_FBSPLASH(APPLET(fbsplash, BB_DIR_SBIN, BB_SUID_DROP))
+IF_FDFLUSH(APPLET_ODDNAME(fdflush, freeramdisk, BB_DIR_BIN, BB_SUID_DROP, fdflush))
+IF_FDFORMAT(APPLET(fdformat, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_FDISK(APPLET(fdisk, BB_DIR_SBIN, BB_SUID_DROP))
+IF_FGCONSOLE(APPLET(fgconsole, BB_DIR_USR_BIN, BB_SUID_DROP))
+/* Benefits from suid root: better access to /dev/BLOCKDEVs: */
+IF_FINDFS(APPLET(findfs, BB_DIR_SBIN, BB_SUID_MAYBE))
+IF_FLASH_ERASEALL(APPLET(flash_eraseall, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_FLASH_LOCK(APPLET_ODDNAME(flash_lock, flash_lock_unlock, BB_DIR_USR_SBIN, BB_SUID_DROP, flash_lock))
+IF_FLASH_UNLOCK(APPLET_ODDNAME(flash_unlock, flash_lock_unlock, BB_DIR_USR_SBIN, BB_SUID_DROP, flash_unlock))
+IF_FLASHCP(APPLET(flashcp, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_FLOCK(APPLET(flock, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_FOLD(APPLET_NOEXEC(fold, fold, BB_DIR_USR_BIN, BB_SUID_DROP, fold))
+IF_FREE(APPLET(free, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_FREERAMDISK(APPLET(freeramdisk, BB_DIR_SBIN, BB_SUID_DROP))
+IF_FSCK(APPLET(fsck, BB_DIR_SBIN, BB_SUID_DROP))
+//IF_E2FSCK(APPLET_ODDNAME(fsck.ext2, e2fsck, BB_DIR_SBIN, BB_SUID_DROP, fsck_ext2))
+//IF_E2FSCK(APPLET_ODDNAME(fsck.ext3, e2fsck, BB_DIR_SBIN, BB_SUID_DROP, fsck_ext3))
+IF_FSCK_MINIX(APPLET_ODDNAME(fsck.minix, fsck_minix, BB_DIR_SBIN, BB_SUID_DROP, fsck_minix))
+IF_FSYNC(APPLET_NOFORK(fsync, fsync, BB_DIR_BIN, BB_SUID_DROP, fsync))
+IF_FTPD(APPLET(ftpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_FTPGET(APPLET_ODDNAME(ftpget, ftpgetput, BB_DIR_USR_BIN, BB_SUID_DROP, ftpget))
+IF_FTPPUT(APPLET_ODDNAME(ftpput, ftpgetput, BB_DIR_USR_BIN, BB_SUID_DROP, ftpput))
+IF_FUSER(APPLET(fuser, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_GETENFORCE(APPLET(getenforce, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_GETOPT(APPLET(getopt, BB_DIR_BIN, BB_SUID_DROP))
+IF_GETSEBOOL(APPLET(getsebool, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_GETTY(APPLET(getty, BB_DIR_SBIN, BB_SUID_DROP))
+IF_HD(APPLET_NOEXEC(hd, hexdump, BB_DIR_USR_BIN, BB_SUID_DROP, hd))
+IF_HDPARM(APPLET(hdparm, BB_DIR_SBIN, BB_SUID_DROP))
+IF_HEAD(APPLET_NOEXEC(head, head, BB_DIR_USR_BIN, BB_SUID_DROP, head))
+IF_HEXDUMP(APPLET_NOEXEC(hexdump, hexdump, BB_DIR_USR_BIN, BB_SUID_DROP, hexdump))
+IF_HOSTNAME(APPLET(hostname, BB_DIR_BIN, BB_SUID_DROP))
+IF_HTTPD(APPLET(httpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_HWCLOCK(APPLET(hwclock, BB_DIR_SBIN, BB_SUID_DROP))
+IF_IFCONFIG(APPLET(ifconfig, BB_DIR_SBIN, BB_SUID_DROP))
+IF_IFUPDOWN(APPLET_ODDNAME(ifdown, ifupdown, BB_DIR_SBIN, BB_SUID_DROP, ifdown))
+IF_IFENSLAVE(APPLET(ifenslave, BB_DIR_SBIN, BB_SUID_DROP))
+IF_IFPLUGD(APPLET(ifplugd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_IFUPDOWN(APPLET_ODDNAME(ifup, ifupdown, BB_DIR_SBIN, BB_SUID_DROP, ifup))
+IF_INETD(APPLET(inetd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_INOTIFYD(APPLET(inotifyd, BB_DIR_SBIN, BB_SUID_DROP))
+IF_INSTALL(APPLET(install, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_IONICE(APPLET(ionice, BB_DIR_BIN, BB_SUID_DROP))
 #if ENABLE_FEATURE_IP_ADDRESS \
  || ENABLE_FEATURE_IP_ROUTE \
  || ENABLE_FEATURE_IP_LINK \
  || ENABLE_FEATURE_IP_TUNNEL \
  || ENABLE_FEATURE_IP_RULE
-IF_IP(APPLET(ip, _BB_DIR_BIN, _BB_SUID_DROP))
+IF_IP(APPLET(ip, BB_DIR_SBIN, BB_SUID_DROP))
 #endif
-IF_IPADDR(APPLET(ipaddr, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_IPCALC(APPLET(ipcalc, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_IPCRM(APPLET(ipcrm, _BB_DIR_USR_BIN, _BB_SUID_REQUIRE))
-IF_IPCS(APPLET(ipcs, _BB_DIR_USR_BIN, _BB_SUID_REQUIRE))
-IF_IPLINK(APPLET(iplink, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_IPROUTE(APPLET(iproute, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_IPRULE(APPLET(iprule, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_IPTUNNEL(APPLET(iptunnel, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_KBD_MODE(APPLET(kbd_mode, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_KILL(APPLET(kill, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_KILLALL(APPLET_ODDNAME(killall, kill, _BB_DIR_USR_BIN, _BB_SUID_DROP, killall))
-IF_KILLALL5(APPLET_ODDNAME(killall5, kill, _BB_DIR_USR_BIN, _BB_SUID_DROP, killall5))
-IF_KLOGD(APPLET(klogd, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_LASH(APPLET(lash, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_LAST(APPLET(last, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_LENGTH(APPLET_NOFORK(length, length, _BB_DIR_USR_BIN, _BB_SUID_DROP, length))
-IF_LESS(APPLET(less, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_SETARCH(APPLET_ODDNAME(linux32, setarch, _BB_DIR_BIN, _BB_SUID_DROP, linux32))
-IF_SETARCH(APPLET_ODDNAME(linux64, setarch, _BB_DIR_BIN, _BB_SUID_DROP, linux64))
-IF_FEATURE_INITRD(APPLET_ODDNAME(linuxrc, init, _BB_DIR_ROOT, _BB_SUID_DROP, linuxrc))
-IF_LN(APPLET_NOEXEC(ln, ln, _BB_DIR_BIN, _BB_SUID_DROP, ln))
-IF_LOAD_POLICY(APPLET(load_policy, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_LOADFONT(APPLET(loadfont, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_LOADKMAP(APPLET(loadkmap, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_LOGGER(APPLET(logger, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_LOGIN(APPLET(login, _BB_DIR_BIN, _BB_SUID_REQUIRE))
-IF_LOGNAME(APPLET_NOFORK(logname, logname, _BB_DIR_USR_BIN, _BB_SUID_DROP, logname))
-IF_LOGREAD(APPLET(logread, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_LOSETUP(APPLET(losetup, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_LPD(APPLET(lpd, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_LPQ(APPLET_ODDNAME(lpq, lpqr, _BB_DIR_USR_BIN, _BB_SUID_DROP, lpq))
-IF_LPR(APPLET_ODDNAME(lpr, lpqr, _BB_DIR_USR_BIN, _BB_SUID_DROP, lpr))
-IF_LS(APPLET_NOEXEC(ls, ls, _BB_DIR_BIN, _BB_SUID_DROP, ls))
-IF_LSATTR(APPLET(lsattr, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_LSMOD(APPLET(lsmod, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_MODPROBE_SMALL(APPLET_ODDNAME(lsmod, modprobe, _BB_DIR_SBIN, _BB_SUID_DROP, modprobe))
-IF_LSPCI(APPLET(lspci, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_LSUSB(APPLET(lsusb, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_UNLZMA(APPLET_ODDNAME(lzcat, unlzma, _BB_DIR_USR_BIN, _BB_SUID_DROP, lzcat))
-IF_LZMA(APPLET_ODDNAME(lzma, unlzma, _BB_DIR_USR_BIN, _BB_SUID_DROP, lzma))
-IF_LZOP(APPLET(lzop, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_LZOP(APPLET_ODDNAME(lzopcat, lzop, _BB_DIR_USR_BIN, _BB_SUID_DROP, lzopcat))
-IF_MAKEDEVS(APPLET(makedevs, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_MAKEMIME(APPLET(makemime, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_MAN(APPLET(man, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_MATCHPATHCON(APPLET(matchpathcon, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_MD5SUM(APPLET_ODDNAME(md5sum, md5_sha1_sum, _BB_DIR_USR_BIN, _BB_SUID_DROP, md5sum))
-IF_MDEV(APPLET(mdev, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_MESG(APPLET(mesg, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_MICROCOM(APPLET(microcom, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, _BB_DIR_BIN, _BB_SUID_DROP, mkdir))
-IF_MKFS_VFAT(APPLET_ODDNAME(mkdosfs, mkfs_vfat, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_vfat))
-IF_MKFS_EXT2(APPLET_ODDNAME(mke2fs, mkfs_ext2, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_ext2))
-IF_MKFIFO(APPLET(mkfifo, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_MKFS_EXT2(APPLET_ODDNAME(mkfs.ext2, mkfs_ext2, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_ext2))
-//IF_MKE2FS(APPLET_ODDNAME(mkfs.ext3, mke2fs, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_ext3))
-IF_MKFS_MINIX(APPLET_ODDNAME(mkfs.minix, mkfs_minix, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_minix))
-IF_MKFS_REISER(APPLET_ODDNAME(mkfs.reiser, mkfs_reiser, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_reiser))
-IF_MKFS_VFAT(APPLET_ODDNAME(mkfs.vfat, mkfs_vfat, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_vfat))
-IF_MKNOD(APPLET(mknod, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_CRYPTPW(APPLET_ODDNAME(mkpasswd, cryptpw, _BB_DIR_USR_BIN, _BB_SUID_DROP, mkpasswd))
-IF_MKSWAP(APPLET(mkswap, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_MKTEMP(APPLET(mktemp, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_MODPROBE(APPLET(modprobe, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_MODPROBE_SMALL(APPLET(modprobe, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_MORE(APPLET(more, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_MOUNT(APPLET(mount, _BB_DIR_BIN, IF_DESKTOP(_BB_SUID_MAYBE) IF_NOT_DESKTOP(_BB_SUID_DROP)))
-IF_MOUNTPOINT(APPLET(mountpoint, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_MSH(APPLET(msh, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_MT(APPLET(mt, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_MV(APPLET(mv, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_NAMEIF(APPLET(nameif, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_NC(APPLET(nc, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_NETSTAT(APPLET(netstat, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_NICE(APPLET(nice, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_NMETER(APPLET(nmeter, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_NOHUP(APPLET(nohup, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_NSLOOKUP(APPLET(nslookup, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_NTPD(APPLET(ntpd, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_OD(APPLET(od, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_OPENVT(APPLET(openvt, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-//IF_PARSE(APPLET(parse, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_PASSWD(APPLET(passwd, _BB_DIR_USR_BIN, _BB_SUID_REQUIRE))
-IF_PATCH(APPLET(patch, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_PGREP(APPLET(pgrep, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_PIDOF(APPLET(pidof, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_PING(APPLET(ping, _BB_DIR_BIN, _BB_SUID_MAYBE))
-IF_PING6(APPLET(ping6, _BB_DIR_BIN, _BB_SUID_MAYBE))
-IF_PIPE_PROGRESS(APPLET(pipe_progress, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_PIVOT_ROOT(APPLET(pivot_root, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_PKILL(APPLET_ODDNAME(pkill, pgrep, _BB_DIR_USR_BIN, _BB_SUID_DROP, pkill))
-IF_POPMAILDIR(APPLET(popmaildir, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_HALT(APPLET_ODDNAME(poweroff, halt, _BB_DIR_SBIN, _BB_SUID_DROP, poweroff))
-IF_PRINTENV(APPLET(printenv, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_PRINTF(APPLET_NOFORK(printf, printf, _BB_DIR_USR_BIN, _BB_SUID_DROP, printf))
-IF_PS(APPLET(ps, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_PSCAN(APPLET(pscan, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_PWD(APPLET_NOFORK(pwd, pwd, _BB_DIR_BIN, _BB_SUID_DROP, pwd))
-IF_RAIDAUTORUN(APPLET(raidautorun, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_RDATE(APPLET(rdate, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_RDEV(APPLET(rdev, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_READAHEAD(APPLET(readahead, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_READLINK(APPLET(readlink, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_READPROFILE(APPLET(readprofile, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_REALPATH(APPLET(realpath, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_HALT(APPLET_ODDNAME(reboot, halt, _BB_DIR_SBIN, _BB_SUID_DROP, reboot))
-IF_REFORMIME(APPLET(reformime, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_RENICE(APPLET(renice, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_RESET(APPLET(reset, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_RESIZE(APPLET(resize, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_RESTORECON(APPLET_ODDNAME(restorecon, setfiles, _BB_DIR_SBIN, _BB_SUID_DROP, restorecon))
-IF_RFKILL(APPLET(rfkill, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_RM(APPLET_NOFORK(rm, rm, _BB_DIR_BIN, _BB_SUID_DROP, rm))
-IF_RMDIR(APPLET_NOFORK(rmdir, rmdir, _BB_DIR_BIN, _BB_SUID_DROP, rmdir))
-IF_RMMOD(APPLET(rmmod, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_MODPROBE_SMALL(APPLET_ODDNAME(rmmod, modprobe, _BB_DIR_SBIN, _BB_SUID_DROP, modprobe))
-IF_ROUTE(APPLET(route, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_RPM(APPLET(rpm, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_RPM2CPIO(APPLET(rpm2cpio, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_RTCWAKE(APPLET(rtcwake, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_RUN_PARTS(APPLET_ODDNAME(run-parts, run_parts, _BB_DIR_BIN, _BB_SUID_DROP, run_parts))
-IF_RUNCON(APPLET(runcon, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_RUNLEVEL(APPLET(runlevel, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_RUNSV(APPLET(runsv, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_RUNSVDIR(APPLET(runsvdir, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_RX(APPLET(rx, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_SCRIPT(APPLET(script, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_SCRIPTREPLAY(APPLET(scriptreplay, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_SED(APPLET(sed, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_SELINUXENABLED(APPLET(selinuxenabled, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_SENDMAIL(APPLET(sendmail, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_SEQ(APPLET_NOFORK(seq, seq, _BB_DIR_USR_BIN, _BB_SUID_DROP, seq))
-IF_SESTATUS(APPLET(sestatus, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_SETARCH(APPLET(setarch, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_SETCONSOLE(APPLET(setconsole, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_SETENFORCE(APPLET(setenforce, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_SETFILES(APPLET(setfiles, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_SETFONT(APPLET(setfont, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_SETKEYCODES(APPLET(setkeycodes, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_SETLOGCONS(APPLET(setlogcons, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_SETSEBOOL(APPLET(setsebool, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_SETSID(APPLET(setsid, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_SETUIDGID(APPLET_ODDNAME(setuidgid, chpst, _BB_DIR_USR_BIN, _BB_SUID_DROP, setuidgid))
-IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, _BB_DIR_BIN, _BB_SUID_DROP, sh))
-IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, _BB_DIR_BIN, _BB_SUID_DROP, sh))
-IF_SHA1SUM(APPLET_ODDNAME(sha1sum, md5_sha1_sum, _BB_DIR_USR_BIN, _BB_SUID_DROP, sha1sum))
-IF_SHA256SUM(APPLET_ODDNAME(sha256sum, md5_sha1_sum, _BB_DIR_USR_BIN, _BB_SUID_DROP, sha256sum))
-IF_SHA512SUM(APPLET_ODDNAME(sha512sum, md5_sha1_sum, _BB_DIR_USR_BIN, _BB_SUID_DROP, sha512sum))
-IF_SHOWKEY(APPLET(showkey, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_SLATTACH(APPLET(slattach, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_SLEEP(APPLET_NOFORK(sleep, sleep, _BB_DIR_BIN, _BB_SUID_DROP, sleep))
-IF_SOFTLIMIT(APPLET_ODDNAME(softlimit, chpst, _BB_DIR_USR_BIN, _BB_SUID_DROP, softlimit))
-IF_SORT(APPLET_NOEXEC(sort, sort, _BB_DIR_USR_BIN, _BB_SUID_DROP, sort))
-IF_SPLIT(APPLET(split, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_START_STOP_DAEMON(APPLET_ODDNAME(start-stop-daemon, start_stop_daemon, _BB_DIR_SBIN, _BB_SUID_DROP, start_stop_daemon))
-IF_STAT(APPLET(stat, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_STRINGS(APPLET(strings, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_STTY(APPLET(stty, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_SU(APPLET(su, _BB_DIR_BIN, _BB_SUID_REQUIRE))
-IF_SULOGIN(APPLET(sulogin, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_SUM(APPLET(sum, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_SV(APPLET(sv, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_SVLOGD(APPLET(svlogd, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_SWAPONOFF(APPLET_ODDNAME(swapoff, swap_on_off, _BB_DIR_SBIN, _BB_SUID_DROP,swapoff))
-IF_SWAPONOFF(APPLET_ODDNAME(swapon, swap_on_off, _BB_DIR_SBIN, _BB_SUID_DROP, swapon))
-IF_SWITCH_ROOT(APPLET(switch_root, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_SYNC(APPLET_NOFORK(sync, sync, _BB_DIR_BIN, _BB_SUID_DROP, sync))
-IF_BB_SYSCTL(APPLET(sysctl, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_SYSLOGD(APPLET(syslogd, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_TAC(APPLET_NOEXEC(tac, tac, _BB_DIR_USR_BIN, _BB_SUID_DROP, tac))
-IF_TAIL(APPLET(tail, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_TAR(APPLET(tar, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_TASKSET(APPLET(taskset, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-/* IF_TC(APPLET(tc, _BB_DIR_SBIN, _BB_SUID_DROP)) */
-IF_TCPSVD(APPLET_ODDNAME(tcpsvd, tcpudpsvd, _BB_DIR_USR_BIN, _BB_SUID_DROP, tcpsvd))
-IF_TEE(APPLET(tee, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_TELNET(APPLET(telnet, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_TELNETD(APPLET(telnetd, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_TEST(APPLET_NOFORK(test, test, _BB_DIR_USR_BIN, _BB_SUID_DROP, test))
+IF_IPADDR(APPLET(ipaddr, BB_DIR_SBIN, BB_SUID_DROP))
+IF_IPCALC(APPLET(ipcalc, BB_DIR_BIN, BB_SUID_DROP))
+IF_IPCRM(APPLET(ipcrm, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_IPCS(APPLET(ipcs, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_IPLINK(APPLET(iplink, BB_DIR_SBIN, BB_SUID_DROP))
+IF_IPROUTE(APPLET(iproute, BB_DIR_SBIN, BB_SUID_DROP))
+IF_IPRULE(APPLET(iprule, BB_DIR_SBIN, BB_SUID_DROP))
+IF_IPTUNNEL(APPLET(iptunnel, BB_DIR_SBIN, BB_SUID_DROP))
+IF_KBD_MODE(APPLET(kbd_mode, BB_DIR_BIN, BB_SUID_DROP))
+IF_KILL(APPLET(kill, BB_DIR_BIN, BB_SUID_DROP))
+IF_KILLALL(APPLET_ODDNAME(killall, kill, BB_DIR_USR_BIN, BB_SUID_DROP, killall))
+IF_KILLALL5(APPLET_ODDNAME(killall5, kill, BB_DIR_USR_SBIN, BB_SUID_DROP, killall5))
+IF_KLOGD(APPLET(klogd, BB_DIR_SBIN, BB_SUID_DROP))
+IF_LAST(APPLET(last, BB_DIR_USR_BIN, BB_SUID_DROP))
+//IF_LENGTH(APPLET_NOFORK(length, length, BB_DIR_USR_BIN, BB_SUID_DROP, length))
+IF_LESS(APPLET(less, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_SETARCH(APPLET_ODDNAME(linux32, setarch, BB_DIR_BIN, BB_SUID_DROP, linux32))
+IF_SETARCH(APPLET_ODDNAME(linux64, setarch, BB_DIR_BIN, BB_SUID_DROP, linux64))
+IF_LN(APPLET_NOEXEC(ln, ln, BB_DIR_BIN, BB_SUID_DROP, ln))
+IF_LOAD_POLICY(APPLET(load_policy, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_LOADFONT(APPLET(loadfont, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_LOADKMAP(APPLET(loadkmap, BB_DIR_SBIN, BB_SUID_DROP))
+IF_LOGGER(APPLET(logger, BB_DIR_USR_BIN, BB_SUID_DROP))
+/* Needs to be run by root or be suid root - needs to change uid and gid: */
+IF_LOGIN(APPLET(login, BB_DIR_BIN, BB_SUID_REQUIRE))
+IF_LOGNAME(APPLET_NOFORK(logname, logname, BB_DIR_USR_BIN, BB_SUID_DROP, logname))
+IF_LOGREAD(APPLET(logread, BB_DIR_SBIN, BB_SUID_DROP))
+IF_LOSETUP(APPLET(losetup, BB_DIR_SBIN, BB_SUID_DROP))
+IF_LPD(APPLET(lpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_LPQ(APPLET_ODDNAME(lpq, lpqr, BB_DIR_USR_BIN, BB_SUID_DROP, lpq))
+IF_LPR(APPLET_ODDNAME(lpr, lpqr, BB_DIR_USR_BIN, BB_SUID_DROP, lpr))
+IF_LS(APPLET_NOEXEC(ls, ls, BB_DIR_BIN, BB_SUID_DROP, ls))
+IF_LSATTR(APPLET(lsattr, BB_DIR_BIN, BB_SUID_DROP))
+IF_LSPCI(APPLET(lspci, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_LSUSB(APPLET(lsusb, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_MAKEDEVS(APPLET(makedevs, BB_DIR_SBIN, BB_SUID_DROP))
+IF_MAKEMIME(APPLET(makemime, BB_DIR_BIN, BB_SUID_DROP))
+IF_MAN(APPLET(man, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_MATCHPATHCON(APPLET(matchpathcon, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_MD5SUM(APPLET_NOEXEC(md5sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, md5sum))
+IF_MICROCOM(APPLET(microcom, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, BB_DIR_BIN, BB_SUID_DROP, mkdir))
+IF_MKFS_VFAT(APPLET_ODDNAME(mkdosfs, mkfs_vfat, BB_DIR_SBIN, BB_SUID_DROP, mkfs_vfat))
+IF_MKFS_EXT2(APPLET_ODDNAME(mke2fs, mkfs_ext2, BB_DIR_SBIN, BB_SUID_DROP, mkfs_ext2))
+IF_MKFIFO(APPLET_NOEXEC(mkfifo, mkfifo, BB_DIR_USR_BIN, BB_SUID_DROP, mkfifo))
+IF_MKFS_EXT2(APPLET_ODDNAME(mkfs.ext2, mkfs_ext2, BB_DIR_SBIN, BB_SUID_DROP, mkfs_ext2))
+//IF_MKE2FS(APPLET_ODDNAME(mkfs.ext3, mke2fs, BB_DIR_SBIN, BB_SUID_DROP, mkfs_ext3))
+IF_MKFS_MINIX(APPLET_ODDNAME(mkfs.minix, mkfs_minix, BB_DIR_SBIN, BB_SUID_DROP, mkfs_minix))
+IF_MKFS_REISER(APPLET_ODDNAME(mkfs.reiser, mkfs_reiser, BB_DIR_SBIN, BB_SUID_DROP, mkfs_reiser))
+IF_MKFS_VFAT(APPLET_ODDNAME(mkfs.vfat, mkfs_vfat, BB_DIR_SBIN, BB_SUID_DROP, mkfs_vfat))
+IF_MKNOD(APPLET_NOEXEC(mknod, mknod, BB_DIR_BIN, BB_SUID_DROP, mknod))
+IF_CRYPTPW(APPLET_ODDNAME(mkpasswd, cryptpw, BB_DIR_USR_BIN, BB_SUID_DROP, mkpasswd))
+IF_MKSWAP(APPLET(mkswap, BB_DIR_SBIN, BB_SUID_DROP))
+IF_MKTEMP(APPLET(mktemp, BB_DIR_BIN, BB_SUID_DROP))
+IF_MORE(APPLET(more, BB_DIR_BIN, BB_SUID_DROP))
+/* On full-blown systems, requires suid for user mounts.
+ * But it's not unthinkable to have it available in non-suid flavor on some systems,
+ * for viewing mount table.
+ * Therefore we use BB_SUID_MAYBE instead of BB_SUID_REQUIRE: */
+IF_MOUNT(APPLET(mount, BB_DIR_BIN, IF_DESKTOP(BB_SUID_MAYBE) IF_NOT_DESKTOP(BB_SUID_DROP)))
+IF_MOUNTPOINT(APPLET(mountpoint, BB_DIR_BIN, BB_SUID_DROP))
+IF_MT(APPLET(mt, BB_DIR_BIN, BB_SUID_DROP))
+IF_MV(APPLET(mv, BB_DIR_BIN, BB_SUID_DROP))
+IF_NAMEIF(APPLET(nameif, BB_DIR_SBIN, BB_SUID_DROP))
+IF_NC(APPLET(nc, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_NETSTAT(APPLET(netstat, BB_DIR_BIN, BB_SUID_DROP))
+IF_NICE(APPLET(nice, BB_DIR_BIN, BB_SUID_DROP))
+IF_NOHUP(APPLET(nohup, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_NSLOOKUP(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_NTPD(APPLET(ntpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_OD(APPLET(od, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_OPENVT(APPLET(openvt, BB_DIR_USR_BIN, BB_SUID_DROP))
+//IF_PARSE(APPLET(parse, BB_DIR_USR_BIN, BB_SUID_DROP))
+/* Needs to be run by root or be suid root - needs to change /etc/{passwd,shadow}: */
+IF_PASSWD(APPLET(passwd, BB_DIR_USR_BIN, BB_SUID_REQUIRE))
+IF_PGREP(APPLET(pgrep, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_PIDOF(APPLET(pidof, BB_DIR_BIN, BB_SUID_DROP))
+IF_PIPE_PROGRESS(APPLET(pipe_progress, BB_DIR_BIN, BB_SUID_DROP))
+IF_PIVOT_ROOT(APPLET(pivot_root, BB_DIR_SBIN, BB_SUID_DROP))
+IF_PKILL(APPLET_ODDNAME(pkill, pgrep, BB_DIR_USR_BIN, BB_SUID_DROP, pkill))
+IF_POPMAILDIR(APPLET(popmaildir, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_PRINTENV(APPLET_NOFORK(printenv, printenv, BB_DIR_BIN, BB_SUID_DROP, printenv))
+IF_PRINTF(APPLET_NOFORK(printf, printf, BB_DIR_USR_BIN, BB_SUID_DROP, printf))
+IF_PS(APPLET(ps, BB_DIR_BIN, BB_SUID_DROP))
+IF_PSCAN(APPLET(pscan, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_PWD(APPLET_NOFORK(pwd, pwd, BB_DIR_BIN, BB_SUID_DROP, pwd))
+IF_RAIDAUTORUN(APPLET(raidautorun, BB_DIR_SBIN, BB_SUID_DROP))
+IF_RDATE(APPLET(rdate, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_RDEV(APPLET(rdev, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_READAHEAD(APPLET(readahead, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_READLINK(APPLET(readlink, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_READPROFILE(APPLET(readprofile, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_REALPATH(APPLET(realpath, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_REFORMIME(APPLET(reformime, BB_DIR_BIN, BB_SUID_DROP))
+IF_RENICE(APPLET(renice, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_RESET(APPLET(reset, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_RESIZE(APPLET(resize, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_RESTORECON(APPLET_ODDNAME(restorecon, setfiles, BB_DIR_SBIN, BB_SUID_DROP, restorecon))
+IF_RM(APPLET_NOFORK(rm, rm, BB_DIR_BIN, BB_SUID_DROP, rm))
+IF_RMDIR(APPLET_NOFORK(rmdir, rmdir, BB_DIR_BIN, BB_SUID_DROP, rmdir))
+IF_ROUTE(APPLET(route, BB_DIR_SBIN, BB_SUID_DROP))
+IF_RTCWAKE(APPLET(rtcwake, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_RUN_PARTS(APPLET_ODDNAME(run-parts, run_parts, BB_DIR_BIN, BB_SUID_DROP, run_parts))
+IF_RUNCON(APPLET(runcon, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_RUNLEVEL(APPLET(runlevel, BB_DIR_SBIN, BB_SUID_DROP))
+IF_RUNSV(APPLET(runsv, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_RUNSVDIR(APPLET(runsvdir, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_RX(APPLET(rx, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_SCRIPT(APPLET(script, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_SCRIPTREPLAY(APPLET(scriptreplay, BB_DIR_BIN, BB_SUID_DROP))
+IF_SELINUXENABLED(APPLET(selinuxenabled, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_SENDMAIL(APPLET(sendmail, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_SEQ(APPLET_NOFORK(seq, seq, BB_DIR_USR_BIN, BB_SUID_DROP, seq))
+IF_SESTATUS(APPLET(sestatus, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_SETARCH(APPLET(setarch, BB_DIR_BIN, BB_SUID_DROP))
+IF_SETCONSOLE(APPLET(setconsole, BB_DIR_SBIN, BB_SUID_DROP))
+IF_SETENFORCE(APPLET(setenforce, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_SETFILES(APPLET(setfiles, BB_DIR_SBIN, BB_SUID_DROP))
+IF_SETFONT(APPLET(setfont, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_SETKEYCODES(APPLET(setkeycodes, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_SETLOGCONS(APPLET(setlogcons, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_SETSEBOOL(APPLET(setsebool, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_SETSID(APPLET(setsid, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_SETUIDGID(APPLET_ODDNAME(setuidgid, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, setuidgid))
+IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum))
+IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum))
+IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum))
+IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum))
+IF_SHOWKEY(APPLET(showkey, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_SLATTACH(APPLET(slattach, BB_DIR_SBIN, BB_SUID_DROP))
+/* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells: */
+IF_SLEEP(APPLET(sleep, BB_DIR_BIN, BB_SUID_DROP))
+IF_SOFTLIMIT(APPLET_ODDNAME(softlimit, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, softlimit))
+IF_SORT(APPLET_NOEXEC(sort, sort, BB_DIR_USR_BIN, BB_SUID_DROP, sort))
+IF_SPLIT(APPLET(split, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_START_STOP_DAEMON(APPLET_ODDNAME(start-stop-daemon, start_stop_daemon, BB_DIR_SBIN, BB_SUID_DROP, start_stop_daemon))
+IF_STAT(APPLET(stat, BB_DIR_BIN, BB_SUID_DROP))
+IF_STRINGS(APPLET(strings, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_STTY(APPLET(stty, BB_DIR_BIN, BB_SUID_DROP))
+/* Needs to be run by root or be suid root - needs to change uid and gid: */
+IF_SU(APPLET(su, BB_DIR_BIN, BB_SUID_REQUIRE))
+IF_SULOGIN(APPLET(sulogin, BB_DIR_SBIN, BB_SUID_DROP))
+IF_SUM(APPLET(sum, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_SV(APPLET(sv, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_SVLOGD(APPLET(svlogd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_SWAPONOFF(APPLET_ODDNAME(swapoff, swap_on_off, BB_DIR_SBIN, BB_SUID_DROP, swapoff))
+IF_SWAPONOFF(APPLET_ODDNAME(swapon, swap_on_off, BB_DIR_SBIN, BB_SUID_DROP, swapon))
+IF_SWITCH_ROOT(APPLET(switch_root, BB_DIR_SBIN, BB_SUID_DROP))
+IF_SYNC(APPLET_NOFORK(sync, sync, BB_DIR_BIN, BB_SUID_DROP, sync))
+IF_BB_SYSCTL(APPLET(sysctl, BB_DIR_SBIN, BB_SUID_DROP))
+IF_SYSLOGD(APPLET(syslogd, BB_DIR_SBIN, BB_SUID_DROP))
+IF_TAC(APPLET_NOEXEC(tac, tac, BB_DIR_USR_BIN, BB_SUID_DROP, tac))
+IF_TAIL(APPLET(tail, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_TASKSET(APPLET(taskset, BB_DIR_USR_BIN, BB_SUID_DROP))
+/* IF_TC(APPLET(tc, BB_DIR_SBIN, BB_SUID_DROP)) */
+IF_TCPSVD(APPLET_ODDNAME(tcpsvd, tcpudpsvd, BB_DIR_USR_BIN, BB_SUID_DROP, tcpsvd))
+IF_TEE(APPLET(tee, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_TELNET(APPLET(telnet, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_TELNETD(APPLET(telnetd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_TEST(APPLET_NOFORK(test, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
 #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
-IF_TFTP(APPLET(tftp, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_TFTPD(APPLET(tftpd, _BB_DIR_USR_BIN, _BB_SUID_DROP))
+IF_TFTP(APPLET(tftp, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_TFTPD(APPLET(tftpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
 #endif
-IF_TIME(APPLET(time, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_TIMEOUT(APPLET(timeout, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_TOP(APPLET(top, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_TOUCH(APPLET_NOFORK(touch, touch, _BB_DIR_BIN, _BB_SUID_DROP, touch))
-IF_TR(APPLET(tr, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_TRACEROUTE(APPLET(traceroute, _BB_DIR_USR_BIN, _BB_SUID_MAYBE))
-IF_TRACEROUTE6(APPLET(traceroute6, _BB_DIR_USR_BIN, _BB_SUID_MAYBE))
-IF_TRUE(APPLET_NOFORK(true, true, _BB_DIR_BIN, _BB_SUID_DROP, true))
-IF_TTY(APPLET(tty, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_TTYSIZE(APPLET(ttysize, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_TUNCTL(APPLET(tunctl, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_TUNE2FS(APPLET(tune2fs, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_UDHCPC(APPLET(udhcpc, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_UDHCPD(APPLET(udhcpd, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-IF_UDPSVD(APPLET_ODDNAME(udpsvd, tcpudpsvd, _BB_DIR_USR_BIN, _BB_SUID_DROP, udpsvd))
-IF_UMOUNT(APPLET(umount, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_UNAME(APPLET(uname, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_UNCOMPRESS(APPLET(uncompress, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_UNEXPAND(APPLET_ODDNAME(unexpand, expand, _BB_DIR_USR_BIN, _BB_SUID_DROP, unexpand))
-IF_UNIQ(APPLET(uniq, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_UNIX2DOS(APPLET_ODDNAME(unix2dos, dos2unix, _BB_DIR_USR_BIN, _BB_SUID_DROP, unix2dos))
-IF_UNXZ(APPLET(unxz, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_UNLZMA(APPLET(unlzma, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_LZOP(APPLET_ODDNAME(unlzop, lzop, _BB_DIR_USR_BIN, _BB_SUID_DROP, unlzop))
-IF_UNZIP(APPLET(unzip, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_UPTIME(APPLET(uptime, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_USLEEP(APPLET_NOFORK(usleep, usleep, _BB_DIR_BIN, _BB_SUID_DROP, usleep))
-IF_UUDECODE(APPLET(uudecode, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_UUENCODE(APPLET(uuencode, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_VCONFIG(APPLET(vconfig, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_VI(APPLET(vi, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_VLOCK(APPLET(vlock, _BB_DIR_USR_BIN, _BB_SUID_REQUIRE))
-IF_VOLNAME(APPLET(volname, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_WALL(APPLET(wall, _BB_DIR_USR_BIN, _BB_SUID_REQUIRE))
-IF_WATCH(APPLET(watch, _BB_DIR_BIN, _BB_SUID_DROP))
-IF_WATCHDOG(APPLET(watchdog, _BB_DIR_SBIN, _BB_SUID_DROP))
-IF_WC(APPLET(wc, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_WGET(APPLET(wget, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_WHICH(APPLET(which, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_WHO(APPLET(who, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-IF_WHOAMI(APPLET_NOFORK(whoami, whoami, _BB_DIR_USR_BIN, _BB_SUID_DROP, whoami))
-IF_UNXZ(APPLET_ODDNAME(xzcat, unxz, _BB_DIR_USR_BIN, _BB_SUID_DROP, xzcat))
-IF_XZ(APPLET_ODDNAME(xz, unxz, _BB_DIR_USR_BIN, _BB_SUID_DROP, xz))
-IF_YES(APPLET_NOFORK(yes, yes, _BB_DIR_USR_BIN, _BB_SUID_DROP, yes))
-IF_GUNZIP(APPLET_ODDNAME(zcat, gunzip, _BB_DIR_BIN, _BB_SUID_DROP, zcat))
-IF_ZCIP(APPLET(zcip, _BB_DIR_SBIN, _BB_SUID_DROP))
+IF_TIME(APPLET(time, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_TIMEOUT(APPLET(timeout, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_TOP(APPLET(top, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_TR(APPLET(tr, BB_DIR_USR_BIN, BB_SUID_DROP))
+/* Needs socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), therefore BB_SUID_MAYBE: */
+IF_TRACEROUTE(APPLET(traceroute, BB_DIR_USR_BIN, BB_SUID_MAYBE))
+IF_TRACEROUTE6(APPLET(traceroute6, BB_DIR_USR_BIN, BB_SUID_MAYBE))
+IF_TRUE(APPLET_NOFORK(true, true, BB_DIR_BIN, BB_SUID_DROP, true))
+IF_TTY(APPLET(tty, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_TTYSIZE(APPLET(ttysize, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_TUNCTL(APPLET(tunctl, BB_DIR_SBIN, BB_SUID_DROP))
+IF_TUNE2FS(APPLET(tune2fs, BB_DIR_SBIN, BB_SUID_DROP))
+IF_UDHCPC(APPLET(udhcpc, BB_DIR_SBIN, BB_SUID_DROP))
+IF_UDHCPD(APPLET(udhcpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+IF_UDPSVD(APPLET_ODDNAME(udpsvd, tcpudpsvd, BB_DIR_USR_BIN, BB_SUID_DROP, udpsvd))
+IF_UMOUNT(APPLET(umount, BB_DIR_BIN, BB_SUID_DROP))
+IF_UNAME(APPLET(uname, BB_DIR_BIN, BB_SUID_DROP))
+IF_UNEXPAND(APPLET_ODDNAME(unexpand, expand, BB_DIR_USR_BIN, BB_SUID_DROP, unexpand))
+IF_UNIQ(APPLET(uniq, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_UNIX2DOS(APPLET_NOEXEC(unix2dos, dos2unix, BB_DIR_USR_BIN, BB_SUID_DROP, unix2dos))
+IF_UPTIME(APPLET(uptime, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_USLEEP(APPLET_NOFORK(usleep, usleep, BB_DIR_BIN, BB_SUID_DROP, usleep))
+IF_UUDECODE(APPLET(uudecode, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_UUENCODE(APPLET(uuencode, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_VCONFIG(APPLET(vconfig, BB_DIR_SBIN, BB_SUID_DROP))
+/* Needs to be run by root or be suid root - needs to change uid and gid: */
+IF_VLOCK(APPLET(vlock, BB_DIR_USR_BIN, BB_SUID_REQUIRE))
+IF_VOLNAME(APPLET(volname, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_WATCH(APPLET(watch, BB_DIR_BIN, BB_SUID_DROP))
+IF_WATCHDOG(APPLET(watchdog, BB_DIR_SBIN, BB_SUID_DROP))
+IF_WC(APPLET(wc, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_WGET(APPLET(wget, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_WHICH(APPLET(which, BB_DIR_USR_BIN, BB_SUID_DROP))
+IF_WHOAMI(APPLET_NOFORK(whoami, whoami, BB_DIR_USR_BIN, BB_SUID_DROP, whoami))
+IF_YES(APPLET_NOFORK(yes, yes, BB_DIR_USR_BIN, BB_SUID_DROP, yes))
+IF_ZCIP(APPLET(zcip, BB_DIR_SBIN, BB_SUID_DROP))
 
-#if !defined(PROTOTYPES) && !defined(NAME_MAIN_CNAME) && !defined(MAKE_USAGE)
+#if !defined(PROTOTYPES) && !defined(NAME_MAIN_CNAME) && !defined(MAKE_USAGE) \
+       && !defined(MAKE_LINKS) && !defined(MAKE_SUID)
 };
 #endif
 
index 2667889..386fe04 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * busybox ar archive data structures
- * Licensed under the GPL v2 or later, see the file LICENSE in this source tree.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #ifndef AR_H
 #define AR_H
diff --git a/include/bb_archive.h b/include/bb_archive.h
new file mode 100644 (file)
index 0000000..b82cfd8
--- /dev/null
@@ -0,0 +1,248 @@
+/* vi: set sw=4 ts=4: */
+#ifndef UNARCHIVE_H
+#define UNARCHIVE_H 1
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+enum {
+#if BB_BIG_ENDIAN
+       COMPRESS_MAGIC = 0x1f9d,
+       GZIP_MAGIC  = 0x1f8b,
+       BZIP2_MAGIC = 256 * 'B' + 'Z',
+       /* .xz signature: 0xfd, '7', 'z', 'X', 'Z', 0x00 */
+       /* More info at: http://tukaani.org/xz/xz-file-format.txt */
+       XZ_MAGIC1   = 256 * 0xfd + '7',
+       XZ_MAGIC2   = 256 * (unsigned)(256 * (256 * 'z' + 'X') + 'Z') + 0,
+       /* Different form: 32 bits, then 16 bits: */
+       /* (unsigned) cast suppresses "integer overflow in expression" warning */
+       XZ_MAGIC1a  = 256 * (unsigned)(256 * (256 * 0xfd + '7') + 'z') + 'X',
+       XZ_MAGIC2a  = 256 * 'Z' + 0,
+#else
+       COMPRESS_MAGIC = 0x9d1f,
+       GZIP_MAGIC  = 0x8b1f,
+       BZIP2_MAGIC = 'B' + 'Z' * 256,
+       XZ_MAGIC1   = 0xfd + '7' * 256,
+       XZ_MAGIC2   = 'z' + ('X' + ('Z' + 0 * 256) * 256) * 256,
+       XZ_MAGIC1a  = 0xfd + ('7' + ('z' + 'X' * 256) * 256) * 256,
+       XZ_MAGIC2a  = 'Z' + 0 * 256,
+#endif
+};
+
+typedef struct file_header_t {
+       char *name;
+       char *link_target;
+#if ENABLE_FEATURE_TAR_UNAME_GNAME
+       char *tar__uname;
+       char *tar__gname;
+#endif
+       off_t size;
+       uid_t uid;
+       gid_t gid;
+       mode_t mode;
+       time_t mtime;
+       dev_t device;
+} file_header_t;
+
+struct hardlinks_t;
+
+typedef struct archive_handle_t {
+       /* Flags. 1st since it is most used member */
+       unsigned ah_flags;
+
+       /* The raw stream as read from disk or stdin */
+       int src_fd;
+
+       /* Define if the header and data component should be processed */
+       char FAST_FUNC (*filter)(struct archive_handle_t *);
+       /* List of files that have been accepted */
+       llist_t *accept;
+       /* List of files that have been rejected */
+       llist_t *reject;
+       /* List of files that have successfully been worked on */
+       llist_t *passed;
+
+       /* Currently processed file's header */
+       file_header_t *file_header;
+
+       /* Process the header component, e.g. tar -t */
+       void FAST_FUNC (*action_header)(const file_header_t *);
+
+       /* Process the data component, e.g. extract to filesystem */
+       void FAST_FUNC (*action_data)(struct archive_handle_t *);
+
+       /* Function that skips data */
+       void FAST_FUNC (*seek)(int fd, off_t amount);
+
+       /* Count processed bytes */
+       off_t offset;
+
+       /* Archiver specific. Can make it a union if it ever gets big */
+#define PAX_NEXT_FILE 0
+#define PAX_GLOBAL    1
+#if ENABLE_TAR || ENABLE_DPKG || ENABLE_DPKG_DEB
+       smallint tar__end;
+# if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+       char* tar__longname;
+       char* tar__linkname;
+# endif
+# if ENABLE_FEATURE_TAR_TO_COMMAND
+       char* tar__to_command;
+       const char* tar__to_command_shell;
+# endif
+# if ENABLE_FEATURE_TAR_SELINUX
+       char* tar__sctx[2];
+# endif
+#endif
+#if ENABLE_CPIO || ENABLE_RPM2CPIO || ENABLE_RPM
+       uoff_t cpio__blocks;
+       struct hardlinks_t *cpio__hardlinks_to_create;
+       struct hardlinks_t *cpio__created_hardlinks;
+#endif
+#if ENABLE_DPKG || ENABLE_DPKG_DEB
+       /* Temporary storage */
+       char *dpkg__buffer;
+       /* How to process any sub archive, e.g. get_header_tar_gz */
+       char FAST_FUNC (*dpkg__action_data_subarchive)(struct archive_handle_t *);
+       /* Contains the handle to a sub archive */
+       struct archive_handle_t *dpkg__sub_archive;
+#endif
+#if ENABLE_FEATURE_AR_CREATE
+       const char *ar__name;
+       struct archive_handle_t *ar__out;
+#endif
+} archive_handle_t;
+/* bits in ah_flags */
+#define ARCHIVE_RESTORE_DATE        (1 << 0)
+#define ARCHIVE_CREATE_LEADING_DIRS (1 << 1)
+#define ARCHIVE_UNLINK_OLD          (1 << 2)
+#define ARCHIVE_EXTRACT_QUIET       (1 << 3)
+#define ARCHIVE_EXTRACT_NEWER       (1 << 4)
+#define ARCHIVE_DONT_RESTORE_OWNER  (1 << 5)
+#define ARCHIVE_DONT_RESTORE_PERM   (1 << 6)
+#define ARCHIVE_NUMERIC_OWNER       (1 << 7)
+#define ARCHIVE_O_TRUNC             (1 << 8)
+#define ARCHIVE_REMEMBER_NAMES      (1 << 9)
+#if ENABLE_RPM
+#define ARCHIVE_REPLACE_VIA_RENAME  (1 << 10)
+#endif
+
+
+/* POSIX tar Header Block, from POSIX 1003.1-1990  */
+#define TAR_BLOCK_SIZE 512
+#define NAME_SIZE      100
+#define NAME_SIZE_STR "100"
+typedef struct tar_header_t {     /* byte offset */
+       char name[NAME_SIZE];     /*   0-99 */
+       char mode[8];             /* 100-107 */
+       char uid[8];              /* 108-115 */
+       char gid[8];              /* 116-123 */
+       char size[12];            /* 124-135 */
+       char mtime[12];           /* 136-147 */
+       char chksum[8];           /* 148-155 */
+       char typeflag;            /* 156-156 */
+       char linkname[NAME_SIZE]; /* 157-256 */
+       /* POSIX:   "ustar" NUL "00" */
+       /* GNU tar: "ustar  " NUL */
+       /* Normally it's defined as magic[6] followed by
+        * version[2], but we put them together to save code.
+        */
+       char magic[8];            /* 257-264 */
+       char uname[32];           /* 265-296 */
+       char gname[32];           /* 297-328 */
+       char devmajor[8];         /* 329-336 */
+       char devminor[8];         /* 337-344 */
+       char prefix[155];         /* 345-499 */
+       char padding[12];         /* 500-512 (pad to exactly TAR_BLOCK_SIZE) */
+} tar_header_t;
+struct BUG_tar_header {
+       char c[sizeof(tar_header_t) == TAR_BLOCK_SIZE ? 1 : -1];
+};
+
+
+
+archive_handle_t *init_handle(void) FAST_FUNC;
+
+char filter_accept_all(archive_handle_t *archive_handle) FAST_FUNC;
+char filter_accept_list(archive_handle_t *archive_handle) FAST_FUNC;
+char filter_accept_list_reassign(archive_handle_t *archive_handle) FAST_FUNC;
+char filter_accept_reject_list(archive_handle_t *archive_handle) FAST_FUNC;
+
+void unpack_ar_archive(archive_handle_t *ar_archive) FAST_FUNC;
+
+void data_skip(archive_handle_t *archive_handle) FAST_FUNC;
+void data_extract_all(archive_handle_t *archive_handle) FAST_FUNC;
+void data_extract_to_stdout(archive_handle_t *archive_handle) FAST_FUNC;
+void data_extract_to_command(archive_handle_t *archive_handle) FAST_FUNC;
+
+void header_skip(const file_header_t *file_header) FAST_FUNC;
+void header_list(const file_header_t *file_header) FAST_FUNC;
+void header_verbose_list(const file_header_t *file_header) FAST_FUNC;
+
+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_bz2(archive_handle_t *archive_handle) FAST_FUNC;
+char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC;
+
+void seek_by_jump(int fd, off_t amount) FAST_FUNC;
+void seek_by_read(int fd, off_t amount) FAST_FUNC;
+
+const char *strip_unsafe_prefix(const char *str) FAST_FUNC;
+
+void data_align(archive_handle_t *archive_handle, unsigned boundary) FAST_FUNC;
+const llist_t *find_list_entry(const llist_t *list, const char *filename) FAST_FUNC;
+const llist_t *find_list_entry2(const llist_t *list, const char *filename) FAST_FUNC;
+
+/* A bit of bunzip2 internals are exposed for compressed help support: */
+typedef struct bunzip_data bunzip_data;
+int start_bunzip(bunzip_data **bdp, int in_fd, const void *inbuf, int len) FAST_FUNC;
+/* NB: read_bunzip returns < 0 on error, or the number of *unfilled* bytes
+ * in outbuf. IOW: on EOF returns len ("all bytes are not filled"), not 0: */
+int read_bunzip(bunzip_data *bd, char *outbuf, int len) FAST_FUNC;
+void dealloc_bunzip(bunzip_data *bd) FAST_FUNC;
+
+/* Meaning and direction (input/output) of the fields are transformer-specific */
+typedef struct transformer_aux_data_t {
+       smallint check_signature; /* most often referenced member */
+       off_t    bytes_out;
+       off_t    bytes_in;  /* used in unzip code only: needs to know packed size */
+       uint32_t crc32;
+       time_t   mtime;     /* gunzip code may set this on exit */
+} transformer_aux_data_t;
+
+void init_transformer_aux_data(transformer_aux_data_t *aux) FAST_FUNC;
+int FAST_FUNC check_signature16(transformer_aux_data_t *aux, int src_fd, unsigned magic16) FAST_FUNC;
+
+IF_DESKTOP(long long) int inflate_unzip(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC;
+IF_DESKTOP(long long) int unpack_Z_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC;
+IF_DESKTOP(long long) int unpack_gz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC;
+IF_DESKTOP(long long) int unpack_bz2_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC;
+IF_DESKTOP(long long) int unpack_lzma_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC;
+IF_DESKTOP(long long) int unpack_xz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC;
+
+char* append_ext(char *filename, const char *expected_ext) FAST_FUNC;
+int bbunpack(char **argv,
+               IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(transformer_aux_data_t *aux),
+               char* FAST_FUNC (*make_new_name)(char *filename, const char *expected_ext),
+               const char *expected_ext
+) FAST_FUNC;
+
+void check_errors_in_children(int signo);
+#if BB_MMU
+void open_transformer(int fd,
+       int check_signature,
+       IF_DESKTOP(long long) int FAST_FUNC (*transformer)(transformer_aux_data_t *aux, int src_fd, int dst_fd)
+) FAST_FUNC;
+#define open_transformer_with_sig(fd, transformer, transform_prog) open_transformer((fd), 1, (transformer))
+#define open_transformer_with_no_sig(fd, transformer)              open_transformer((fd), 0, (transformer))
+#else
+void open_transformer(int fd, const char *transform_prog) FAST_FUNC;
+#define open_transformer_with_sig(fd, transformer, transform_prog) open_transformer((fd), (transform_prog))
+/* open_transformer_with_no_sig() does not exist on NOMMU */
+#endif
+
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
similarity index 87%
rename from e2fsprogs/e2fs_defs.h
rename to include/bb_e2fs_defs.h
index 379640e..3f5e3c4 100644 (file)
@@ -406,25 +406,43 @@ struct ext2_super_block {
         * Performance hints.  Directory preallocation should only
         * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on.
         */
-       uint8_t s_prealloc_blocks;      /* Nr of blocks to try to preallocate*/
-       uint8_t s_prealloc_dir_blocks;  /* Nr to preallocate for dirs */
+       uint8_t         s_prealloc_blocks;      /* Nr of blocks to try to preallocate*/
+       uint8_t         s_prealloc_dir_blocks;  /* Nr to preallocate for dirs */
        uint16_t        s_reserved_gdt_blocks;  /* Per group table for online growth */
        /*
         * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set.
         */
-       uint8_t         s_journal_uuid[16];     /* uuid of journal superblock */
-       uint32_t        s_journal_inum;         /* inode number of journal file */
+/*D0*/ uint8_t         s_journal_uuid[16];     /* uuid of journal superblock */
+/*E0*/ uint32_t        s_journal_inum;         /* inode number of journal file */
        uint32_t        s_journal_dev;          /* device number of journal file */
        uint32_t        s_last_orphan;          /* start of list of inodes to delete */
        uint32_t        s_hash_seed[4];         /* HTREE hash seed */
        uint8_t         s_def_hash_version;     /* Default hash version to use */
        uint8_t         s_jnl_backup_type;      /* Default type of journal backup */
        uint16_t        s_reserved_word_pad;
-       uint32_t        s_default_mount_opts;
+/*100*/        uint32_t        s_default_mount_opts;
        uint32_t        s_first_meta_bg;        /* First metablock group */
+       /* ext3 additions */
        uint32_t        s_mkfs_time;            /* When the filesystem was created */
        uint32_t        s_jnl_blocks[17];       /* Backup of the journal inode */
-       uint32_t        s_reserved[172];        /* Padding to the end of the block */
+       /* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */
+/*150*/        uint32_t        s_blocks_count_hi;      /* Blocks count */
+       uint32_t        s_r_blocks_count_hi;    /* Reserved blocks count */
+       uint32_t        s_free_blocks_count_hi; /* Free blocks count */
+       uint16_t        s_min_extra_isize;      /* All inodes have at least # bytes */
+       uint16_t        s_want_extra_isize;     /* New inodes should reserve # bytes */
+       uint32_t        s_flags;                /* Miscellaneous flags */
+       uint16_t        s_raid_stride;          /* RAID stride */
+       uint16_t        s_mmp_interval;         /* # seconds to wait in MMP checking */
+       uint64_t        s_mmp_block;            /* Block for multi-mount protection */
+       uint32_t        s_raid_stripe_width;    /* blocks on all data disks (N*stride)*/
+       uint8_t         s_log_groups_per_flex;  /* FLEX_BG group size */
+       uint8_t         s_reserved_char_pad2;
+       uint16_t        s_reserved_pad;
+       uint32_t        s_reserved[162];        /* Padding to the end of the block */
+};
+struct BUG_ext2_super_block {
+       char bug[sizeof(struct ext2_super_block) == 1024 ? 1 : -1];
 };
 
 /*
@@ -463,30 +481,53 @@ struct ext2_super_block {
 #define EXT2_HAS_INCOMPAT_FEATURE(sb,mask)                     \
        ( EXT2_SB(sb)->s_feature_incompat & (mask) )
 
+/* for s_feature_compat */
 #define EXT2_FEATURE_COMPAT_DIR_PREALLOC       0x0001
 #define EXT2_FEATURE_COMPAT_IMAGIC_INODES      0x0002
 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL                0x0004
 #define EXT2_FEATURE_COMPAT_EXT_ATTR           0x0008
-#define EXT2_FEATURE_COMPAT_RESIZE_INODE       0x0010
+#define EXT2_FEATURE_COMPAT_RESIZE_INO         0x0010
 #define EXT2_FEATURE_COMPAT_DIR_INDEX          0x0020
 
+/* for s_feature_ro_compat */
 #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER    0x0001
 #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE      0x0002
-/* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR    0x0004 not used */
+#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR       0x0004 /* not used */
+#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE       0x0008
+#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM                0x0010
+#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK       0x0020
+#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE     0x0040
 
+/* for s_feature_incompat */
 #define EXT2_FEATURE_INCOMPAT_COMPRESSION      0x0001
 #define EXT2_FEATURE_INCOMPAT_FILETYPE         0x0002
-#define EXT3_FEATURE_INCOMPAT_RECOVER          0x0004 /* Needs recovery */
-#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV      0x0008 /* Journal device */
+#define EXT3_FEATURE_INCOMPAT_RECOVER          0x0004
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV      0x0008
 #define EXT2_FEATURE_INCOMPAT_META_BG          0x0010
-#define EXT3_FEATURE_INCOMPAT_EXTENTS          0x0040
+#define EXT4_FEATURE_INCOMPAT_EXTENTS          0x0040
+#define EXT4_FEATURE_INCOMPAT_64BIT            0x0080
+#define EXT4_FEATURE_INCOMPAT_MMP              0x0100
+#define EXT4_FEATURE_INCOMPAT_FLEX_BG          0x0200
 
 
 #define EXT2_FEATURE_COMPAT_SUPP       0
-#define EXT2_FEATURE_INCOMPAT_SUPP     (EXT2_FEATURE_INCOMPAT_FILETYPE)
 #define EXT2_FEATURE_RO_COMPAT_SUPP    (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
                                         EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
                                         EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+#define EXT2_FEATURE_INCOMPAT_SUPP     (EXT2_FEATURE_INCOMPAT_FILETYPE| \
+                                        EXT2_FEATURE_INCOMPAT_META_BG)
+#define EXT2_FEATURE_INCOMPAT_UNSUPPORTED      (~EXT2_FEATURE_INCOMPAT_SUPP)
+#define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED     (~EXT2_FEATURE_RO_COMPAT_SUPP)
+
+#define EXT3_FEATURE_RO_COMPAT_SUPP    (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+                                        EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+                                        EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+#define EXT3_FEATURE_INCOMPAT_SUPP     (EXT2_FEATURE_INCOMPAT_FILETYPE| \
+                                        EXT3_FEATURE_INCOMPAT_RECOVER| \
+                                        EXT2_FEATURE_INCOMPAT_META_BG)
+#define EXT3_FEATURE_INCOMPAT_UNSUPPORTED      (~EXT3_FEATURE_INCOMPAT_SUPP)
+#define EXT3_FEATURE_RO_COMPAT_UNSUPPORTED     (~EXT3_FEATURE_RO_COMPAT_SUPP)
+
 
 /*
  * Default values for user and/or group using reserved blocks
index 48ce856..b1e31e5 100644 (file)
@@ -1,52 +1,39 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Busybox main internal header file
- *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #ifndef BUSYBOX_H
 #define BUSYBOX_H 1
 
 #include "libbb.h"
+/* BB_DIR_foo and BB_SUID_bar constants: */
+#include "applet_metadata.h"
 
 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 
-/* order matters: used as index into "install_dir[]" in appletlib.c */
-typedef enum bb_install_loc_t {
-       _BB_DIR_ROOT = 0,
-       _BB_DIR_BIN,
-       _BB_DIR_SBIN,
-       _BB_DIR_USR_BIN,
-       _BB_DIR_USR_SBIN
-} bb_install_loc_t;
-
-typedef enum bb_suid_t {
-       _BB_SUID_DROP = 0,
-       _BB_SUID_MAYBE,
-       _BB_SUID_REQUIRE
-} bb_suid_t;
-
-
 /* Defined in appletlib.c (by including generated applet_tables.h) */
 /* Keep in sync with applets/applet_tables.c! */
-extern const char applet_names[];
+extern const char applet_names[] ALIGN1;
 extern int (*const applet_main[])(int argc, char **argv);
 extern const uint16_t applet_nameofs[];
-extern const uint8_t applet_install_loc[];
+extern const uint8_t applet_install_loc[] ALIGN1;
 
 #if ENABLE_FEATURE_SUID || ENABLE_FEATURE_PREFER_APPLETS
-#define APPLET_NAME(i) (applet_names + (applet_nameofs[i] & 0x0fff))
+# define APPLET_NAME(i) (applet_names + (applet_nameofs[i] & 0x0fff))
 #else
-#define APPLET_NAME(i) (applet_names + applet_nameofs[i])
+# define APPLET_NAME(i) (applet_names + applet_nameofs[i])
 #endif
 
 #if ENABLE_FEATURE_PREFER_APPLETS
-#define APPLET_IS_NOFORK(i) (applet_nameofs[i] & (1 << 12))
-#define APPLET_IS_NOEXEC(i) (applet_nameofs[i] & (1 << 13))
+# define APPLET_IS_NOFORK(i) (applet_nameofs[i] & (1 << 12))
+# define APPLET_IS_NOEXEC(i) (applet_nameofs[i] & (1 << 13))
+#else
+# define APPLET_IS_NOFORK(i) 0
+# define APPLET_IS_NOEXEC(i) 0
 #endif
 
 #if ENABLE_FEATURE_SUID
-#define APPLET_SUID(i) ((applet_nameofs[i] >> 14) & 0x3)
+# define APPLET_SUID(i) ((applet_nameofs[i] >> 14) & 0x3)
 #endif
 
 #if ENABLE_FEATURE_INSTALLER
index 925270d..4c237ef 100644 (file)
@@ -45,7 +45,7 @@ typedef struct FS {                   /* format strings */
 typedef struct dumper_t {
        off_t dump_skip;                /* bytes to skip */
        int dump_length;                /* max bytes to read */
-       smallint dump_vflag; /*enum dump_vflag_t*/
+       smallint dump_vflag;            /*enum dump_vflag_t*/
        FS *fshead;
 } dumper_t;
 
index c26e5d1..a2ba6d0 100644 (file)
@@ -3,7 +3,7 @@
  * This header makes it easier to include kernel headers
  * which use u32 and such.
  *
- * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #ifndef FIX_U32_H
 #define FIX_U32_H 1
index 7a95f88..e5075e5 100644 (file)
@@ -18,7 +18,7 @@
    02111-1307 USA.
  */
 /*
- *     POSIX Standard: 9.2.1 Group Database Access     <grp.h>
+ * POSIX Standard: 9.2.1 Group Database Access <grp.h>
  */
 #ifndef BB_GRP_H
 #define BB_GRP_H 1
@@ -29,7 +29,7 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
  * We will use libc-defined structures, but will #define function names
  * so that function calls are directed to bb_internal_XXX replacements
  */
-
+#undef endgrent
 #define setgrent     bb_internal_setgrent
 #define endgrent     bb_internal_endgrent
 #define getgrent     bb_internal_getgrent
@@ -64,7 +64,7 @@ extern struct group *fgetgrent(FILE *__stream);
 
 /* Write the given entry onto the given stream.  */
 extern int putgrent(const struct group *__restrict __p,
-                    FILE *__restrict __f);
+               FILE *__restrict __f);
 #endif
 
 /* Search for an entry with a matching group ID.  */
@@ -82,32 +82,32 @@ extern struct group *getgrnam(const char *__name);
    POSIX people would choose.  */
 
 extern int getgrent_r(struct group *__restrict __resultbuf,
-                      char *__restrict __buffer, size_t __buflen,
-                      struct group **__restrict __result);
+               char *__restrict __buffer, size_t __buflen,
+               struct group **__restrict __result);
 
 /* Search for an entry with a matching group ID.  */
 extern int getgrgid_r(gid_t __gid, struct group *__restrict __resultbuf,
-                      char *__restrict __buffer, size_t __buflen,
-                      struct group **__restrict __result);
+               char *__restrict __buffer, size_t __buflen,
+               struct group **__restrict __result);
 
 /* Search for an entry with a matching group name.  */
 extern int getgrnam_r(const char *__restrict __name,
-                      struct group *__restrict __resultbuf,
-                      char *__restrict __buffer, size_t __buflen,
-                      struct group **__restrict __result);
+               struct group *__restrict __resultbuf,
+               char *__restrict __buffer, size_t __buflen,
+               struct group **__restrict __result);
 
 /* Read a group entry from STREAM.  This function is not standardized
    an probably never will.  */
 extern int fgetgrent_r(FILE *__restrict __stream,
-                       struct group *__restrict __resultbuf,
-                       char *__restrict __buffer, size_t __buflen,
-                       struct group **__restrict __result);
+               struct group *__restrict __resultbuf,
+               char *__restrict __buffer, size_t __buflen,
+               struct group **__restrict __result);
 
 /* Store at most *NGROUPS members of the group set for USER into
    *GROUPS.  Also include GROUP.  The actual number of groups found is
    returned in *NGROUPS.  Return -1 if the if *NGROUPS is too small.  */
 extern int getgrouplist(const char *__user, gid_t __group,
-                        gid_t *__groups, int *__ngroups);
+               gid_t *__groups, int *__ngroups);
 
 /* Initialize the group set for the current user
    by reading the group database and using all groups
index e2a8322..64167bb 100644 (file)
@@ -3,9 +3,9 @@
  * Busybox main internal header file
  *
  * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
- * Permission has been granted to redistribute this code under the GPL.
+ * Permission has been granted to redistribute this code under GPL.
  *
- * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #ifndef LIBBB_H
 #define LIBBB_H 1
 #include <netdb.h>
 #include <setjmp.h>
 #include <signal.h>
+#if defined __UCLIBC__ /* TODO: and glibc? */
+/* use inlined versions of these: */
+# define sigfillset(s)    __sigfillset(s)
+# define sigemptyset(s)   __sigemptyset(s)
+# define sigisemptyset(s) __sigisemptyset(s)
+#endif
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <string.h>
-#include <sys/poll.h>
+/* There are two incompatible basename's, let's not use them! */
+/* See the dirname/basename man page for details */
+#include <libgen.h> /* dirname,basename */
+#undef basename
+#define basename dont_use_basename
+#include <poll.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/types.h>
+#ifndef major
+# include <sys/sysmacros.h>
+#endif
 #include <sys/wait.h>
 #include <termios.h>
 #include <time.h>
-#include <unistd.h>
-/* Try to pull in PATH_MAX */
-#include <limits.h>
 #include <sys/param.h>
+#include <pwd.h>
+#include <grp.h>
+#if ENABLE_FEATURE_SHADOWPASSWDS
+# if !ENABLE_USE_BB_SHADOW
+/* If using busybox's shadow implementation, do not include the shadow.h
+ * header as the toolchain may not provide it at all.
+ */
+#  include <shadow.h>
+# endif
+#endif
+#if defined(ANDROID) || defined(__ANDROID__)
+# define endpwent() ((void)0)
+# define endgrent() ((void)0)
+#endif
 #ifdef HAVE_MNTENT_H
-#include <mntent.h>
+# include <mntent.h>
 #endif
 #ifdef HAVE_SYS_STATFS_H
-#include <sys/statfs.h>
+# include <sys/statfs.h>
 #endif
+/* Don't do this here:
+ * #include <sys/sysinfo.h>
+ * Some linux/ includes pull in conflicting definition
+ * of struct sysinfo (only in some toolchanins), which breaks build.
+ * Include sys/sysinfo.h only in those files which need it.
+ */
 #if ENABLE_SELINUX
-#include <selinux/selinux.h>
-#include <selinux/context.h>
-#include <selinux/flask.h>
-#include <selinux/av_permissions.h>
+# include <selinux/selinux.h>
+# include <selinux/context.h>
+# include <selinux/flask.h>
+# include <selinux/av_permissions.h>
+#endif
+#if ENABLE_FEATURE_UTMP
+# include <utmp.h>
 #endif
 #if ENABLE_LOCALE_SUPPORT
 # include <locale.h>
 #ifdef DMALLOC
 # include <dmalloc.h>
 #endif
-#include <pwd.h>
-#include <grp.h>
-#if ENABLE_FEATURE_SHADOWPASSWDS
-# if !ENABLE_USE_BB_SHADOW
-/* If using busybox's shadow implementation, do not include the shadow.h
- * header as the toolchain may not provide it at all.
- */
-#  include <shadow.h>
-# endif
+/* Just in case libc doesn't define some of these... */
+#ifndef _PATH_PASSWD
+#define _PATH_PASSWD  "/etc/passwd"
+#endif
+#ifndef _PATH_GROUP
+#define _PATH_GROUP   "/etc/group"
+#endif
+#ifndef _PATH_SHADOW
+#define _PATH_SHADOW  "/etc/shadow"
 #endif
-#if defined __FreeBSD__
+#ifndef _PATH_GSHADOW
+#define _PATH_GSHADOW "/etc/gshadow"
+#endif
+#if defined __FreeBSD__ || defined __OpenBSD__
 # include <netinet/in.h>
 # include <arpa/inet.h>
 #elif defined __APPLE__
    typedef unsigned socklen_t;
 # endif
 #endif
+#ifndef HAVE_CLEARENV
+# define clearenv() do { if (environ) environ[0] = NULL; } while (0)
+#endif
+#ifndef HAVE_FDATASYNC
+# define fdatasync fsync
+#endif
+#ifndef HAVE_XTABS
+# define XTABS TAB3
+#endif
 
 
 /* Some libc's forget to declare these, do it ourself */
@@ -96,31 +142,6 @@ int vdprintf(int d, const char *format, va_list ap);
 #endif
 /* klogctl is in libc's klog.h, but we cheat and not #include that */
 int klogctl(int type, char *b, int len);
-/* This is declared here rather than #including <libgen.h> in order to avoid
- * confusing the two versions of basename.  See the dirname/basename man page
- * for details. */
-#if !defined __FreeBSD__
-char *dirname(char *path);
-#endif
-/* Include our own copy of struct sysinfo to avoid binary compatibility
- * problems with Linux 2.4, which changed things.  Grumble, grumble. */
-struct sysinfo {
-       long uptime;                    /* Seconds since boot */
-       unsigned long loads[3];         /* 1, 5, and 15 minute load averages */
-       unsigned long totalram;         /* Total usable main memory size */
-       unsigned long freeram;          /* Available memory size */
-       unsigned long sharedram;        /* Amount of shared memory */
-       unsigned long bufferram;        /* Memory used by buffers */
-       unsigned long totalswap;        /* Total swap space size */
-       unsigned long freeswap;         /* swap space still available */
-       unsigned short procs;           /* Number of current processes */
-       unsigned short pad;                     /* Padding needed for m68k */
-       unsigned long totalhigh;        /* Total high memory size */
-       unsigned long freehigh;         /* Available high memory size */
-       unsigned int mem_unit;          /* Memory unit size in bytes */
-       char _f[20 - 2 * sizeof(long) - sizeof(int)]; /* Padding: libc5 uses this.. */
-};
-int sysinfo(struct sysinfo* info);
 #ifndef PATH_MAX
 # define PATH_MAX 256
 #endif
@@ -129,6 +150,30 @@ int sysinfo(struct sysinfo* info);
 #endif
 
 
+/* Busybox does not use threads, we can speed up stdio. */
+#ifdef HAVE_UNLOCKED_STDIO
+# undef  getc
+# define getc(stream) getc_unlocked(stream)
+# undef  getchar
+# define getchar() getchar_unlocked()
+# undef  putc
+# define putc(c, stream) putc_unlocked(c, stream)
+# undef  putchar
+# define putchar(c) putchar_unlocked(c)
+# undef  fgetc
+# define fgetc(stream) getc_unlocked(stream)
+# undef  fputc
+# define fputc(c, stream) putc_unlocked(c, stream)
+#endif
+/* Above functions are required by POSIX.1-2008, below ones are extensions */
+#ifdef HAVE_UNLOCKED_LINE_OPS
+# undef  fgets
+# define fgets(s, n, stream) fgets_unlocked(s, n, stream)
+# undef  fputs
+# define fputs(s, stream) fputs_unlocked(s, stream)
+#endif
+
+
 /* Make all declarations hidden (-fvisibility flag only affects definitions) */
 /* (don't include system headers after this until corresponding pop!) */
 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
@@ -170,7 +215,7 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 # if ULONG_MAX > 0xffffffff
 /* "long" is long enough on this system */
 typedef unsigned long uoff_t;
-#  define XATOOFF(a) xatoul_range(a, 0, LONG_MAX)
+#  define XATOOFF(a) xatoul_range((a), 0, LONG_MAX)
 /* usage: sz = BB_STRTOOFF(s, NULL, 10); if (errno || sz < 0) die(); */
 #  define BB_STRTOOFF bb_strtoul
 #  define STRTOOFF strtoul
@@ -179,7 +224,7 @@ typedef unsigned long uoff_t;
 # else
 /* "long" is too short, need "long long" */
 typedef unsigned long long uoff_t;
-#  define XATOOFF(a) xatoull_range(a, 0, LLONG_MAX)
+#  define XATOOFF(a) xatoull_range((a), 0, LLONG_MAX)
 #  define BB_STRTOOFF bb_strtoull
 #  define STRTOOFF strtoull
 #  define OFF_FMT "ll"
@@ -190,13 +235,13 @@ typedef unsigned long long uoff_t;
 /* While sizeof(off_t) == sizeof(int), off_t is typedef'ed to long anyway.
  * gcc will throw warnings on printf("%d", off_t). Crap... */
 typedef unsigned long uoff_t;
-#  define XATOOFF(a) xatoi_u(a)
+#  define XATOOFF(a) xatoi_positive(a)
 #  define BB_STRTOOFF bb_strtou
 #  define STRTOOFF strtol
 #  define OFF_FMT "l"
 # else
 typedef unsigned long uoff_t;
-#  define XATOOFF(a) xatoul_range(a, 0, LONG_MAX)
+#  define XATOOFF(a) xatoul_range((a), 0, LONG_MAX)
 #  define BB_STRTOOFF bb_strtoul
 #  define STRTOOFF strtol
 #  define OFF_FMT "l"
@@ -204,6 +249,12 @@ typedef unsigned long uoff_t;
 #endif
 /* scary. better ideas? (but do *test* them first!) */
 #define OFF_T_MAX  ((off_t)~((off_t)1 << (sizeof(off_t)*8-1)))
+/* Users report bionic to use 32-bit off_t even if LARGEFILE support is requested.
+ * We misdetected that. Don't let it build:
+ */
+struct BUG_off_t_size_is_misdetected {
+       char BUG_off_t_size_is_misdetected[sizeof(off_t) == sizeof(uoff_t) ? 1 : -1];
+};
 
 /* Some useful definitions */
 #undef FALSE
@@ -213,20 +264,13 @@ typedef unsigned long uoff_t;
 #undef SKIP
 #define SKIP   ((int) 2)
 
-/* for mtab.c */
-#define MTAB_GETMOUNTPT '1'
-#define MTAB_GETDEVICE  '2'
-
-#define BUF_SIZE        8192
-#define EXPAND_ALLOC    1024
-
 /* Macros for min/max.  */
 #ifndef MIN
-#define        MIN(a,b) (((a)<(b))?(a):(b))
+#define MIN(a,b) (((a)<(b))?(a):(b))
 #endif
 
 #ifndef MAX
-#define        MAX(a,b) (((a)>(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
 #endif
 
 /* buffer allocation schemes */
@@ -254,6 +298,11 @@ extern int *const bb_errno;
 #define errno (*bb_errno)
 #endif
 
+#if !(ULONG_MAX > 0xffffffff)
+/* Only 32-bit CPUs need this, 64-bit ones use inlined version */
+uint64_t bb_bswap_64(uint64_t x) FAST_FUNC;
+#endif
+
 unsigned long long monotonic_ns(void) FAST_FUNC;
 unsigned long long monotonic_us(void) FAST_FUNC;
 unsigned long long monotonic_ms(void) FAST_FUNC;
@@ -269,7 +318,7 @@ extern char *strrstr(const char *haystack, const char *needle) FAST_FUNC;
 
 //TODO: supply a pointer to char[11] buffer (avoid statics)?
 extern const char *bb_mode_string(mode_t mode) FAST_FUNC;
-extern int is_directory(const char *name, int followLinks, struct stat *statBuf) FAST_FUNC;
+extern int is_directory(const char *name, int followLinks) FAST_FUNC;
 enum { /* DO NOT CHANGE THESE VALUES!  cp.c, mv.c, install.c depend on them. */
        FILEUTILS_PRESERVE_STATUS = 1 << 0, /* -p */
        FILEUTILS_DEREFERENCE     = 1 << 1, /* !-d */
@@ -284,6 +333,7 @@ enum {      /* DO NOT CHANGE THESE VALUES!  cp.c, mv.c, install.c depend on them. */
        FILEUTILS_PRESERVE_SECURITY_CONTEXT = 1 << 9, /* -c */
        FILEUTILS_SET_SECURITY_CONTEXT = 1 << 10,
 #endif
+       FILEUTILS_IGNORE_CHMOD_ERR = 1 << 11,
 };
 #define FILEUTILS_CP_OPTSTR "pdRfilsLH" IF_SELINUX("c")
 extern int remove_file(const char *path, int flags) FAST_FUNC;
@@ -320,20 +370,27 @@ extern void bb_copyfd_exact_size(int fd1, int fd2, off_t size) FAST_FUNC;
 /* "short" copy can be detected by return value < size */
 /* this helper yells "short read!" if param is not -1 */
 extern void complain_copyfd_and_die(off_t sz) NORETURN FAST_FUNC;
+
 extern char bb_process_escape_sequence(const char **ptr) FAST_FUNC;
+char* strcpy_and_process_escape_sequences(char *dst, const char *src) FAST_FUNC;
 /* xxxx_strip version can modify its parameter:
  * "/"        -> "/"
  * "abc"      -> "abc"
  * "abc/def"  -> "def"
  * "abc/def/" -> "def" !!
  */
-extern char *bb_get_last_path_component_strip(char *path) FAST_FUNC;
+char *bb_get_last_path_component_strip(char *path) FAST_FUNC;
 /* "abc/def/" -> "" and it never modifies 'path' */
-extern char *bb_get_last_path_component_nostrip(const char *path) FAST_FUNC;
+char *bb_get_last_path_component_nostrip(const char *path) FAST_FUNC;
+/* Simpler version: does not special case "/" string */
+const char *bb_basename(const char *name) FAST_FUNC;
+/* NB: can violate const-ness (similarly to strchr) */
+char *last_char_is(const char *s, int c) FAST_FUNC;
+const char* endofname(const char *name) FAST_FUNC;
 
-int ndelay_on(int fd) FAST_FUNC;
-int ndelay_off(int fd) FAST_FUNC;
-int close_on_exec_on(int fd) FAST_FUNC;
+void ndelay_on(int fd) FAST_FUNC;
+void ndelay_off(int fd) FAST_FUNC;
+void close_on_exec_on(int fd) FAST_FUNC;
 void xdup2(int, int) FAST_FUNC;
 void xmove_fd(int, int) FAST_FUNC;
 
@@ -404,6 +461,8 @@ void record_signo(int signo); /* not FAST_FUNC! */
 
 void xsetgid(gid_t gid) FAST_FUNC;
 void xsetuid(uid_t uid) FAST_FUNC;
+void xsetegid(gid_t egid) FAST_FUNC;
+void xseteuid(uid_t euid) FAST_FUNC;
 void xchdir(const char *path) FAST_FUNC;
 void xchroot(const char *path) FAST_FUNC;
 void xsetenv(const char *key, const char *value) FAST_FUNC;
@@ -411,21 +470,25 @@ void bb_unsetenv(const char *key) FAST_FUNC;
 void bb_unsetenv_and_free(char *key) FAST_FUNC;
 void xunlink(const char *pathname) FAST_FUNC;
 void xstat(const char *pathname, struct stat *buf) FAST_FUNC;
+void xfstat(int fd, struct stat *buf, const char *errmsg) FAST_FUNC;
+int open3_or_warn(const char *pathname, int flags, int mode) FAST_FUNC;
+int open_or_warn(const char *pathname, int flags) FAST_FUNC;
+int xopen3(const char *pathname, int flags, int mode) FAST_FUNC;
 int xopen(const char *pathname, int flags) FAST_FUNC;
 int xopen_nonblocking(const char *pathname) FAST_FUNC;
-int xopen3(const char *pathname, int flags, int mode) FAST_FUNC;
-int open_or_warn(const char *pathname, int flags) FAST_FUNC;
-int open3_or_warn(const char *pathname, int flags, int mode) FAST_FUNC;
+int xopen_as_uid_gid(const char *pathname, int flags, uid_t u, gid_t g) FAST_FUNC;
 int open_or_warn_stdin(const char *pathname) FAST_FUNC;
+int xopen_stdin(const char *pathname) FAST_FUNC;
 void xrename(const char *oldpath, const char *newpath) FAST_FUNC;
 int rename_or_warn(const char *oldpath, const char *newpath) FAST_FUNC;
 off_t xlseek(int fd, off_t offset, int whence) FAST_FUNC;
+int xmkstemp(char *template) FAST_FUNC;
 off_t fdlength(int fd) FAST_FUNC;
 
 uoff_t FAST_FUNC get_volume_size_in_bytes(int fd,
-                const char *override,
-                unsigned override_units,
-                int extend);
+               const char *override,
+               unsigned override_units,
+               int extend);
 
 void xpipe(int filedes[2]) FAST_FUNC;
 /* In this form code with pipes is much more readable */
@@ -463,7 +526,8 @@ struct BUG_too_small {
 
 void parse_datestr(const char *date_str, struct tm *ptm) FAST_FUNC;
 time_t validate_tm_time(const char *date_str, struct tm *ptm) FAST_FUNC;
-
+char *strftime_HHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC;
+char *strftime_YYYYMMDDHHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC;
 
 int xsocket(int domain, int type, int protocol) FAST_FUNC;
 void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) FAST_FUNC;
@@ -510,12 +574,7 @@ enum {
  * and if kernel doesn't support it, fall back to IPv4.
  * This is useful if you plan to bind to resulting local lsa.
  */
-#if ENABLE_FEATURE_IPV6
 int xsocket_type(len_and_sockaddr **lsap, int af, int sock_type) FAST_FUNC;
-#else
-int xsocket_type(len_and_sockaddr **lsap, int sock_type) FAST_FUNC;
-#define xsocket_type(lsap, af, sock_type) xsocket_type((lsap), (sock_type))
-#endif
 int xsocket_stream(len_and_sockaddr **lsap) FAST_FUNC;
 /* Create server socket bound to bindaddr:port. bindaddr can be NULL,
  * numeric IP ("N.N.N.N") or numeric IPv6 address,
@@ -555,7 +614,7 @@ len_and_sockaddr* xhost_and_af2sockaddr(const char *host, int port, sa_family_t
 /* Assign sin[6]_port member if the socket is an AF_INET[6] one,
  * otherwise no-op. Useful for ftp.
  * NB: does NOT do htons() internally, just direct assignment. */
-void set_nport(len_and_sockaddr *lsa, unsigned port) FAST_FUNC;
+void set_nport(struct sockaddr *sa, unsigned port) FAST_FUNC;
 /* Retrieve sin[6]_port or return -1 for non-INET[6] lsa's */
 int get_nport(const struct sockaddr *sa) FAST_FUNC;
 /* Reverse DNS. Returns NULL on failure. */
@@ -584,6 +643,7 @@ ssize_t recv_from_to(int fd, void *buf, size_t len, int flags,
                struct sockaddr *to,
                socklen_t sa_size) FAST_FUNC;
 
+uint16_t inet_cksum(uint16_t *addr, int len) FAST_FUNC;
 
 char *xstrdup(const char *s) FAST_FUNC RETURNS_MALLOC;
 char *xstrndup(const char *s, int n) FAST_FUNC RETURNS_MALLOC;
@@ -621,6 +681,13 @@ const char* FAST_FUNC printable_string(uni_stat_t *stats, const char *str);
  * else it is printed as-is (except for ch = 0x9b) */
 enum { PRINTABLE_META = 0x100 };
 void fputc_printable(int ch, FILE *file) FAST_FUNC;
+/* Return a string that is the printable representation of character ch.
+ * Buffer must hold at least four characters. */
+enum {
+       VISIBLE_ENDLINE   = 1 << 0,
+       VISIBLE_SHOW_TABS = 1 << 1,
+};
+void visible(unsigned ch, char *buf, int flags) FAST_FUNC;
 
 /* dmalloc will redefine these to it's own implementation. It is safe
  * to have the prototypes here unconditionally.  */
@@ -628,17 +695,20 @@ void *malloc_or_warn(size_t size) FAST_FUNC RETURNS_MALLOC;
 void *xmalloc(size_t size) FAST_FUNC RETURNS_MALLOC;
 void *xzalloc(size_t size) FAST_FUNC RETURNS_MALLOC;
 void *xrealloc(void *old, size_t size) FAST_FUNC;
-/* After xrealloc_vector(v, 4, idx) it's ok to use
+/* After v = xrealloc_vector(v, SHIFT, idx) it's ok to use
  * at least v[idx] and v[idx+1], for all idx values.
- * shift specifies how many new elements are added (1: 2, 2: 4... 8: 256...)
- * when all elements are used up. New elements are zeroed out. */
+ * SHIFT specifies how many new elements are added (1:2, 2:4, ..., 8:256...)
+ * when all elements are used up. New elements are zeroed out.
+ * xrealloc_vector(v, SHIFT, idx) *MUST* be called with consecutive IDXs -
+ * skipping an index is a bad bug - it may miss a realloc!
+ */
 #define xrealloc_vector(vector, shift, idx) \
        xrealloc_vector_helper((vector), (sizeof((vector)[0]) << 8) + (shift), (idx))
 void* xrealloc_vector_helper(void *vector, unsigned sizeof_and_shift, int idx) FAST_FUNC;
 
 
 extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC;
-extern ssize_t nonblock_safe_read(int fd, void *buf, size_t count) FAST_FUNC;
+extern ssize_t nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR) FAST_FUNC;
 // NB: will return short read on error, not -1,
 // if some data was read before error occurred
 extern ssize_t full_read(int fd, void *buf, size_t count) FAST_FUNC;
@@ -649,25 +719,31 @@ extern ssize_t open_read_close(const char *filename, void *buf, size_t maxsz) FA
 // Reads one line a-la fgets (but doesn't save terminating '\n').
 // Reads byte-by-byte. Useful when it is important to not read ahead.
 // Bytes are appended to pfx (which must be malloced, or NULL).
-extern char *xmalloc_reads(int fd, char *pfx, size_t *maxsz_p) FAST_FUNC;
+extern char *xmalloc_reads(int fd, size_t *maxsz_p) FAST_FUNC;
 /* Reads block up to *maxsz_p (default: INT_MAX - 4095) */
 extern void *xmalloc_read(int fd, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC;
 /* Returns NULL if file can't be opened (default max size: INT_MAX - 4095) */
 extern void *xmalloc_open_read_close(const char *filename, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC;
-/* Autodetects gzip/bzip2 formats. fd may be in the middle of the file! */
-#if ENABLE_FEATURE_SEAMLESS_LZMA \
+/* Never returns NULL */
+extern void *xmalloc_xopen_read_close(const char *filename, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC;
+
+#define SEAMLESS_COMPRESSION (0 \
+ || ENABLE_FEATURE_SEAMLESS_XZ \
+ || ENABLE_FEATURE_SEAMLESS_LZMA \
  || ENABLE_FEATURE_SEAMLESS_BZ2 \
  || ENABLE_FEATURE_SEAMLESS_GZ \
- /* || ENABLE_FEATURE_SEAMLESS_Z */
-extern void setup_unzip_on_fd(int fd /*, int fail_if_not_detected*/) FAST_FUNC;
-#else
-# define setup_unzip_on_fd(...) ((void)0)
-#endif
+ || ENABLE_FEATURE_SEAMLESS_Z)
+
+#if SEAMLESS_COMPRESSION
+/* Autodetects gzip/bzip2 formats. fd may be in the middle of the file! */
+extern int setup_unzip_on_fd(int fd, int fail_if_not_detected) FAST_FUNC;
 /* Autodetects .gz etc */
 extern int open_zipped(const char *fname) FAST_FUNC;
+#else
+# define setup_unzip_on_fd(...) (0)
+# define open_zipped(fname)     open((fname), O_RDONLY);
+#endif
 extern void *xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC;
-/* Never returns NULL */
-extern void *xmalloc_xopen_read_close(const char *filename, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC;
 
 extern ssize_t safe_write(int fd, const void *buf, size_t count) FAST_FUNC;
 // NB: will return short write on error, not -1,
@@ -685,8 +761,12 @@ extern void xclose(int fd) FAST_FUNC;
 /* Reads and prints to stdout till eof, then closes FILE. Exits on error: */
 extern void xprint_and_close_file(FILE *file) FAST_FUNC;
 
+/* Reads a line from a text file, up to a newline or NUL byte, inclusive.
+ * Returns malloc'ed char*. If end is NULL '\n' isn't considered
+ * end of line. If end isn't NULL, length of the chunk is stored in it.
+ * Returns NULL if EOF/error.
+ */
 extern char *bb_get_chunk_from_file(FILE *file, int *end) FAST_FUNC;
-extern char *bb_get_chunk_with_continuation(FILE *file, int *end, int *lineno) FAST_FUNC;
 /* Reads up to (and including) TERMINATING_STRING: */
 extern char *xmalloc_fgets_str(FILE *file, const char *terminating_string) FAST_FUNC RETURNS_MALLOC;
 /* Same, with limited max size, and returns the length (excluding NUL): */
@@ -728,7 +808,6 @@ void qsort_string_vector(char **sv, unsigned count) FAST_FUNC;
 int safe_poll(struct pollfd *ufds, nfds_t nfds, int timeout_ms) FAST_FUNC;
 
 char *safe_gethostname(void) FAST_FUNC;
-char *safe_getdomainname(void) FAST_FUNC;
 
 /* Convert each alpha char in str to lower-case */
 char* str_tolower(char *str) FAST_FUNC;
@@ -739,8 +818,8 @@ char *itoa(int n) FAST_FUNC;
 char *utoa_to_buf(unsigned n, char *buf, unsigned buflen) FAST_FUNC;
 char *itoa_to_buf(int n, char *buf, unsigned buflen) FAST_FUNC;
 /* Intelligent formatters of bignums */
-void smart_ulltoa4(unsigned long long ul, char buf[5], const char *scale) FAST_FUNC;
-void smart_ulltoa5(unsigned long long ul, char buf[5], const char *scale) FAST_FUNC;
+char *smart_ulltoa4(unsigned long long ul, char buf[4], const char *scale) FAST_FUNC;
+char *smart_ulltoa5(unsigned long long ul, char buf[5], const char *scale) FAST_FUNC;
 /* If block_size == 0, display size without fractional part,
  * else display (size * block_size) with one decimal digit.
  * If display_unit == 0, show value no bigger than 1024 with suffix (K,M,G...),
@@ -751,9 +830,9 @@ void smart_ulltoa5(unsigned long long ul, char buf[5], const char *scale) FAST_F
 const char *make_human_readable_str(unsigned long long size,
                unsigned long block_size, unsigned long display_unit) FAST_FUNC;
 /* Put a string of hex bytes ("1b2e66fe"...), return advanced pointer */
-char *bin2hex(char *buf, const char *cp, int count) FAST_FUNC;
+char *bin2hex(char *dst, const char *src, int count) FAST_FUNC;
 /* Reverse */
-char* hex2bin(char *dst, const char *str, int count) FAST_FUNC;
+char* hex2bin(char *dst, const char *src, int count) FAST_FUNC;
 
 /* Generate a UUID */
 void generate_uuid(uint8_t *buf) FAST_FUNC;
@@ -763,13 +842,21 @@ struct suffix_mult {
        char suffix[4];
        unsigned mult;
 };
+extern const struct suffix_mult bkm_suffixes[];
+#define km_suffixes (bkm_suffixes + 1)
+
 #include "xatonum.h"
 /* Specialized: */
+
 /* Using xatoi() instead of naive atoi() is not always convenient -
  * in many places people want *non-negative* values, but store them
  * in signed int. Therefore we need this one:
- * dies if input is not in [0, INT_MAX] range. Also will reject '-0' etc */
-int xatoi_u(const char *numstr) FAST_FUNC;
+ * dies if input is not in [0, INT_MAX] range. Also will reject '-0' etc.
+ * It should really be named xatoi_nonnegative (since it allows 0),
+ * but that would be too long.
+ */
+int xatoi_positive(const char *numstr) FAST_FUNC;
+
 /* Useful for reading port numbers */
 uint16_t xatou16(const char *numstr) FAST_FUNC;
 
@@ -800,14 +887,14 @@ char* xuid2uname(uid_t uid) FAST_FUNC;
 char* xgid2group(gid_t gid) FAST_FUNC;
 char* uid2uname(uid_t uid) FAST_FUNC;
 char* gid2group(gid_t gid) FAST_FUNC;
-char* uid2uname_utoa(long uid) FAST_FUNC;
-char* gid2group_utoa(long gid) FAST_FUNC;
+char* uid2uname_utoa(uid_t uid) FAST_FUNC;
+char* gid2group_utoa(gid_t gid) FAST_FUNC;
 /* versions which cache results (useful for ps, ls etc) */
 const char* get_cached_username(uid_t uid) FAST_FUNC;
 const char* get_cached_groupname(gid_t gid) FAST_FUNC;
 void clear_username_cache(void) FAST_FUNC;
 /* internally usernames are saved in fixed-sized char[] buffers */
-enum { USERNAME_MAX_SIZE = 16 - sizeof(int) };
+enum { USERNAME_MAX_SIZE = 32 - sizeof(uid_t) };
 #if ENABLE_FEATURE_CHECK_NAMES
 void die_if_bad_username(const char* name) FAST_FUNC;
 #else
@@ -822,6 +909,7 @@ void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const
 # define update_utmp(pid, new_type, tty_name, username, hostname) ((void)0)
 #endif
 
+
 int execable_file(const char *name) FAST_FUNC;
 char *find_execable(const char *filename, char **PATHp) FAST_FUNC;
 int exists_execable(const char *filename) FAST_FUNC;
@@ -830,14 +918,16 @@ int exists_execable(const char *filename) FAST_FUNC;
  * but it may exec busybox and call applet instead of searching PATH.
  */
 #if ENABLE_FEATURE_PREFER_APPLETS
-int bb_execvp(const char *file, char *const argv[]) FAST_FUNC;
-#define BB_EXECVP(prog,cmd) bb_execvp(prog,cmd)
+int BB_EXECVP(const char *file, char *const argv[]) FAST_FUNC;
 #define BB_EXECLP(prog,cmd,...) \
-       execlp((find_applet_by_name(prog) >= 0) ? CONFIG_BUSYBOX_EXEC_PATH : prog, \
-               cmd, __VA_ARGS__)
+       do { \
+               if (find_applet_by_name(prog) >= 0) \
+                       execlp(bb_busybox_exec_path, cmd, __VA_ARGS__); \
+               execlp(prog, cmd, __VA_ARGS__); \
+       } while (0)
 #else
 #define BB_EXECVP(prog,cmd)     execvp(prog,cmd)
-#define BB_EXECLP(prog,cmd,...) execlp(prog,cmd, __VA_ARGS__)
+#define BB_EXECLP(prog,cmd,...) execlp(prog,cmd,__VA_ARGS__)
 #endif
 int BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC;
 
@@ -871,19 +961,8 @@ pid_t wait_any_nohang(int *wstat) FAST_FUNC;
 int wait4pid(pid_t pid) FAST_FUNC;
 /* Same as wait4pid(spawn(argv)), but with NOFORK/NOEXEC if configured: */
 int spawn_and_wait(char **argv) FAST_FUNC;
-struct nofork_save_area {
-       jmp_buf die_jmp;
-       const char *applet_name;
-       int xfunc_error_retval;
-       uint32_t option_mask32;
-       int die_sleep;
-       smallint saved;
-};
-void save_nofork_data(struct nofork_save_area *save) FAST_FUNC;
-void restore_nofork_data(struct nofork_save_area *save) FAST_FUNC;
 /* Does NOT check that applet is NOFORK, just blindly runs it */
 int run_nofork_applet(int applet_no, char **argv) FAST_FUNC;
-int run_nofork_applet_prime(struct nofork_save_area *old, int applet_no, char **argv) FAST_FUNC;
 
 /* Helpers for daemonization.
  *
@@ -913,6 +992,7 @@ enum {
        DAEMON_DEVNULL_STDIO = 2,
        DAEMON_CLOSE_EXTRA_FDS = 4,
        DAEMON_ONLY_SANITIZE = 8, /* internal use */
+       DAEMON_DOUBLE_FORK = 16, /* double fork to avoid controlling tty */
 };
 #if BB_MMU
   enum { re_execed = 0 };
@@ -921,6 +1001,9 @@ enum {
 # define bb_daemonize(flags)                bb_daemonize_or_rexec(flags, bogus)
 #else
   extern bool re_execed;
+  /* Note: re_exec() and fork_or_rexec() do argv[0][0] |= 0x80 on NOMMU!
+   * _Parent_ needs to undo it if it doesn't want to have argv[0] mangled.
+   */
   void re_exec(char **argv) NORETURN FAST_FUNC;
   pid_t fork_or_rexec(char **argv) FAST_FUNC;
   int  BUG_fork_is_unavailable_on_nommu(void) FAST_FUNC;
@@ -950,9 +1033,13 @@ extern uint32_t option_mask32;
 extern uint32_t getopt32(char **argv, const char *applet_opts, ...) FAST_FUNC;
 
 
+/* Having next pointer as a first member allows easy creation
+ * of "llist-compatible" structs, and using llist_FOO functions
+ * on them.
+ */
 typedef struct llist_t {
-       char *data;
        struct llist_t *link;
+       char *data;
 } llist_t;
 void llist_add_to(llist_t **old_head, void *data) FAST_FUNC;
 void llist_add_to_end(llist_t **list_head, void *data) FAST_FUNC;
@@ -987,7 +1074,7 @@ enum {
 extern const char *msg_eol;
 extern smallint logmode;
 extern int die_sleep;
-extern int xfunc_error_retval;
+extern uint8_t xfunc_error_retval;
 extern jmp_buf die_jmp;
 extern void xfunc_die(void) NORETURN FAST_FUNC;
 extern void bb_show_usage(void) NORETURN FAST_FUNC;
@@ -1035,9 +1122,6 @@ void bb_displayroutes(int noresolve, int netstatfmt) FAST_FUNC;
 
 
 /* Networking */
-int create_icmp_socket(void) FAST_FUNC;
-int create_icmp6_socket(void) FAST_FUNC;
-/* interface.c */
 /* This structure defines protocol families and their handlers. */
 struct aftype {
        const char *name;
@@ -1066,6 +1150,7 @@ struct hwtype {
 };
 extern smallint interface_opt_a;
 int display_interfaces(char *ifname) FAST_FUNC;
+int in_ether(const char *bufp, struct sockaddr *sap) FAST_FUNC;
 #if ENABLE_FEATURE_HWIB
 int in_ib(const char *bufp, struct sockaddr *sap) FAST_FUNC;
 #else
@@ -1103,7 +1188,7 @@ extern int del_loop(const char *device) FAST_FUNC;
 /* If *devname is not NULL, use that name, otherwise try to find free one,
  * malloc and return it in *devname.
  * return value: 1: read-only loopdev was setup, 0: rw, < 0: error */
-extern int set_loop(char **devname, const char *file, unsigned long long offset) FAST_FUNC;
+extern int set_loop(char **devname, const char *file, unsigned long long offset, int ro) FAST_FUNC;
 
 /* Like bb_ask below, but asks on stdin with no timeout.  */
 char *bb_ask_stdin(const char * prompt) FAST_FUNC;
@@ -1124,18 +1209,20 @@ enum {
        PARSE_MIN_DIE   = 0x00100000, // die if < min tokens found
        // keep a copy of current line
        PARSE_KEEP_COPY = 0x00200000 * ENABLE_FEATURE_CROND_D,
-//     PARSE_ESCAPE    = 0x00400000, // process escape sequences in tokens
+       PARSE_EOL_COMMENTS = 0x00400000, // comments are recognized even if they aren't the first char
        // NORMAL is:
        // * remove leading and trailing delimiters and collapse
        //   multiple delimiters into one
        // * warn and continue if less than mintokens delimiters found
        // * grab everything into last token
-       PARSE_NORMAL    = PARSE_COLLAPSE | PARSE_TRIM | PARSE_GREEDY,
+       // * comments are recognized even if they aren't the first char
+       PARSE_NORMAL    = PARSE_COLLAPSE | PARSE_TRIM | PARSE_GREEDY | PARSE_EOL_COMMENTS,
 };
 typedef struct parser_t {
        FILE *fp;
-       char *line;
        char *data;
+       char *line, *nline;
+       size_t line_alloc, nline_alloc;
        int lineno;
 } parser_t;
 parser_t* config_open(const char *filename) FAST_FUNC;
@@ -1151,10 +1238,8 @@ void config_close(parser_t *parser) FAST_FUNC;
  * If path is NULL, it is assumed to be "/".
  * filename should not be NULL. */
 char *concat_path_file(const char *path, const char *filename) FAST_FUNC;
+/* Returns NULL on . and .. */
 char *concat_subpath_file(const char *path, const char *filename) FAST_FUNC;
-const char *bb_basename(const char *name) FAST_FUNC;
-/* NB: can violate const-ness (similarly to strchr) */
-char *last_char_is(const char *s, int c) FAST_FUNC;
 
 
 int bb_make_directory(char *path, long mode, int flags) FAST_FUNC;
@@ -1167,10 +1252,17 @@ char *bb_simplify_path(const char *path) FAST_FUNC;
 /* Returns ptr to NUL */
 char *bb_simplify_abs_path_inplace(char *path) FAST_FUNC;
 
-#define FAIL_DELAY 3
+#define LOGIN_FAIL_DELAY 3
 extern void bb_do_delay(int seconds) FAST_FUNC;
 extern void change_identity(const struct passwd *pw) FAST_FUNC;
 extern void run_shell(const char *shell, int loginshell, const char *command, const char **additional_args) NORETURN FAST_FUNC;
+
+/* Returns $SHELL, getpwuid(getuid())->pw_shell, or DEFAULT_SHELL.
+ * Note that getpwuid result might need xstrdup'ing
+ * if there is a possibility of intervening getpwxxx() calls.
+ */
+const char *get_shell_name(void) FAST_FUNC;
+
 #if ENABLE_SELINUX
 extern void renew_current_security_context(void) FAST_FUNC;
 extern void set_current_security_context(security_context_t sid) FAST_FUNC;
@@ -1183,6 +1275,12 @@ extern void selinux_preserve_fcontext(int fdesc) FAST_FUNC;
 #endif
 extern void selinux_or_die(void) FAST_FUNC;
 
+
+/* systemd support */
+#define SD_LISTEN_FDS_START 3
+int sd_listen_fds(void);
+
+
 /* setup_environment:
  * if chdir pw->pw_dir: ok: else if to_tmp == 1: goto /tmp else: goto / or die
  * if clear_env = 1: cd(pw->pw_dir), clear environment, then set
@@ -1201,22 +1299,30 @@ extern void selinux_or_die(void) FAST_FUNC;
 #define SETUP_ENV_CHANGEENV (1 << 0)
 #define SETUP_ENV_CLEARENV  (1 << 1)
 #define SETUP_ENV_TO_TMP    (1 << 2)
-extern void setup_environment(const char *shell, int flags, const struct passwd *pw) FAST_FUNC;
-extern int correct_password(const struct passwd *pw) FAST_FUNC;
+#define SETUP_ENV_NO_CHDIR  (1 << 4)
+void setup_environment(const char *shell, int flags, const struct passwd *pw) FAST_FUNC;
+void nuke_str(char *str) FAST_FUNC;
+int ask_and_check_password_extended(const struct passwd *pw, int timeout, const char *prompt) FAST_FUNC;
+int ask_and_check_password(const struct passwd *pw) FAST_FUNC;
 /* Returns a malloced string */
 #if !ENABLE_USE_BB_CRYPT
 #define pw_encrypt(clear, salt, cleanup) pw_encrypt(clear, salt)
 #endif
 extern char *pw_encrypt(const char *clear, const char *salt, int cleanup) FAST_FUNC;
 extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC;
-/* rnd is additional random input. New one is returned.
+/*
+ * rnd is additional random input. New one is returned.
  * Useful if you call crypt_make_salt many times in a row:
  * rnd = crypt_make_salt(buf1, 4, 0);
  * rnd = crypt_make_salt(buf2, 4, rnd);
  * rnd = crypt_make_salt(buf3, 4, rnd);
  * (otherwise we risk having same salt generated)
  */
-extern int crypt_make_salt(char *p, int cnt, int rnd) FAST_FUNC;
+extern int crypt_make_salt(char *p, int cnt /*, int rnd*/) FAST_FUNC;
+/* "$N$" + sha_salt_16_bytes + NUL */
+#define MAX_PW_SALT_LEN (3 + 16 + 1)
+extern char* crypt_make_pw_salt(char p[MAX_PW_SALT_LEN], const char *algo) FAST_FUNC;
+
 
 /* Returns number of lines changed, or -1 on error */
 #if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP)
@@ -1263,7 +1369,7 @@ void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) FAST
 void reset_ino_dev_hashtable(void) FAST_FUNC;
 #ifdef __GLIBC__
 /* At least glibc has horrendously large inline for this, so wrap it */
-unsigned long long bb_makedev(unsigned int major, unsigned int minor) FAST_FUNC;
+unsigned long long bb_makedev(unsigned major, unsigned minor) FAST_FUNC;
 #undef makedev
 #define makedev(a,b) bb_makedev(a,b)
 #endif
@@ -1284,25 +1390,37 @@ enum {
        KEYCODE_DELETE   =  -9,
        KEYCODE_PAGEUP   = -10,
        KEYCODE_PAGEDOWN = -11,
-
-       KEYCODE_CTRL_UP    = KEYCODE_UP    & ~0x40,
-       KEYCODE_CTRL_DOWN  = KEYCODE_DOWN  & ~0x40,
-       KEYCODE_CTRL_RIGHT = KEYCODE_RIGHT & ~0x40,
-       KEYCODE_CTRL_LEFT  = KEYCODE_LEFT  & ~0x40,
+       // -12 is reserved for Alt/Ctrl/Shift-TAB
 #if 0
-       KEYCODE_FUN1     = -12,
-       KEYCODE_FUN2     = -13,
-       KEYCODE_FUN3     = -14,
-       KEYCODE_FUN4     = -15,
-       KEYCODE_FUN5     = -16,
-       KEYCODE_FUN6     = -17,
-       KEYCODE_FUN7     = -18,
-       KEYCODE_FUN8     = -19,
-       KEYCODE_FUN9     = -20,
-       KEYCODE_FUN10    = -21,
-       KEYCODE_FUN11    = -22,
-       KEYCODE_FUN12    = -23,
+       KEYCODE_FUN1     = -13,
+       KEYCODE_FUN2     = -14,
+       KEYCODE_FUN3     = -15,
+       KEYCODE_FUN4     = -16,
+       KEYCODE_FUN5     = -17,
+       KEYCODE_FUN6     = -18,
+       KEYCODE_FUN7     = -19,
+       KEYCODE_FUN8     = -20,
+       KEYCODE_FUN9     = -21,
+       KEYCODE_FUN10    = -22,
+       KEYCODE_FUN11    = -23,
+       KEYCODE_FUN12    = -24,
 #endif
+       /* Be sure that last defined value is small enough
+        * to not interfere with Alt/Ctrl/Shift bits.
+        * So far we do not exceed -31 (0xfff..fffe1),
+        * which gives us three upper bits in LSB to play with.
+        */
+       //KEYCODE_SHIFT_TAB  = (-12)         & ~0x80,
+       //KEYCODE_SHIFT_...  = KEYCODE_...   & ~0x80,
+       //KEYCODE_CTRL_UP    = KEYCODE_UP    & ~0x40,
+       //KEYCODE_CTRL_DOWN  = KEYCODE_DOWN  & ~0x40,
+       KEYCODE_CTRL_RIGHT = KEYCODE_RIGHT & ~0x40,
+       KEYCODE_CTRL_LEFT  = KEYCODE_LEFT  & ~0x40,
+       //KEYCODE_ALT_UP     = KEYCODE_UP    & ~0x20,
+       //KEYCODE_ALT_DOWN   = KEYCODE_DOWN  & ~0x20,
+       KEYCODE_ALT_RIGHT  = KEYCODE_RIGHT & ~0x20,
+       KEYCODE_ALT_LEFT   = KEYCODE_LEFT  & ~0x20,
+
        KEYCODE_CURSOR_POS = -0x100, /* 0xfff..fff00 */
        /* How long is the longest ESC sequence we know?
         * We want it big enough to be able to contain
@@ -1329,8 +1447,9 @@ void read_key_ungets(char *buffer, const char *str, unsigned len) FAST_FUNC;
 
 #if ENABLE_FEATURE_EDITING
 /* It's NOT just ENABLEd or disabled. It's a number: */
-# ifdef CONFIG_FEATURE_EDITING_HISTORY
+# if defined CONFIG_FEATURE_EDITING_HISTORY && CONFIG_FEATURE_EDITING_HISTORY > 0
 #  define MAX_HISTORY (CONFIG_FEATURE_EDITING_HISTORY + 0)
+unsigned size_from_HISTFILESIZE(const char *hp) FAST_FUNC;
 # else
 #  define MAX_HISTORY 0
 # endif
@@ -1340,7 +1459,14 @@ typedef struct line_input_t {
 # if MAX_HISTORY
        int cnt_history;
        int cur_history;
+       int max_history; /* must never be <= 0 */
 #  if ENABLE_FEATURE_EDITING_SAVEHISTORY
+       /* meaning of this field depends on FEATURE_EDITING_SAVE_ON_EXIT:
+        * if !FEATURE_EDITING_SAVE_ON_EXIT: "how many lines are
+        * in on-disk history"
+        * if FEATURE_EDITING_SAVE_ON_EXIT: "how many in-memory lines are
+        * also in on-disk history (and thus need to be skipped on save)"
+        */
        unsigned cnt_history_in_file;
        const char *hist_file;
 #  endif
@@ -1348,27 +1474,31 @@ typedef struct line_input_t {
 # endif
 } line_input_t;
 enum {
-       DO_HISTORY = 1 * (MAX_HISTORY > 0),
-       SAVE_HISTORY = 2 * (MAX_HISTORY > 0) * ENABLE_FEATURE_EDITING_SAVEHISTORY,
-       TAB_COMPLETION = 4 * ENABLE_FEATURE_TAB_COMPLETION,
-       USERNAME_COMPLETION = 8 * ENABLE_FEATURE_USERNAME_COMPLETION,
-       VI_MODE = 0x10 * ENABLE_FEATURE_EDITING_VI,
-       WITH_PATH_LOOKUP = 0x20,
-       FOR_SHELL = DO_HISTORY | SAVE_HISTORY | TAB_COMPLETION | USERNAME_COMPLETION,
+       DO_HISTORY       = 1 * (MAX_HISTORY > 0),
+       TAB_COMPLETION   = 2 * ENABLE_FEATURE_TAB_COMPLETION,
+       USERNAME_COMPLETION = 4 * ENABLE_FEATURE_USERNAME_COMPLETION,
+       VI_MODE          = 8 * ENABLE_FEATURE_EDITING_VI,
+       WITH_PATH_LOOKUP = 0x10,
+       FOR_SHELL        = DO_HISTORY | TAB_COMPLETION | USERNAME_COMPLETION,
 };
 line_input_t *new_line_input_t(int flags) FAST_FUNC;
 /* So far static: void free_line_input_t(line_input_t *n) FAST_FUNC; */
-/* maxsize must be >= 2.
+/*
+ * maxsize must be >= 2.
  * Returns:
  * -1 on read errors or EOF, or on bare Ctrl-D,
  * 0  on ctrl-C (the line entered is still returned in 'command'),
  * >0 length of input string, including terminating '\n'
  */
-int read_line_input(const char* prompt, char* command, int maxsize, line_input_t *state) FAST_FUNC;
+int read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize, int timeout) FAST_FUNC;
+void show_history(const line_input_t *st) FAST_FUNC;
+# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
+void save_history(line_input_t *st);
+# endif
 #else
 #define MAX_HISTORY 0
 int read_line_input(const char* prompt, char* command, int maxsize) FAST_FUNC;
-#define read_line_input(prompt, command, maxsize, state) \
+#define read_line_input(state, prompt, command, maxsize, timeout) \
        read_line_input(prompt, command, maxsize)
 #endif
 
@@ -1381,6 +1511,29 @@ enum { COMM_LEN = TASK_COMM_LEN };
 enum { COMM_LEN = 16 };
 # endif
 #endif
+
+struct smaprec {
+       unsigned long mapped_rw;
+       unsigned long mapped_ro;
+       unsigned long shared_clean;
+       unsigned long shared_dirty;
+       unsigned long private_clean;
+       unsigned long private_dirty;
+       unsigned long stack;
+       unsigned long smap_pss, smap_swap;
+       unsigned long smap_size;
+       unsigned long smap_start;
+       char smap_mode[5];
+       char *smap_name;
+};
+
+#if !ENABLE_PMAP
+#define procps_read_smaps(pid, total, cb, data) \
+       procps_read_smaps(pid, total)
+#endif
+int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
+               void (*cb)(struct smaprec *, void *), void *data);
+
 typedef struct procps_status_t {
        DIR *dir;
        IF_FEATURE_SHOW_THREADS(DIR *task_dir;)
@@ -1391,6 +1544,7 @@ typedef struct procps_status_t {
        char *argv0;
        char *exe;
        IF_SELINUX(char *context;)
+       IF_FEATURE_SHOW_THREADS(unsigned main_thread_pid;)
        /* Everything below must contain no ptrs to malloc'ed data:
         * it is memset(0) for each process in procps_scan() */
        unsigned long vsz, rss; /* we round it to kbytes */
@@ -1409,13 +1563,7 @@ typedef struct procps_status_t {
 #endif
        unsigned tty_major,tty_minor;
 #if ENABLE_FEATURE_TOPMEM
-       unsigned long mapped_rw;
-       unsigned long mapped_ro;
-       unsigned long shared_clean;
-       unsigned long shared_dirty;
-       unsigned long private_clean;
-       unsigned long private_dirty;
-       unsigned long stack;
+       struct smaprec smaps;
 #endif
        char state[4];
        /* basename of executable in exec(2), read from /proc/N/stat
@@ -1458,13 +1606,6 @@ enum {
        PSSCAN_NICE     = (1 << 20) * ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS,
        PSSCAN_RUIDGID  = (1 << 21) * ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS,
        PSSCAN_TASKS    = (1 << 22) * ENABLE_FEATURE_SHOW_THREADS,
-       /* These are all retrieved from proc/NN/stat in one go: */
-       PSSCAN_STAT     = PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID
-       /**/            | PSSCAN_COMM | PSSCAN_STATE
-       /**/            | PSSCAN_VSZ | PSSCAN_RSS
-       /**/            | PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_START_TIME
-       /**/            | PSSCAN_TTY | PSSCAN_NICE
-       /**/            | PSSCAN_CPU
 };
 //procps_status_t* alloc_procps_scan(void) FAST_FUNC;
 void free_procps_scan(procps_status_t* sp) FAST_FUNC;
@@ -1474,57 +1615,67 @@ procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC;
 void read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC;
 pid_t *find_pid_by_name(const char* procName) FAST_FUNC;
 pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC;
+int starts_with_cpu(const char *str) FAST_FUNC;
+unsigned get_cpu_count(void) FAST_FUNC;
 
 
-extern const char bb_uuenc_tbl_base64[];
-extern const char bb_uuenc_tbl_std[];
+/* Use strict=1 if you process input from untrusted source:
+ * it will return NULL on invalid %xx (bad hex chars)
+ * and str + 1 if decoded char is / or NUL.
+ * In non-strict mode, it always succeeds (returns str),
+ * and also it additionally decoded '+' to space.
+ */
+char *percent_decode_in_place(char *str, int strict) FAST_FUNC;
+
+
+extern const char bb_uuenc_tbl_base64[] ALIGN1;
+extern const char bb_uuenc_tbl_std[] ALIGN1;
 void bb_uuencode(char *store, const void *s, int length, const char *tbl) FAST_FUNC;
+enum {
+       BASE64_FLAG_UU_STOP = 0x100,
+       /* Sign-extends to a value which never matches fgetc result: */
+       BASE64_FLAG_NO_STOP_CHAR = 0x80,
+};
+const char *decode_base64(char **pp_dst, const char *src) FAST_FUNC;
+void read_base64(FILE *src_stream, FILE *dst_stream, int flags) FAST_FUNC;
 
-typedef struct sha1_ctx_t {
-       uint32_t hash[8];    /* 5, +3 elements for sha256 */
-       uint64_t total64;
-       uint8_t wbuffer[64]; /* NB: always correctly aligned for uint64_t */
-       void (*process_block)(struct sha1_ctx_t*) FAST_FUNC;
-} sha1_ctx_t;
-void sha1_begin(sha1_ctx_t *ctx) FAST_FUNC;
-void sha1_hash(const void *data, size_t length, sha1_ctx_t *ctx) FAST_FUNC;
-void sha1_end(void *resbuf, sha1_ctx_t *ctx) FAST_FUNC;
-typedef struct sha1_ctx_t sha256_ctx_t;
-void sha256_begin(sha256_ctx_t *ctx) FAST_FUNC;
-#define sha256_hash sha1_hash
-#define sha256_end sha1_end
+typedef struct md5_ctx_t {
+       uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */
+       void (*process_block)(struct md5_ctx_t*) FAST_FUNC;
+       uint64_t total64;    /* must be directly before hash[] */
+       uint32_t hash[8];    /* 4 elements for md5, 5 for sha1, 8 for sha256 */
+} md5_ctx_t;
+typedef struct md5_ctx_t sha1_ctx_t;
+typedef struct md5_ctx_t sha256_ctx_t;
 typedef struct sha512_ctx_t {
+       uint64_t total64[2];  /* must be directly before hash[] */
        uint64_t hash[8];
-       uint64_t total64[2];
-       uint8_t wbuffer[128]; /* NB: always correctly aligned for uint64_t */
+       uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */
 } sha512_ctx_t;
-void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC;
-void sha512_hash(const void *buffer, size_t len, sha512_ctx_t *ctx) FAST_FUNC;
-void sha512_end(void *resbuf, sha512_ctx_t *ctx) FAST_FUNC;
-#if 1
-typedef struct md5_ctx_t {
-       uint32_t A;
-       uint32_t B;
-       uint32_t C;
-       uint32_t D;
-       uint64_t total;
-       uint32_t buflen;
-       char buffer[128];
-} md5_ctx_t;
-#else
-/* libbb/md5prime.c uses a bit different one: */
-typedef struct md5_ctx_t {
-       uint32_t state[4];      /* state (ABCD) */
-       uint32_t count[2];      /* number of bits, modulo 2^64 (lsb first) */
-       unsigned char buffer[64];       /* input buffer */
-} md5_ctx_t;
-#endif
+typedef struct sha3_ctx_t {
+       uint64_t state[25];
+       unsigned bytes_queued;
+} sha3_ctx_t;
 void md5_begin(md5_ctx_t *ctx) FAST_FUNC;
-void md5_hash(const void *data, size_t length, md5_ctx_t *ctx) FAST_FUNC;
-void md5_end(void *resbuf, md5_ctx_t *ctx) FAST_FUNC;
-
+void md5_hash(md5_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
+void md5_end(md5_ctx_t *ctx, void *resbuf) FAST_FUNC;
+void sha1_begin(sha1_ctx_t *ctx) FAST_FUNC;
+#define sha1_hash md5_hash
+void sha1_end(sha1_ctx_t *ctx, void *resbuf) FAST_FUNC;
+void sha256_begin(sha256_ctx_t *ctx) FAST_FUNC;
+#define sha256_hash md5_hash
+#define sha256_end  sha1_end
+void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC;
+void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
+void sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC;
+void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC;
+void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
+void sha3_end(sha3_ctx_t *ctx, void *resbuf) FAST_FUNC;
 
+extern uint32_t *global_crc32_table;
 uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC;
+uint32_t crc32_block_endian1(uint32_t val, const void *buf, unsigned len, uint32_t *crc_table) FAST_FUNC;
+uint32_t crc32_block_endian0(uint32_t val, const void *buf, unsigned len, uint32_t *crc_table) FAST_FUNC;
 
 typedef struct masks_labels_t {
        const char *labels;
@@ -1535,49 +1686,72 @@ int print_flags_separated(const int *masks, const char *labels,
 int print_flags(const masks_labels_t *ml, int flags) FAST_FUNC;
 
 typedef struct bb_progress_t {
-       off_t lastsize;
-       unsigned lastupdate_sec;
+       unsigned last_size;
+       unsigned last_update_sec;
+       unsigned last_change_sec;
        unsigned start_sec;
-       smallint inited;
+       const char *curfile;
 } bb_progress_t;
 
-void bb_progress_init(bb_progress_t *p) FAST_FUNC;
-void bb_progress_update(bb_progress_t *p, const char *curfile,
-                       off_t beg_range, off_t transferred,
-                       off_t totalsize) FAST_FUNC;
+#define is_bb_progress_inited(p) ((p)->curfile != NULL)
+#define bb_progress_free(p) do { \
+       if (ENABLE_UNICODE_SUPPORT) free((char*)((p)->curfile)); \
+       (p)->curfile = NULL; \
+} while (0)
+void bb_progress_init(bb_progress_t *p, const char *curfile) FAST_FUNC;
+void bb_progress_update(bb_progress_t *p,
+                       uoff_t beg_range,
+                       uoff_t transferred,
+                       uoff_t totalsize) FAST_FUNC;
+
 
 extern const char *applet_name;
+
+/* Some older linkers don't perform string merging, we used to have common strings
+ * as global arrays to do it by hand. But:
+ * (1) newer linkers do it themselves,
+ * (2) however, they DONT merge string constants with global arrays,
+ * even if the value is the same (!). Thus global arrays actually
+ * increased size a bit: for example, "/etc/passwd" string from libc
+ * wasn't merged with bb_path_passwd_file[] array!
+ * Therefore now we use #defines.
+ */
 /* "BusyBox vN.N.N (timestamp or extra_version)" */
-extern const char bb_banner[];
-extern const char bb_msg_memory_exhausted[];
-extern const char bb_msg_invalid_date[];
-extern const char bb_msg_read_error[];
-extern const char bb_msg_write_error[];
-extern const char bb_msg_unknown[];
-extern const char bb_msg_can_not_create_raw_socket[];
-extern const char bb_msg_perm_denied_are_you_root[];
-extern const char bb_msg_you_must_be_root[];
-extern const char bb_msg_requires_arg[];
-extern const char bb_msg_invalid_arg[];
-extern const char bb_msg_standard_input[];
-extern const char bb_msg_standard_output[];
-
-extern const char bb_str_default[];
+extern const char bb_banner[] ALIGN1;
+extern const char bb_msg_memory_exhausted[] ALIGN1;
+extern const char bb_msg_invalid_date[] ALIGN1;
+#define bb_msg_read_error "read error"
+#define bb_msg_write_error "write error"
+extern const char bb_msg_unknown[] ALIGN1;
+extern const char bb_msg_can_not_create_raw_socket[] ALIGN1;
+extern const char bb_msg_perm_denied_are_you_root[] ALIGN1;
+extern const char bb_msg_you_must_be_root[] ALIGN1;
+extern const char bb_msg_requires_arg[] ALIGN1;
+extern const char bb_msg_invalid_arg[] ALIGN1;
+extern const char bb_msg_standard_input[] ALIGN1;
+extern const char bb_msg_standard_output[] ALIGN1;
+
 /* NB: (bb_hexdigits_upcase[i] | 0x20) -> lowercase hex digit */
-extern const char bb_hexdigits_upcase[];
-
-extern const char bb_path_mtab_file[];
-extern const char bb_path_passwd_file[];
-extern const char bb_path_shadow_file[];
-extern const char bb_path_gshadow_file[];
-extern const char bb_path_group_file[];
-extern const char bb_path_motd_file[];
-extern const char bb_path_wtmp_file[];
-extern const char bb_dev_null[];
-extern const char bb_busybox_exec_path[];
+extern const char bb_hexdigits_upcase[] ALIGN1;
+
+extern const char bb_path_wtmp_file[] ALIGN1;
+
+/* Busybox mount uses either /proc/mounts or /etc/mtab to
+ * get the list of currently mounted filesystems */
+#define bb_path_mtab_file IF_FEATURE_MTAB_SUPPORT("/etc/mtab")IF_NOT_FEATURE_MTAB_SUPPORT("/proc/mounts")
+
+#define bb_path_passwd_file  _PATH_PASSWD
+#define bb_path_group_file   _PATH_GROUP
+#define bb_path_shadow_file  _PATH_SHADOW
+#define bb_path_gshadow_file _PATH_GSHADOW
+
+#define bb_path_motd_file "/etc/motd"
+
+#define bb_dev_null "/dev/null"
+extern const char bb_busybox_exec_path[] ALIGN1;
 /* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin,
  * but I want to save a few bytes here */
-extern const char bb_PATH_root_path[]; /* "PATH=/sbin:/usr/sbin:/bin:/usr/bin" */
+extern const char bb_PATH_root_path[] ALIGN1; /* "PATH=/sbin:/usr/sbin:/bin:/usr/bin" */
 #define bb_default_root_path (bb_PATH_root_path + sizeof("PATH"))
 #define bb_default_path      (bb_PATH_root_path + sizeof("PATH=/sbin:/usr/sbin"))
 
@@ -1601,69 +1775,71 @@ extern struct globals *const ptr_to_globals;
        (*(struct globals**)&ptr_to_globals) = (void*)(x); \
        barrier(); \
 } while (0)
+#define FREE_PTR_TO_GLOBALS() do { \
+       if (ENABLE_FEATURE_CLEAN_UP) { \
+               free(ptr_to_globals); \
+       } \
+} while (0)
 
 /* You can change LIBBB_DEFAULT_LOGIN_SHELL, but don't use it,
  * use bb_default_login_shell and following defines.
  * If you change LIBBB_DEFAULT_LOGIN_SHELL,
  * don't forget to change increment constant. */
 #define LIBBB_DEFAULT_LOGIN_SHELL  "-/bin/sh"
-extern const char bb_default_login_shell[];
+extern const char bb_default_login_shell[] ALIGN1;
 /* "/bin/sh" */
 #define DEFAULT_SHELL              (bb_default_login_shell+1)
 /* "sh" */
 #define DEFAULT_SHELL_SHORT_NAME   (bb_default_login_shell+6)
 
-#if ENABLE_FEATURE_DEVFS
+/* The following devices are the same on all systems.  */
+#define CURRENT_TTY "/dev/tty"
+#define DEV_CONSOLE "/dev/console"
+
+#if defined(__FreeBSD_kernel__)
+# define CURRENT_VC CURRENT_TTY
+# define VC_1 "/dev/ttyv0"
+# define VC_2 "/dev/ttyv1"
+# define VC_3 "/dev/ttyv2"
+# define VC_4 "/dev/ttyv3"
+# define VC_5 "/dev/ttyv4"
+# define VC_FORMAT "/dev/ttyv%d"
+#elif defined(__GNU__)
+# define CURRENT_VC CURRENT_TTY
+# define VC_1 "/dev/tty1"
+# define VC_2 "/dev/tty2"
+# define VC_3 "/dev/tty3"
+# define VC_4 "/dev/tty4"
+# define VC_5 "/dev/tty5"
+# define VC_FORMAT "/dev/tty%d"
+#elif ENABLE_FEATURE_DEVFS
+/*Linux, obsolete devfs names */
 # define CURRENT_VC "/dev/vc/0"
 # define VC_1 "/dev/vc/1"
 # define VC_2 "/dev/vc/2"
 # define VC_3 "/dev/vc/3"
 # define VC_4 "/dev/vc/4"
 # define VC_5 "/dev/vc/5"
-# if defined(__sh__) || defined(__H8300H__) || defined(__H8300S__)
-/* Yes, this sucks, but both SH (including sh64) and H8 have a SCI(F) for their
-   respective serial ports .. as such, we can't use the common device paths for
-   these. -- PFM */
-#  define SC_0 "/dev/ttsc/0"
-#  define SC_1 "/dev/ttsc/1"
-#  define SC_FORMAT "/dev/ttsc/%d"
-# else
-#  define SC_0 "/dev/tts/0"
-#  define SC_1 "/dev/tts/1"
-#  define SC_FORMAT "/dev/tts/%d"
-# endif
 # define VC_FORMAT "/dev/vc/%d"
-# define LOOP_FORMAT "/dev/loop/%d"
+# define LOOP_FORMAT "/dev/loop/%u"
 # define LOOP_NAMESIZE (sizeof("/dev/loop/") + sizeof(int)*3 + 1)
 # define LOOP_NAME "/dev/loop/"
 # define FB_0 "/dev/fb/0"
 #else
+/*Linux, normal names */
 # define CURRENT_VC "/dev/tty0"
 # define VC_1 "/dev/tty1"
 # define VC_2 "/dev/tty2"
 # define VC_3 "/dev/tty3"
 # define VC_4 "/dev/tty4"
 # define VC_5 "/dev/tty5"
-# if defined(__sh__) || defined(__H8300H__) || defined(__H8300S__)
-#  define SC_0 "/dev/ttySC0"
-#  define SC_1 "/dev/ttySC1"
-#  define SC_FORMAT "/dev/ttySC%d"
-# else
-#  define SC_0 "/dev/ttyS0"
-#  define SC_1 "/dev/ttyS1"
-#  define SC_FORMAT "/dev/ttyS%d"
-# endif
 # define VC_FORMAT "/dev/tty%d"
-# define LOOP_FORMAT "/dev/loop%d"
+# define LOOP_FORMAT "/dev/loop%u"
 # define LOOP_NAMESIZE (sizeof("/dev/loop") + sizeof(int)*3 + 1)
 # define LOOP_NAME "/dev/loop"
 # define FB_0 "/dev/fb0"
 #endif
 
-/* The following devices are the same on devfs and non-devfs systems.  */
-#define CURRENT_TTY "/dev/tty"
-#define DEV_CONSOLE "/dev/console"
-
 
 #define ARRAY_SIZE(x) ((unsigned)(sizeof(x) / sizeof((x)[0])))
 
similarity index 97%
rename from archival/liblzo_interface.h
rename to include/liblzo_interface.h
index 9a84c0b..b7f1b63 100644 (file)
@@ -30,7 +30,7 @@
 /*
 static void die_at(int line)
 {
-        bb_error_msg_and_die("internal error at %d", line);
+       bb_error_msg_and_die("internal error at %d", line);
 }
 #define assert(v) if (!(v)) die_at(__LINE__)
 */
index 0dadf42..cfc8029 100644 (file)
@@ -1,24 +1,12 @@
 /* vi: set sw=4 ts=4: */
 /*
  Copyright 2006, Bernhard Reutner-Fischer
-
  Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
-*/
-#ifndef        BB_PLATFORM_H
* Copyright 2006, Bernhard Reutner-Fischer
+ *
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#ifndef BB_PLATFORM_H
 #define BB_PLATFORM_H 1
 
-/* Assume all these functions exist by default.  Platforms where it is not
- * true will #undef them below.
- */
-#define HAVE_FDPRINTF 1
-#define HAVE_MEMRCHR 1
-#define HAVE_MKDTEMP 1
-#define HAVE_SETBIT 1
-#define HAVE_STRCASESTR 1
-#define HAVE_STRCHRNUL 1
-#define HAVE_STRSEP 1
-#define HAVE_STRSIGNAL 1
-#define HAVE_VASPRINTF 1
 
 /* Convenience macros to test the version of gcc. */
 #undef __GNUC_PREREQ
 # endif
 #endif
 
-/* Define macros for some gcc attributes.  This permits us to use the
-   macros freely, and know that they will come into play for the
-   version of gcc in which they are supported.  */
-
 #if !__GNUC_PREREQ(2,7)
 # ifndef __attribute__
 #  define __attribute__(x)
@@ -93,7 +77,7 @@
 #endif
 
 /* -fwhole-program makes all symbols local. The attribute externally_visible
  forces a symbol global.  */
* forces a symbol global.  */
 #if __GNUC_PREREQ(4,1)
 # define EXTERNALLY_VISIBLE __attribute__(( visibility("default") ))
 //__attribute__ ((__externally_visible__))
 #endif
 
 /* We use __extension__ in some places to suppress -pedantic warnings
  about GCC extensions.  This feature didn't work properly before
  gcc 2.8.  */
* about GCC extensions.  This feature didn't work properly before
* gcc 2.8.  */
 #if !__GNUC_PREREQ(2,8)
 # ifndef __extension__
 #  define __extension__
 # endif
 #endif
 
-/* gcc-2.95 had no va_copy but only __va_copy. */
-#if !__GNUC_PREREQ(3,0)
-# include <stdarg.h>
-# if !defined va_copy && defined __va_copy
-#  define va_copy(d,s) __va_copy((d),(s))
-# endif
-#endif
-
 /* FAST_FUNC is a qualifier which (possibly) makes function call faster
  * and/or smaller by using modified ABI. It is usually only needed
  * on non-static, busybox internal functions. Recent versions of gcc
 
 /* Make all declarations hidden (-fvisibility flag only affects definitions) */
 /* (don't include system headers after this until corresponding pop!) */
-#if __GNUC_PREREQ(4,1)
+#if __GNUC_PREREQ(4,1) && !defined(__CYGWIN__)
 # define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN _Pragma("GCC visibility push(hidden)")
 # define POP_SAVED_FUNCTION_VISIBILITY              _Pragma("GCC visibility pop")
 #else
 # define POP_SAVED_FUNCTION_VISIBILITY
 #endif
 
+/* gcc-2.95 had no va_copy but only __va_copy. */
+#if !__GNUC_PREREQ(3,0)
+# include <stdarg.h>
+# if !defined va_copy && defined __va_copy
+#  define va_copy(d,s) __va_copy((d),(s))
+# endif
+#endif
+
+
 /* ---- Endian Detection ------------------------------------ */
 
+#include <limits.h>
 #if defined(__digital__) && defined(__unix__)
 # include <sex.h>
-# define __BIG_ENDIAN__ (BYTE_ORDER == BIG_ENDIAN)
-# define __BYTE_ORDER BYTE_ORDER
-#elif defined __FreeBSD__
-# include <sys/resource.h>     /* rlimit */
+#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
+   || defined(__APPLE__)
+# include <sys/resource.h>  /* rlimit */
 # include <machine/endian.h>
 # define bswap_64 __bswap64
 # define bswap_32 __bswap32
 # define bswap_16 __bswap16
-# define __BIG_ENDIAN__ (_BYTE_ORDER == _BIG_ENDIAN)
-#elif !defined __APPLE__
+#else
 # include <byteswap.h>
 # include <endian.h>
 #endif
 
-#if defined(__BIG_ENDIAN__) && __BIG_ENDIAN__
+#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN
 # define BB_BIG_ENDIAN 1
 # define BB_LITTLE_ENDIAN 0
-#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN
+#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN
+# define BB_BIG_ENDIAN 0
+# define BB_LITTLE_ENDIAN 1
+#elif defined(_BYTE_ORDER) && _BYTE_ORDER == _BIG_ENDIAN
 # define BB_BIG_ENDIAN 1
 # define BB_LITTLE_ENDIAN 0
-#elif (defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) || defined(__386__)
+#elif defined(_BYTE_ORDER) && _BYTE_ORDER == _LITTLE_ENDIAN
+# define BB_BIG_ENDIAN 0
+# define BB_LITTLE_ENDIAN 1
+#elif defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN
+# define BB_BIG_ENDIAN 1
+# define BB_LITTLE_ENDIAN 0
+#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN
+# define BB_BIG_ENDIAN 0
+# define BB_LITTLE_ENDIAN 1
+#elif defined(__386__)
 # define BB_BIG_ENDIAN 0
 # define BB_LITTLE_ENDIAN 1
 #else
 # error "Can't determine endianness"
 #endif
 
+#if ULONG_MAX > 0xffffffff
+# define bb_bswap_64(x) bswap_64(x)
+#endif
+
 /* SWAP_LEnn means "convert CPU<->little_endian by swapping bytes" */
 #if BB_BIG_ENDIAN
 # define SWAP_BE16(x) (x)
 # define SWAP_BE64(x) (x)
 # define SWAP_LE16(x) bswap_16(x)
 # define SWAP_LE32(x) bswap_32(x)
-# define SWAP_LE64(x) bswap_64(x)
+# define SWAP_LE64(x) bb_bswap_64(x)
+# define IF_BIG_ENDIAN(...) __VA_ARGS__
+# define IF_LITTLE_ENDIAN(...)
 #else
 # define SWAP_BE16(x) bswap_16(x)
 # define SWAP_BE32(x) bswap_32(x)
-# define SWAP_BE64(x) bswap_64(x)
+# define SWAP_BE64(x) bb_bswap_64(x)
 # define SWAP_LE16(x) (x)
 # define SWAP_LE32(x) (x)
 # define SWAP_LE64(x) (x)
+# define IF_BIG_ENDIAN(...)
+# define IF_LITTLE_ENDIAN(...) __VA_ARGS__
 #endif
 
+
 /* ---- Unaligned access ------------------------------------ */
 
+#include <stdint.h>
+typedef int      bb__aliased_int      FIX_ALIASING;
+typedef long     bb__aliased_long     FIX_ALIASING;
+typedef uint16_t bb__aliased_uint16_t FIX_ALIASING;
+typedef uint32_t bb__aliased_uint32_t FIX_ALIASING;
+typedef uint64_t bb__aliased_uint64_t FIX_ALIASING;
+
 /* NB: unaligned parameter should be a pointer, aligned one -
  * a lvalue. This makes it more likely to not swap them by mistake
  */
 #if defined(i386) || defined(__x86_64__) || defined(__powerpc__)
-# include <stdint.h>
-typedef int      bb__aliased_int      FIX_ALIASING;
-typedef uint16_t bb__aliased_uint16_t FIX_ALIASING;
-typedef uint32_t bb__aliased_uint32_t FIX_ALIASING;
-# define move_from_unaligned_int(v, intp) ((v) = *(bb__aliased_int*)(intp))
+# define move_from_unaligned_int(v, intp)  ((v) = *(bb__aliased_int*)(intp))
+# define move_from_unaligned_long(v, longp) ((v) = *(bb__aliased_long*)(longp))
 # define move_from_unaligned16(v, u16p) ((v) = *(bb__aliased_uint16_t*)(u16p))
 # define move_from_unaligned32(v, u32p) ((v) = *(bb__aliased_uint32_t*)(u32p))
 # define move_to_unaligned16(u16p, v)   (*(bb__aliased_uint16_t*)(u16p) = (v))
@@ -215,11 +224,12 @@ typedef uint32_t bb__aliased_uint32_t FIX_ALIASING;
 #else
 /* performs reasonably well (gcc usually inlines memcpy here) */
 # define move_from_unaligned_int(v, intp) (memcpy(&(v), (intp), sizeof(int)))
+# define move_from_unaligned_long(v, longp) (memcpy(&(v), (longp), sizeof(long)))
 # define move_from_unaligned16(v, u16p) (memcpy(&(v), (u16p), 2))
 # define move_from_unaligned32(v, u32p) (memcpy(&(v), (u32p), 4))
 # define move_to_unaligned16(u16p, v) do { \
        uint16_t __t = (v); \
-       memcpy((u16p), &__t, 4); \
+       memcpy((u16p), &__t, 2); \
 } while (0)
 # define move_to_unaligned32(u32p, v) do { \
        uint32_t __t = (v); \
@@ -227,38 +237,9 @@ typedef uint32_t bb__aliased_uint32_t FIX_ALIASING;
 } while (0)
 #endif
 
-/* ---- Compiler dependent settings ------------------------- */
 
-#if (defined __digital__ && defined __unix__) \
- || defined __APPLE__ || defined __FreeBSD__
-# undef HAVE_MNTENT_H
-# undef HAVE_SYS_STATFS_H
-#else
-# define HAVE_MNTENT_H 1
-# define HAVE_SYS_STATFS_H 1
-#endif
-
-/*----- Kernel versioning ------------------------------------*/
-
-#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
-
-/* ---- Miscellaneous --------------------------------------- */
-
-#if defined(__GNU_LIBRARY__) && __GNU_LIBRARY__ < 5 && \
-       !defined(__dietlibc__) && \
-       !defined(_NEWLIB_VERSION) && \
-       !(defined __digital__ && defined __unix__)
-# error "Sorry, this libc version is not supported :("
-#endif
-
-/* Don't perpetuate e2fsck crap into the headers.  Clean up e2fsck instead. */
+/* ---- Size-saving "small" ints (arch-dependent) ----------- */
 
-#if defined __GLIBC__ || defined __UCLIBC__ \
- || defined __dietlibc__ || defined _NEWLIB_VERSION
-# include <features.h>
-#endif
-
-/* Size-saving "small" ints (arch-dependent) */
 #if defined(i386) || defined(__x86_64__) || defined(__mips__) || defined(__cris__)
 /* add other arches which benefit from this... */
 typedef signed char smallint;
@@ -278,8 +259,43 @@ typedef unsigned smalluint;
 # include <stdbool.h>
 #endif
 
-/* Try to defeat gcc's alignment of "char message[]"-like data */
-#if 1 /* if needed: !defined(arch1) && !defined(arch2) */
+
+/*----- Kernel versioning ------------------------------------*/
+
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+
+#ifdef __UCLIBC__
+# define UCLIBC_VERSION KERNEL_VERSION(__UCLIBC_MAJOR__, __UCLIBC_MINOR__, __UCLIBC_SUBLEVEL__)
+#else
+# define UCLIBC_VERSION 0
+#endif
+
+
+/* ---- Miscellaneous --------------------------------------- */
+
+#if defined __GLIBC__ \
+ || defined __UCLIBC__ \
+ || defined __dietlibc__ \
+ || defined __BIONIC__ \
+ || defined _NEWLIB_VERSION
+# include <features.h>
+#endif
+
+/* Define bb_setpgrp */
+#if defined(__digital__) && defined(__unix__)
+/* use legacy setpgrp(pid_t, pid_t) for now.  move to platform.c */
+# define bb_setpgrp() do { pid_t __me = getpid(); setpgrp(__me, __me); } while (0)
+#else
+# define bb_setpgrp() setpgrp()
+#endif
+
+/* fdprintf is more readable, we used it before dprintf was standardized */
+#include <unistd.h>
+#define fdprintf dprintf
+
+/* Useful for defeating gcc's alignment of "char message[]"-like data */
+#if !defined(__s390__)
+    /* on s390[x], non-word-aligned data accesses require larger code */
 # define ALIGN1 __attribute__((aligned(1)))
 # define ALIGN2 __attribute__((aligned(2)))
 # define ALIGN4 __attribute__((aligned(4)))
@@ -290,15 +306,15 @@ typedef unsigned smalluint;
 # define ALIGN4
 #endif
 
-
-/* uclibc does not implement daemon() for no-mmu systems.
+/*
  * For 0.9.29 and svn, __ARCH_USE_MMU__ indicates no-mmu reliably.
  * For earlier versions there is no reliable way to check if we are building
  * for a mmu-less system.
  */
 #if ENABLE_NOMMU || \
-    (defined __UCLIBC__ && __UCLIBC_MAJOR__ >= 0 && __UCLIBC_MINOR__ >= 9 && \
-    __UCLIBC_SUBLEVEL__ > 28 && !defined __ARCH_USE_MMU__)
+    (defined __UCLIBC__ && \
+     UCLIBC_VERSION > KERNEL_VERSION(0, 9, 28) && \
+     !defined __ARCH_USE_MMU__)
 # define BB_MMU 0
 # define USE_FOR_NOMMU(...) __VA_ARGS__
 # define USE_FOR_MMU(...)
@@ -308,18 +324,10 @@ typedef unsigned smalluint;
 # define USE_FOR_MMU(...) __VA_ARGS__
 #endif
 
-/* Don't use lchown with glibc older than 2.1.x */
-#if defined(__GLIBC__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 1
-# define lchown chown
-#endif
-
 #if defined(__digital__) && defined(__unix__)
-
 # include <standards.h>
 # include <inttypes.h>
 # define PRIu32 "u"
-/* use legacy setpgrp(pid_t,pid_t) for now.  move to platform.c */
-# define bb_setpgrp() do { pid_t __me = getpid(); setpgrp(__me, __me); } while (0)
 # if !defined ADJ_OFFSET_SINGLESHOT && defined MOD_CLKA && defined MOD_OFFSET
 #  define ADJ_OFFSET_SINGLESHOT (MOD_CLKA | MOD_OFFSET)
 # endif
@@ -332,15 +340,49 @@ typedef unsigned smalluint;
 # if !defined ADJ_TICK && defined MOD_CLKB
 #  define ADJ_TICK MOD_CLKB
 # endif
+#endif
 
-#else
-
-# define bb_setpgrp() setpgrp()
+#if defined(__CYGWIN__)
+# define MAXSYMLINKS SYMLOOP_MAX
+#endif
 
+#if defined(ANDROID) || defined(__ANDROID__)
+# define BB_ADDITIONAL_PATH ":/system/sbin:/system/bin:/system/xbin"
+# define SYS_ioprio_set __NR_ioprio_set
+# define SYS_ioprio_get __NR_ioprio_get
 #endif
 
-#if defined(__GLIBC__)
-# define fdprintf dprintf
+
+/* ---- Who misses what? ------------------------------------ */
+
+/* Assume all these functions and header files exist by default.
+ * Platforms where it is not true will #undef them below.
+ */
+#define HAVE_CLEARENV 1
+#define HAVE_FDATASYNC 1
+#define HAVE_DPRINTF 1
+#define HAVE_MEMRCHR 1
+#define HAVE_MKDTEMP 1
+#define HAVE_PTSNAME_R 1
+#define HAVE_SETBIT 1
+#define HAVE_SIGHANDLER_T 1
+#define HAVE_STPCPY 1
+#define HAVE_STRCASESTR 1
+#define HAVE_STRCHRNUL 1
+#define HAVE_STRSEP 1
+#define HAVE_STRSIGNAL 1
+#define HAVE_STRVERSCMP 1
+#define HAVE_VASPRINTF 1
+#define HAVE_UNLOCKED_STDIO 1
+#define HAVE_UNLOCKED_LINE_OPS 1
+#define HAVE_GETLINE 1
+#define HAVE_XTABS 1
+#define HAVE_MNTENT_H 1
+#define HAVE_NET_ETHERNET_H 1
+#define HAVE_SYS_STATFS_H 1
+
+#if defined(__UCLIBC__) && UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32)
+# undef HAVE_STRVERSCMP
 #endif
 
 #if defined(__dietlibc__)
@@ -348,19 +390,70 @@ typedef unsigned smalluint;
 #endif
 
 #if defined(__WATCOMC__)
-# undef HAVE_FDPRINTF
+# undef HAVE_DPRINTF
+# undef HAVE_GETLINE
 # undef HAVE_MEMRCHR
 # undef HAVE_MKDTEMP
 # undef HAVE_SETBIT
+# undef HAVE_STPCPY
 # undef HAVE_STRCASESTR
 # undef HAVE_STRCHRNUL
 # undef HAVE_STRSEP
 # undef HAVE_STRSIGNAL
+# undef HAVE_STRVERSCMP
 # undef HAVE_VASPRINTF
+# undef HAVE_UNLOCKED_STDIO
+# undef HAVE_UNLOCKED_LINE_OPS
+# undef HAVE_NET_ETHERNET_H
+#endif
+
+#if defined(__CYGWIN__)
+# undef HAVE_CLEARENV
+# undef HAVE_FDPRINTF
+# undef HAVE_MEMRCHR
+# undef HAVE_PTSNAME_R
+# undef HAVE_STRVERSCMP
+# undef HAVE_UNLOCKED_LINE_OPS
 #endif
 
-#if defined(__FreeBSD__)
+/* These BSD-derived OSes share many similarities */
+#if (defined __digital__ && defined __unix__) \
+ || defined __APPLE__ \
+ || defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__
+# undef HAVE_CLEARENV
+# undef HAVE_FDATASYNC
+# undef HAVE_GETLINE
+# undef HAVE_MNTENT_H
+# undef HAVE_PTSNAME_R
+# undef HAVE_SYS_STATFS_H
+# undef HAVE_SIGHANDLER_T
+# undef HAVE_STRVERSCMP
+# undef HAVE_XTABS
+# undef HAVE_DPRINTF
+# undef HAVE_UNLOCKED_STDIO
+# undef HAVE_UNLOCKED_LINE_OPS
+#endif
+
+#if defined(__FreeBSD__) || defined(__APPLE__)
+# undef HAVE_STRCHRNUL
+#endif
+
+#if defined(__NetBSD__)
+# define HAVE_GETLINE 1  /* Recent NetBSD versions have getline() */
+#endif
+
+#if defined(__digital__) && defined(__unix__)
+# undef HAVE_STPCPY
+#endif
+
+#if defined(ANDROID) || defined(__ANDROID__)
+# undef HAVE_DPRINTF
+# undef HAVE_GETLINE
+# undef HAVE_STPCPY
 # undef HAVE_STRCHRNUL
+# undef HAVE_STRVERSCMP
+# undef HAVE_UNLOCKED_LINE_OPS
+# undef HAVE_NET_ETHERNET_H
 #endif
 
 /*
@@ -368,8 +461,8 @@ typedef unsigned smalluint;
  * These must come after all the HAVE_* macros are defined (or not)
  */
 
-#ifndef HAVE_FDPRINTF
-extern int fdprintf(int fd, const char *format, ...);
+#ifndef HAVE_DPRINTF
+extern int dprintf(int fd, const char *format, ...);
 #endif
 
 #ifndef HAVE_MEMRCHR
@@ -385,6 +478,14 @@ extern char *mkdtemp(char *template) FAST_FUNC;
 # define clrbit(a, b)  ((a)[(b) >> 3] &= ~(1 << ((b) & 7)))
 #endif
 
+#ifndef HAVE_SIGHANDLER_T
+typedef void (*sighandler_t)(int);
+#endif
+
+#ifndef HAVE_STPCPY
+extern char *stpcpy(char *p, const char *to_add) FAST_FUNC;
+#endif
+
 #ifndef HAVE_STRCASESTR
 extern char *strcasestr(const char *s, const char *pattern) FAST_FUNC;
 #endif
@@ -406,4 +507,10 @@ extern char *strsep(char **stringp, const char *delim) FAST_FUNC;
 extern int vasprintf(char **string_ptr, const char *format, va_list p) FAST_FUNC;
 #endif
 
+#ifndef HAVE_GETLINE
+# include <stdio.h> /* for FILE */
+# include <sys/types.h> /* size_t */
+extern ssize_t getline(char **lineptr, size_t *n, FILE *stream) FAST_FUNC;
+#endif
+
 #endif
index aa63ac9..625b6f5 100644 (file)
@@ -18,7 +18,7 @@
    02111-1307 USA.  */
 
 /*
- *     POSIX Standard: 9.2.2 User Database Access      <pwd.h>
+ * POSIX Standard: 9.2.2 User Database Access  <pwd.h>
  */
 
 #ifndef BB_PWD_H
@@ -30,7 +30,7 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
  * We will use libc-defined structures, but will #define function names
  * so that function calls are directed to bb_internal_XXX replacements
  */
-
+#undef endpwent
 #define setpwent    bb_internal_setpwent
 #define endpwent    bb_internal_endpwent
 #define getpwent    bb_internal_getpwent
@@ -63,7 +63,7 @@ extern struct passwd *fgetpwent(FILE *__stream);
 
 /* Write the given entry onto the given stream.  */
 extern int putpwent(const struct passwd *__restrict __p,
-                    FILE *__restrict __f);
+               FILE *__restrict __f);
 #endif
 
 /* Search for an entry with a matching user ID.  */
@@ -81,25 +81,25 @@ extern struct passwd *getpwnam(const char *__name);
    POSIX people would choose.  */
 
 extern int getpwent_r(struct passwd *__restrict __resultbuf,
-                      char *__restrict __buffer, size_t __buflen,
-                      struct passwd **__restrict __result);
+               char *__restrict __buffer, size_t __buflen,
+               struct passwd **__restrict __result);
 
 extern int getpwuid_r(uid_t __uid,
-                      struct passwd *__restrict __resultbuf,
-                      char *__restrict __buffer, size_t __buflen,
-                      struct passwd **__restrict __result);
+               struct passwd *__restrict __resultbuf,
+               char *__restrict __buffer, size_t __buflen,
+               struct passwd **__restrict __result);
 
 extern int getpwnam_r(const char *__restrict __name,
-                      struct passwd *__restrict __resultbuf,
-                      char *__restrict __buffer, size_t __buflen,
-                      struct passwd **__restrict __result);
+               struct passwd *__restrict __resultbuf,
+               char *__restrict __buffer, size_t __buflen,
+               struct passwd **__restrict __result);
 
 /* Read an entry from STREAM.  This function is not standardized and
    probably never will.  */
 extern int fgetpwent_r(FILE *__restrict __stream,
-                       struct passwd *__restrict __resultbuf,
-                       char *__restrict __buffer, size_t __buflen,
-                       struct passwd **__restrict __result);
+               struct passwd *__restrict __resultbuf,
+               char *__restrict __buffer, size_t __buflen,
+               struct passwd **__restrict __result);
 
 POP_SAVED_FUNCTION_VISIBILITY
 
index b5fe8ec..750fc20 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Common defines/structures/etc... for applets that need to work with the RTC.
  *
- * Licensed under the GPL-2 or later.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #ifndef BB_RTC_H
@@ -35,9 +35,9 @@ struct linux_rtc_time {
 };
 
 struct linux_rtc_wkalrm {
-       unsigned char enabled;  /* 0 = alarm disabled, 1 = alarm enabled */
+       unsigned char enabled;  /* 0 = alarm disabled, 1 = alarm enabled */
        unsigned char pending;  /* 0 = alarm not pending, 1 = alarm pending */
-       struct linux_rtc_time time;     /* time the alarm is set to */
+       struct linux_rtc_time time;  /* time the alarm is set to */
 };
 
 /*
index de126dd..7babe4f 100644 (file)
@@ -37,11 +37,6 @@ struct spwd {
        unsigned long sp_flag;  /* Reserved */
 };
 
-/* Paths to the user database files */
-#ifndef _PATH_SHADOW
-#define _PATH_SHADOW "/etc/shadow"
-#endif
-
 #define setspent    bb_internal_setspent
 #define endspent    bb_internal_endspent
 #define getspent    bb_internal_getspent
@@ -84,21 +79,21 @@ extern int putspent(const struct spwd *__p, FILE *__stream);
 
 /* Reentrant versions of some of the functions above */
 extern int getspent_r(struct spwd *__result_buf, char *__buffer,
-                      size_t __buflen, struct spwd **__result);
+               size_t __buflen, struct spwd **__result);
 #endif
 
 extern int getspnam_r(const char *__name, struct spwd *__result_buf,
-                      char *__buffer, size_t __buflen,
-                      struct spwd **__result);
+               char *__buffer, size_t __buflen,
+               struct spwd **__result);
 
 #ifdef UNUSED_FOR_NOW
 extern int sgetspent_r(const char *__string, struct spwd *__result_buf,
-                       char *__buffer, size_t __buflen,
-                       struct spwd **__result);
+               char *__buffer, size_t __buflen,
+               struct spwd **__result);
 
 extern int fgetspent_r(FILE *__stream, struct spwd *__result_buf,
-                       char *__buffer, size_t __buflen,
-                       struct spwd **__result);
+               char *__buffer, size_t __buflen,
+               struct spwd **__result);
 /* Protect password file against multi writers */
 extern int lckpwdf(void);
 
diff --git a/include/unarchive.h b/include/unarchive.h
deleted file mode 100644 (file)
index b55af6d..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-/* vi: set sw=4 ts=4: */
-#ifndef UNARCHIVE_H
-#define UNARCHIVE_H 1
-
-PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
-
-enum {
-#if BB_BIG_ENDIAN
-       COMPRESS_MAGIC = 0x1f9d,
-       GZIP_MAGIC  = 0x1f8b,
-       BZIP2_MAGIC = 'B' * 256 + 'Z',
-       /* .xz signature: 0xfd, '7', 'z', 'X', 'Z', 0x00 */
-       /* More info at: http://tukaani.org/xz/xz-file-format.txt */
-       XZ_MAGIC1   = 0xfd * 256 + '7',
-       XZ_MAGIC2   = (('z' * 256 + 'X') * 256 + 'Z') * 256 + 0,
-       /* Different form: 32 bits, then 16 bits: */
-       XZ_MAGIC1a  = ((0xfd * 256 + '7') * 256 + 'z') * 256 + 'X',
-       XZ_MAGIC2a  = 'Z' * 256 + 0,
-#else
-       COMPRESS_MAGIC = 0x9d1f,
-       GZIP_MAGIC  = 0x8b1f,
-       BZIP2_MAGIC = 'Z' * 256 + 'B',
-       XZ_MAGIC1   = '7' * 256 + 0xfd,
-       XZ_MAGIC2   = ((0 * 256 + 'Z') * 256 + 'X') * 256 + 'z',
-       XZ_MAGIC1a  = (('X' * 256 + 'z') * 256 + '7') * 256 + 0xfd,
-       XZ_MAGIC2a  = 0 * 256 + 'Z',
-#endif
-};
-
-typedef struct file_header_t {
-       char *name;
-       char *link_target;
-#if ENABLE_FEATURE_TAR_UNAME_GNAME
-       char *tar__uname;
-       char *tar__gname;
-#endif
-       off_t size;
-       uid_t uid;
-       gid_t gid;
-       mode_t mode;
-       time_t mtime;
-       dev_t device;
-} file_header_t;
-
-struct hardlinks_t;
-
-typedef struct archive_handle_t {
-       /* Flags. 1st since it is most used member */
-       unsigned ah_flags;
-
-       /* The raw stream as read from disk or stdin */
-       int src_fd;
-
-       /* Define if the header and data component should be processed */
-       char FAST_FUNC (*filter)(struct archive_handle_t *);
-       /* List of files that have been accepted */
-       llist_t *accept;
-       /* List of files that have been rejected */
-       llist_t *reject;
-       /* List of files that have successfully been worked on */
-       llist_t *passed;
-
-       /* Currently processed file's header */
-       file_header_t *file_header;
-
-       /* Process the header component, e.g. tar -t */
-       void FAST_FUNC (*action_header)(const file_header_t *);
-
-       /* Process the data component, e.g. extract to filesystem */
-       void FAST_FUNC (*action_data)(struct archive_handle_t *);
-
-       /* Function that skips data */
-       void FAST_FUNC (*seek)(int fd, off_t amount);
-
-       /* Count processed bytes */
-       off_t offset;
-
-       /* Archiver specific. Can make it a union if it ever gets big */
-#if ENABLE_TAR || ENABLE_DPKG || ENABLE_DPKG_DEB
-       smallint tar__end;
-# if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
-       char* tar__longname;
-       char* tar__linkname;
-# endif
-#if ENABLE_FEATURE_TAR_TO_COMMAND
-       char* tar__to_command;
-#endif
-# if ENABLE_FEATURE_TAR_SELINUX
-       char* tar__global_sctx;
-       char* tar__next_file_sctx;
-# endif
-#endif
-#if ENABLE_CPIO || ENABLE_RPM2CPIO || ENABLE_RPM
-       uoff_t cpio__blocks;
-       struct hardlinks_t *cpio__hardlinks_to_create;
-       struct hardlinks_t *cpio__created_hardlinks;
-#endif
-#if ENABLE_DPKG || ENABLE_DPKG_DEB
-       /* Temporary storage */
-       char *dpkg__buffer;
-       /* How to process any sub archive, e.g. get_header_tar_gz */
-       char FAST_FUNC (*dpkg__action_data_subarchive)(struct archive_handle_t *);
-       /* Contains the handle to a sub archive */
-       struct archive_handle_t *dpkg__sub_archive;
-#endif
-#if ENABLE_FEATURE_AR_CREATE
-       const char *ar__name;
-       struct archive_handle_t *ar__out;
-#endif
-} archive_handle_t;
-/* bits in ah_flags */
-#define ARCHIVE_RESTORE_DATE        (1 << 0)
-#define ARCHIVE_CREATE_LEADING_DIRS (1 << 1)
-#define ARCHIVE_UNLINK_OLD          (1 << 2)
-#define ARCHIVE_EXTRACT_QUIET       (1 << 3)
-#define ARCHIVE_EXTRACT_NEWER       (1 << 4)
-#define ARCHIVE_DONT_RESTORE_OWNER  (1 << 5)
-#define ARCHIVE_DONT_RESTORE_PERM   (1 << 6)
-#define ARCHIVE_NUMERIC_OWNER       (1 << 7)
-#define ARCHIVE_O_TRUNC             (1 << 8)
-
-
-/* POSIX tar Header Block, from POSIX 1003.1-1990  */
-#define TAR_BLOCK_SIZE 512
-#define NAME_SIZE      100
-#define NAME_SIZE_STR "100"
-typedef struct tar_header_t {       /* byte offset */
-       char name[NAME_SIZE];     /*   0-99 */
-       char mode[8];             /* 100-107 */
-       char uid[8];              /* 108-115 */
-       char gid[8];              /* 116-123 */
-       char size[12];            /* 124-135 */
-       char mtime[12];           /* 136-147 */
-       char chksum[8];           /* 148-155 */
-       char typeflag;            /* 156-156 */
-       char linkname[NAME_SIZE]; /* 157-256 */
-       /* POSIX:   "ustar" NUL "00" */
-       /* GNU tar: "ustar  " NUL */
-       /* Normally it's defined as magic[6] followed by
-        * version[2], but we put them together to save code.
-        */
-       char magic[8];            /* 257-264 */
-       char uname[32];           /* 265-296 */
-       char gname[32];           /* 297-328 */
-       char devmajor[8];         /* 329-336 */
-       char devminor[8];         /* 337-344 */
-       char prefix[155];         /* 345-499 */
-       char padding[12];         /* 500-512 (pad to exactly TAR_BLOCK_SIZE) */
-} tar_header_t;
-struct BUG_tar_header {
-       char c[sizeof(tar_header_t) == TAR_BLOCK_SIZE ? 1 : -1];
-};
-
-
-
-/* Info struct unpackers can fill out to inform users of thing like
- * timestamps of unpacked files */
-typedef struct unpack_info_t {
-       time_t mtime;
-} unpack_info_t;
-
-extern archive_handle_t *init_handle(void) FAST_FUNC;
-
-extern char filter_accept_all(archive_handle_t *archive_handle) FAST_FUNC;
-extern char filter_accept_list(archive_handle_t *archive_handle) FAST_FUNC;
-extern char filter_accept_list_reassign(archive_handle_t *archive_handle) FAST_FUNC;
-extern char filter_accept_reject_list(archive_handle_t *archive_handle) FAST_FUNC;
-
-extern void unpack_ar_archive(archive_handle_t *ar_archive) FAST_FUNC;
-
-extern void data_skip(archive_handle_t *archive_handle) FAST_FUNC;
-extern void data_extract_all(archive_handle_t *archive_handle) FAST_FUNC;
-extern void data_extract_to_stdout(archive_handle_t *archive_handle) FAST_FUNC;
-extern void data_extract_to_command(archive_handle_t *archive_handle) FAST_FUNC;
-
-extern void header_skip(const file_header_t *file_header) FAST_FUNC;
-extern void header_list(const file_header_t *file_header) FAST_FUNC;
-extern void header_verbose_list(const file_header_t *file_header) FAST_FUNC;
-
-extern char get_header_ar(archive_handle_t *archive_handle) FAST_FUNC;
-extern char get_header_cpio(archive_handle_t *archive_handle) FAST_FUNC;
-extern char get_header_tar(archive_handle_t *archive_handle) FAST_FUNC;
-extern char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC;
-extern char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC;
-extern char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC;
-
-extern void seek_by_jump(int fd, off_t amount) FAST_FUNC;
-extern void seek_by_read(int fd, off_t amount) FAST_FUNC;
-
-extern void data_align(archive_handle_t *archive_handle, unsigned boundary) FAST_FUNC;
-extern const llist_t *find_list_entry(const llist_t *list, const char *filename) FAST_FUNC;
-extern const llist_t *find_list_entry2(const llist_t *list, const char *filename) FAST_FUNC;
-
-/* A bit of bunzip2 internals are exposed for compressed help support: */
-typedef struct bunzip_data bunzip_data;
-int start_bunzip(bunzip_data **bdp, int in_fd, const unsigned char *inbuf, int len) FAST_FUNC;
-int read_bunzip(bunzip_data *bd, char *outbuf, int len) FAST_FUNC;
-void dealloc_bunzip(bunzip_data *bd) FAST_FUNC;
-
-typedef struct inflate_unzip_result {
-       off_t bytes_out;
-       uint32_t crc;
-} inflate_unzip_result;
-
-IF_DESKTOP(long long) int inflate_unzip(inflate_unzip_result *res, off_t compr_size, int src_fd, int dst_fd) FAST_FUNC;
-/* xz unpacker takes .xz stream from offset 6 */
-IF_DESKTOP(long long) int unpack_xz_stream(int src_fd, int dst_fd) FAST_FUNC;
-/* lzma unpacker takes .lzma stream from offset 0 */
-IF_DESKTOP(long long) int unpack_lzma_stream(int src_fd, int dst_fd) FAST_FUNC;
-/* the rest wants 2 first bytes already skipped by the caller */
-IF_DESKTOP(long long) int unpack_bz2_stream(int src_fd, int dst_fd) FAST_FUNC;
-IF_DESKTOP(long long) int unpack_gz_stream(int src_fd, int dst_fd) FAST_FUNC;
-IF_DESKTOP(long long) int unpack_gz_stream_with_info(int src_fd, int dst_fd, unpack_info_t *info) FAST_FUNC;
-IF_DESKTOP(long long) int unpack_Z_stream(int src_fd, int dst_fd) FAST_FUNC;
-/* wrapper which checks first two bytes to be "BZ" */
-IF_DESKTOP(long long) int unpack_bz2_stream_prime(int src_fd, int dst_fd) FAST_FUNC;
-
-char* append_ext(char *filename, const char *expected_ext) FAST_FUNC;
-int bbunpack(char **argv,
-           IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(unpack_info_t *info),
-           char* FAST_FUNC (*make_new_name)(char *filename, const char *expected_ext),
-           const char *expected_ext
-) FAST_FUNC;
-
-#if BB_MMU
-void open_transformer(int fd,
-       IF_DESKTOP(long long) int FAST_FUNC (*transformer)(int src_fd, int dst_fd)) FAST_FUNC;
-#define open_transformer(fd, transformer, transform_prog) open_transformer(fd, transformer)
-#else
-void open_transformer(int src_fd, const char *transform_prog) FAST_FUNC;
-#define open_transformer(fd, transformer, transform_prog) open_transformer(fd, transform_prog)
-#endif
-
-POP_SAVED_FUNCTION_VISIBILITY
-
-#endif
index e9e2bd1..0317a21 100644 (file)
@@ -1,6 +1,6 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #ifndef UNICODE_H
 #define UNICODE_H 1
@@ -27,6 +27,7 @@ enum {
 # define unicode_strwidth(string) strlen(string)
 # define unicode_status UNICODE_OFF
 # define init_unicode() ((void)0)
+# define reinit_unicode(LANG) ((void)0)
 
 #else
 
@@ -60,13 +61,14 @@ enum {
 //UNUSED: unsigned FAST_FUNC unicode_padding_to_width(unsigned width, const char *src);
 //UNUSED: char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char *src, unsigned width, int flags);
 char* FAST_FUNC unicode_conv_to_printable(uni_stat_t *stats, const char *src);
-char* FAST_FUNC unicode_conv_to_printable_maxwidth(uni_stat_t *stats, const char *src, unsigned maxwidth);
-char* FAST_FUNC unicode_conv_to_printable_fixedwidth(uni_stat_t *stats, const char *src, unsigned width);
+//UNUSED: char* FAST_FUNC unicode_conv_to_printable_maxwidth(uni_stat_t *stats, const char *src, unsigned maxwidth);
+char* FAST_FUNC unicode_conv_to_printable_fixedwidth(/*uni_stat_t *stats,*/ const char *src, unsigned width);
 
 # if ENABLE_UNICODE_USING_LOCALE
 
 extern uint8_t unicode_status;
 void init_unicode(void) FAST_FUNC;
+void reinit_unicode(const char *LANG) FAST_FUNC;
 
 # else
 
@@ -75,9 +77,11 @@ void init_unicode(void) FAST_FUNC;
 #  if !ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
 #   define unicode_status UNICODE_ON
 #   define init_unicode() ((void)0)
+#   define reinit_unicode(LANG) ((void)0)
 #  else
 extern uint8_t unicode_status;
 void init_unicode(void) FAST_FUNC;
+void reinit_unicode(const char *LANG) FAST_FUNC;
 #  endif
 
 #  undef MB_CUR_MAX
index df5b2f8..78beccf 100644 (file)
 #ifndef BB_USAGE_H
 #define BB_USAGE_H 1
 
-
 #define NOUSAGE_STR "\b"
 
 INSERT
 
-#define acpid_trivial_usage \
-       "[-d] [-c CONFDIR] [-l LOGFILE] [-e PROC_EVENT_FILE] [EVDEV_EVENT_FILE]..."
-#define acpid_full_usage "\n\n" \
-       "Listen to ACPI events and spawn specific helpers on event arrival\n" \
-     "\nOptions:" \
-     "\n       -d      Don't daemonize, log to stderr" \
-     "\n       -c DIR  Config directory [/etc/acpi]" \
-     "\n       -e FILE /proc event file [/proc/acpi/event]" \
-     "\n       -l FILE Log file [/var/log/acpid]" \
-       IF_FEATURE_ACPID_COMPAT( \
-     "\n\nAccept and ignore compatibility options -g -m -s -S -v" \
-       )
-
-#define acpid_example_usage \
-       "# acpid -l /var/log/my-acpi-log\n" \
-       "# acpid -d /dev/input/event*\n"
-
-#define addgroup_trivial_usage \
-       "[-g GID] " IF_FEATURE_ADDUSER_TO_GROUP("[USER] ") "GROUP"
-#define addgroup_full_usage "\n\n" \
-       "Add a group " IF_FEATURE_ADDUSER_TO_GROUP("or add a user to a group") "\n" \
-     "\nOptions:" \
-     "\n       -g GID  Group id" \
-     "\n       -S      Create a system group" \
-
-#define adduser_trivial_usage \
-       "[OPTIONS] USER"
-#define adduser_full_usage "\n\n" \
-       "Add a user\n" \
-     "\nOptions:" \
-     "\n       -h DIR          Home directory" \
-     "\n       -g GECOS        GECOS field" \
-     "\n       -s SHELL        Login shell" \
-     "\n       -G GRP          Add user to existing group" \
-     "\n       -S              Create a system user" \
-     "\n       -D              Don't assign a password" \
-     "\n       -H              Don't create home directory" \
-     "\n       -u UID          User id" \
-
-#define adjtimex_trivial_usage \
-       "[-q] [-o OFF] [-f FREQ] [-p TCONST] [-t TICK]"
-#define adjtimex_full_usage "\n\n" \
-       "Read and optionally set system timebase parameters. See adjtimex(2)\n" \
-     "\nOptions:" \
-     "\n       -q      Quiet" \
-     "\n       -o OFF  Time offset, microseconds" \
-     "\n       -f FREQ Frequency adjust, integer kernel units (65536 is 1ppm)" \
-     "\n               (positive values make clock run faster)" \
-     "\n       -t TICK Microseconds per tick, usually 10000" \
-     "\n       -p TCONST" \
-
-#define ar_trivial_usage \
-       "[-o] [-v] [-p] [-t] [-x] ARCHIVE FILES"
-#define ar_full_usage "\n\n" \
-       "Extract or list FILES from an ar archive\n" \
-     "\nOptions:" \
-     "\n       -o      Preserve original dates" \
-     "\n       -p      Extract to stdout" \
-     "\n       -t      List" \
-     "\n       -x      Extract" \
-     "\n       -v      Verbose" \
-
-#define arp_trivial_usage \
-     "\n[-vn]  [-H HWTYPE] [-i IF] -a [HOSTNAME]" \
-     "\n[-v]               [-i IF] -d HOSTNAME [pub]" \
-     "\n[-v]   [-H HWTYPE] [-i IF] -s HOSTNAME HWADDR [temp]" \
-     "\n[-v]   [-H HWTYPE] [-i IF] -s HOSTNAME HWADDR [netmask MASK] pub" \
-     "\n[-v]   [-H HWTYPE] [-i IF] -Ds HOSTNAME IFACE [netmask MASK] pub"
-#define arp_full_usage "\n\n" \
-       "Manipulate ARP cache\n" \
-     "\nOptions:" \
-       "\n     -a              Display (all) hosts" \
-       "\n     -s              Set new ARP entry" \
-       "\n     -d              Delete a specified entry" \
-       "\n     -v              Verbose" \
-       "\n     -n              Don't resolve names" \
-       "\n     -i IF           Network interface" \
-       "\n     -D              Read <hwaddr> from given device" \
-       "\n     -A, -p AF       Protocol family" \
-       "\n     -H HWTYPE       Hardware address type" \
-
-#define arping_trivial_usage \
-       "[-fqbDUA] [-c CNT] [-w TIMEOUT] [-I IFACE] [-s SRC_IP] DST_IP"
-#define arping_full_usage "\n\n" \
-       "Send ARP requests/replies\n" \
-     "\nOptions:" \
-     "\n       -f              Quit on first ARP reply" \
-     "\n       -q              Quiet" \
-     "\n       -b              Keep broadcasting, don't go unicast" \
-     "\n       -D              Duplicated address detection mode" \
-     "\n       -U              Unsolicited ARP mode, update your neighbors" \
-     "\n       -A              ARP answer mode, update your neighbors" \
-     "\n       -c N            Stop after sending N ARP requests" \
-     "\n       -w TIMEOUT      Time to wait for ARP reply, seconds" \
-     "\n       -I IFACE        Interface to use (default eth0)" \
-     "\n       -s SRC_IP       Sender IP address" \
-     "\n       DST_IP          Target IP address" \
-
-#define sh_trivial_usage NOUSAGE_STR
-#define sh_full_usage ""
-#define ash_trivial_usage NOUSAGE_STR
-#define ash_full_usage ""
-#define hush_trivial_usage NOUSAGE_STR
-#define hush_full_usage ""
-#define lash_trivial_usage NOUSAGE_STR
-#define lash_full_usage ""
-#define msh_trivial_usage NOUSAGE_STR
-#define msh_full_usage ""
-#define bash_trivial_usage NOUSAGE_STR
-#define bash_full_usage ""
-
-#define awk_trivial_usage \
-       "[OPTIONS] [AWK_PROGRAM] [FILE]..."
-#define awk_full_usage "\n\n" \
-       "Options:" \
-     "\n       -v VAR=VAL      Set variable" \
-     "\n       -F SEP          Use SEP as field separator" \
-     "\n       -f FILE         Read program from FILE" \
-
-#define basename_trivial_usage \
-       "FILE [SUFFIX]"
-#define basename_full_usage "\n\n" \
-       "Strip directory path and .SUFFIX from FILE\n"
-#define basename_example_usage \
-       "$ basename /usr/local/bin/foo\n" \
-       "foo\n" \
-       "$ basename /usr/local/bin/\n" \
-       "bin\n" \
-       "$ basename /foo/bar.txt .txt\n" \
-       "bar"
-
-#define beep_trivial_usage \
-       "-f FREQ -l LEN -d DELAY -r COUNT -n"
-#define beep_full_usage "\n\n" \
-       "Options:" \
-     "\n       -f      Frequency in Hz" \
-     "\n       -l      Length in ms" \
-     "\n       -d      Delay in ms" \
-     "\n       -r      Repetitions" \
-     "\n       -n      Start new tone" \
-
-#define blkid_trivial_usage \
-       ""
-#define blkid_full_usage "\n\n" \
-       "Print UUIDs of all filesystems"
-
-#define brctl_trivial_usage \
-       "COMMAND [BRIDGE [INTERFACE]]"
-#define brctl_full_usage "\n\n" \
-       "Manage ethernet bridges\n" \
-     "\nCommands:" \
-       IF_FEATURE_BRCTL_SHOW( \
-     "\n       show                    Show a list of bridges" \
-       ) \
-     "\n       addbr BRIDGE            Create BRIDGE" \
-     "\n       delbr BRIDGE            Delete BRIDGE" \
-     "\n       addif BRIDGE IFACE      Add IFACE to BRIDGE" \
-     "\n       delif BRIDGE IFACE      Delete IFACE from BRIDGE" \
-       IF_FEATURE_BRCTL_FANCY( \
-     "\n       setageing BRIDGE TIME           Set ageing time" \
-     "\n       setfd BRIDGE TIME               Set bridge forward delay" \
-     "\n       sethello BRIDGE TIME            Set hello time" \
-     "\n       setmaxage BRIDGE TIME           Set max message age" \
-     "\n       setpathcost BRIDGE COST         Set path cost" \
-     "\n       setportprio BRIDGE PRIO         Set port priority" \
-     "\n       setbridgeprio BRIDGE PRIO       Set bridge priority" \
-     "\n       stp BRIDGE [1/yes/on|0/no/off]  STP on/off" \
-       ) \
-
-#define bzip2_trivial_usage \
-       "[OPTIONS] [FILE]..."
-#define bzip2_full_usage "\n\n" \
-       "Compress FILEs (or stdin) with bzip2 algorithm\n" \
-     "\nOptions:" \
-     "\n       -1..9   Compression level" \
-     "\n       -d      Decompress" \
-     "\n       -c      Write to stdout" \
-     "\n       -f      Force" \
-
 #define busybox_notes_usage \
        "Hello world!\n"
 
-#define lzop_trivial_usage \
-       "[-cfvd123456789CF] [FILE]..."
-#define lzop_full_usage "\n\n" \
-       "Options:" \
-     "\n       -1..9   Compression level" \
-     "\n       -d      Decompress" \
-     "\n       -c      Write to stdout" \
-     "\n       -f      Force" \
-     "\n       -v      Verbose" \
-     "\n       -F      Don't store or verify checksum" \
-     "\n       -C      Also write checksum of compressed block" \
-
-#define lzopcat_trivial_usage \
-       "[-vCF] [FILE]..."
-#define lzopcat_full_usage "\n\n" \
-       "       -v      Verbose" \
-     "\n       -F      Don't store or verify checksum" \
-
-#define unlzop_trivial_usage \
-       "[-cfvCF] [FILE]..."
-#define unlzop_full_usage "\n\n" \
-       "Options:" \
-     "\n       -c      Write to stdout" \
-     "\n       -f      Force" \
-     "\n       -v      Verbose" \
-     "\n       -F      Don't store or verify checksum" \
-
-#define unlzma_trivial_usage \
-       "[OPTIONS] [FILE]..."
-#define unlzma_full_usage "\n\n" \
-       "Decompress FILE (or stdin)\n" \
-     "\nOptions:" \
-     "\n       -c      Write to stdout" \
-     "\n       -f      Force" \
-
-#define lzma_trivial_usage \
-       "-d [OPTIONS] [FILE]..."
-#define lzma_full_usage "\n\n" \
-       "Decompress FILE (or stdin)\n" \
-     "\nOptions:" \
-     "\n       -d      Decompress" \
-     "\n       -c      Write to stdout" \
-     "\n       -f      Force" \
-
-#define lzcat_trivial_usage \
-       "FILE"
-#define lzcat_full_usage "\n\n" \
-       "Decompress to stdout"
-
-#define unxz_trivial_usage \
-       "[OPTIONS] [FILE]..."
-#define unxz_full_usage "\n\n" \
-       "Decompress FILE (or stdin)\n" \
-     "\nOptions:" \
-     "\n       -c      Write to stdout" \
-     "\n       -f      Force" \
-
-#define xz_trivial_usage \
-       "-d [OPTIONS] [FILE]..."
-#define xz_full_usage "\n\n" \
-       "Decompress FILE (or stdin)\n" \
-     "\nOptions:" \
-     "\n       -d      Decompress" \
-     "\n       -c      Write to stdout" \
-     "\n       -f      Force" \
-
-#define xzcat_trivial_usage \
-       "FILE"
-#define xzcat_full_usage "\n\n" \
-       "Decompress to stdout"
-
-#define cal_trivial_usage \
-       "[-jy] [[MONTH] YEAR]"
-#define cal_full_usage "\n\n" \
-       "Display a calendar\n" \
-     "\nOptions:" \
-     "\n       -j      Use julian dates" \
-     "\n       -y      Display the entire year" \
-
-#define cat_trivial_usage \
-       "[FILE]..."
-#define cat_full_usage "\n\n" \
-       "Concatenate FILEs and print them to stdout" \
-
-#define cat_example_usage \
-       "$ cat /proc/uptime\n" \
-       "110716.72 17.67"
-
-#define catv_trivial_usage \
-       "[-etv] [FILE]..."
-#define catv_full_usage "\n\n" \
-       "Display nonprinting characters as ^x or M-x\n" \
-     "\nOptions:" \
-     "\n       -e      End each line with $" \
-     "\n       -t      Show tabs as ^I" \
-     "\n       -v      Don't use ^x or M-x escapes" \
-
-#define chat_trivial_usage \
-       "EXPECT [SEND [EXPECT [SEND...]]]"
-#define chat_full_usage "\n\n" \
-       "Useful for interacting with a modem connected to stdin/stdout.\n" \
-       "A script consists of one or more \"expect-send\" pairs of strings,\n" \
-       "each pair is a pair of arguments. Example:\n" \
-       "chat '' ATZ OK ATD123456 CONNECT '' ogin: pppuser word: ppppass '~'" \
-
-#define chattr_trivial_usage \
-       "[-R] [-+=AacDdijsStTu] [-v VERSION] [FILE]..."
-#define chattr_full_usage "\n\n" \
-       "Change file attributes on an ext2 fs\n" \
-     "\nModifiers:" \
-     "\n       -       Remove attributes" \
-     "\n       +       Add attributes" \
-     "\n       =       Set attributes" \
-     "\nAttributes:" \
-     "\n       A       Don't track atime" \
-     "\n       a       Append mode only" \
-     "\n       c       Enable compress" \
-     "\n       D       Write dir contents synchronously" \
-     "\n       d       Don't backup with dump" \
-     "\n       i       Cannot be modified (immutable)" \
-     "\n       j       Write all data to journal first" \
-     "\n       s       Zero disk storage when deleted" \
-     "\n       S       Write file contents synchronously" \
-     "\n       t       Disable tail-merging of partial blocks with other files" \
-     "\n       u       Allow file to be undeleted" \
-     "\nOptions:" \
-     "\n       -R      Recurse" \
-     "\n       -v      Set the file's version/generation number" \
-
-#define chcon_trivial_usage \
-       "[OPTIONS] CONTEXT FILE..." \
-       "\n     chcon [OPTIONS] [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE..." \
-       IF_FEATURE_CHCON_LONG_OPTIONS( \
-       "\n     chcon [OPTIONS] --reference=RFILE FILE..." \
-       )
-#define chcon_full_usage "\n\n" \
-       "Change the security context of each FILE to CONTEXT\n" \
-       IF_FEATURE_CHCON_LONG_OPTIONS( \
-     "\n       -v,--verbose            Verbose" \
-     "\n       -c,--changes            Report changes made" \
-     "\n       -h,--no-dereference     Affect symlinks instead of their targets" \
-     "\n       -f,--silent,--quiet     Suppress most error messages" \
-     "\n       --reference=RFILE       Use RFILE's group instead of using a CONTEXT value" \
-     "\n       -u,--user=USER          Set user/role/type/range in the target" \
-     "\n       -r,--role=ROLE          security context" \
-     "\n       -t,--type=TYPE" \
-     "\n       -l,--range=RANGE" \
-     "\n       -R,--recursive          Recurse" \
-       ) \
-       IF_NOT_FEATURE_CHCON_LONG_OPTIONS( \
-     "\n       -v      Verbose" \
-     "\n       -c      Report changes made" \
-     "\n       -h      Affect symlinks instead of their targets" \
-     "\n       -f      Suppress most error messages" \
-     "\n       -u USER Set user/role/type/range in the target security context" \
-     "\n       -r ROLE" \
-     "\n       -t TYPE" \
-     "\n       -l RNG" \
-     "\n       -R      Recurse" \
-       )
-
-#define chmod_trivial_usage \
-       "[-R"IF_DESKTOP("cvf")"] MODE[,MODE]... FILE..."
-#define chmod_full_usage "\n\n" \
-       "Each MODE is one or more of the letters ugoa, one of the\n" \
-       "symbols +-= and one or more of the letters rwxst\n" \
-     "\nOptions:" \
-     "\n       -R      Recurse" \
-       IF_DESKTOP( \
-     "\n       -c      List changed files" \
-     "\n       -v      List all files" \
-     "\n       -f      Hide errors" \
-       )
-#define chmod_example_usage \
-       "$ ls -l /tmp/foo\n" \
-       "-rw-rw-r--    1 root     root            0 Apr 12 18:25 /tmp/foo\n" \
-       "$ chmod u+x /tmp/foo\n" \
-       "$ ls -l /tmp/foo\n" \
-       "-rwxrw-r--    1 root     root            0 Apr 12 18:25 /tmp/foo*\n" \
-       "$ chmod 444 /tmp/foo\n" \
-       "$ ls -l /tmp/foo\n" \
-       "-r--r--r--    1 root     root            0 Apr 12 18:25 /tmp/foo\n"
-
-#define chgrp_trivial_usage \
-       "[-RhLHP"IF_DESKTOP("cvf")"]... GROUP FILE..."
-#define chgrp_full_usage "\n\n" \
-       "Change the group membership of each FILE to GROUP\n" \
-     "\nOptions:" \
-     "\n       -R      Recurse" \
-     "\n       -h      Affect symlinks instead of symlink targets" \
-     "\n       -L      Traverse all symlinks to directories" \
-     "\n       -H      Traverse symlinks on command line only" \
-     "\n       -P      Don't traverse symlinks (default)" \
-       IF_DESKTOP( \
-     "\n       -c      List changed files" \
-     "\n       -v      Verbose" \
-     "\n       -f      Hide errors" \
-       )
-#define chgrp_example_usage \
-       "$ ls -l /tmp/foo\n" \
-       "-r--r--r--    1 andersen andersen        0 Apr 12 18:25 /tmp/foo\n" \
-       "$ chgrp root /tmp/foo\n" \
-       "$ ls -l /tmp/foo\n" \
-       "-r--r--r--    1 andersen root            0 Apr 12 18:25 /tmp/foo\n"
-
-#define chown_trivial_usage \
-       "[-RhLHP"IF_DESKTOP("cvf")"]... OWNER[<.|:>[GROUP]] FILE..."
-#define chown_full_usage "\n\n" \
-       "Change the owner and/or group of each FILE to OWNER and/or GROUP\n" \
-     "\nOptions:" \
-     "\n       -R      Recurse" \
-     "\n       -h      Affect symlinks instead of symlink targets" \
-     "\n       -L      Traverse all symlinks to directories" \
-     "\n       -H      Traverse symlinks on command line only" \
-     "\n       -P      Don't traverse symlinks (default)" \
-       IF_DESKTOP( \
-     "\n       -c      List changed files" \
-     "\n       -v      List all files" \
-     "\n       -f      Hide errors" \
-       )
-#define chown_example_usage \
-       "$ ls -l /tmp/foo\n" \
-       "-r--r--r--    1 andersen andersen        0 Apr 12 18:25 /tmp/foo\n" \
-       "$ chown root /tmp/foo\n" \
-       "$ ls -l /tmp/foo\n" \
-       "-r--r--r--    1 root     andersen        0 Apr 12 18:25 /tmp/foo\n" \
-       "$ chown root.root /tmp/foo\n" \
-       "ls -l /tmp/foo\n" \
-       "-r--r--r--    1 root     root            0 Apr 12 18:25 /tmp/foo\n"
-
-#define chpst_trivial_usage \
-       "[-vP012] [-u USER[:GRP]] [-U USER[:GRP]] [-e DIR]\n" \
-       "       [-/ DIR] [-n NICE] [-m BYTES] [-d BYTES] [-o N]\n" \
-       "       [-p N] [-f BYTES] [-c BYTES] PROG ARGS"
-#define chpst_full_usage "\n\n" \
-       "Change the process state, run PROG\n" \
-     "\nOptions:" \
-     "\n       -u USER[:GRP]   Set uid and gid" \
-     "\n       -U USER[:GRP]   Set $UID and $GID in environment" \
-     "\n       -e DIR          Set environment variables as specified by files" \
-     "\n                       in DIR: file=1st_line_of_file" \
-     "\n       -/ DIR          Chroot to DIR" \
-     "\n       -n NICE         Add NICE to nice value" \
-     "\n       -m BYTES        Same as -d BYTES -s BYTES -l BYTES" \
-     "\n       -d BYTES        Limit data segment" \
-     "\n       -o N            Limit number of open files per process" \
-     "\n       -p N            Limit number of processes per uid" \
-     "\n       -f BYTES        Limit output file sizes" \
-     "\n       -c BYTES        Limit core file size" \
-     "\n       -v              Verbose" \
-     "\n       -P              Create new process group" \
-     "\n       -0              Close stdin" \
-     "\n       -1              Close stdout" \
-     "\n       -2              Close stderr" \
-
-#define setuidgid_trivial_usage \
-       "USER PROG ARGS"
-#define setuidgid_full_usage "\n\n" \
-       "Set uid and gid to USER's uid and gid, drop supplementary group ids,\n" \
-       "run PROG"
-#define envuidgid_trivial_usage \
-       "USER PROG ARGS"
-#define envuidgid_full_usage "\n\n" \
-       "Set $UID to USER's uid and $GID to USER's gid, run PROG"
-#define envdir_trivial_usage \
-       "DIR PROG ARGS"
-#define envdir_full_usage "\n\n" \
-       "Set various environment variables as specified by files\n" \
-       "in the directory DIR, run PROG"
-#define softlimit_trivial_usage \
-       "[-a BYTES] [-m BYTES] [-d BYTES] [-s BYTES] [-l BYTES]\n" \
-       "       [-f BYTES] [-c BYTES] [-r BYTES] [-o N] [-p N] [-t N]\n" \
-       "       PROG ARGS"
-#define softlimit_full_usage "\n\n" \
-       "Set soft resource limits, then run PROG\n" \
-     "\nOptions:" \
-     "\n       -a BYTES        Limit total size of all segments" \
-     "\n       -m BYTES        Same as -d BYTES -s BYTES -l BYTES -a BYTES" \
-     "\n       -d BYTES        Limit data segment" \
-     "\n       -s BYTES        Limit stack segment" \
-     "\n       -l BYTES        Limit locked memory size" \
-     "\n       -o N            Limit number of open files per process" \
-     "\n       -p N            Limit number of processes per uid" \
-     "\nOptions controlling file sizes:" \
-     "\n       -f BYTES        Limit output file sizes" \
-     "\n       -c BYTES        Limit core file size" \
-     "\nEfficiency opts:" \
-     "\n       -r BYTES        Limit resident set size" \
-     "\n       -t N            Limit CPU time, process receives" \
-     "\n                       a SIGXCPU after N seconds" \
-
-#define chroot_trivial_usage \
-       "NEWROOT [PROG ARGS]"
-#define chroot_full_usage "\n\n" \
-       "Run PROG with root directory set to NEWROOT"
-#define chroot_example_usage \
-       "$ ls -l /bin/ls\n" \
-       "lrwxrwxrwx    1 root     root          12 Apr 13 00:46 /bin/ls -> /BusyBox\n" \
-       "# mount /dev/hdc1 /mnt -t minix\n" \
-       "# chroot /mnt\n" \
-       "# ls -l /bin/ls\n" \
-       "-rwxr-xr-x    1 root     root        40816 Feb  5 07:45 /bin/ls*\n"
-
-#define chvt_trivial_usage \
-       "N"
-#define chvt_full_usage "\n\n" \
-       "Change the foreground virtual terminal to /dev/ttyN"
-
-#define cksum_trivial_usage \
-       "FILES..."
-#define cksum_full_usage "\n\n" \
-       "Calculate the CRC32 checksums of FILES"
-
-#define clear_trivial_usage \
-       ""
-#define clear_full_usage "\n\n" \
-       "Clear screen"
-
-#define cmp_trivial_usage \
-       "[-l] [-s] FILE1 [FILE2" IF_DESKTOP(" [SKIP1 [SKIP2]]") "]"
-#define cmp_full_usage "\n\n" \
-       "Compare FILE1 with FILE2 (or stdin)\n" \
-     "\nOptions:" \
-     "\n       -l      Write the byte numbers (decimal) and values (octal)" \
-     "\n               for all differing bytes" \
-     "\n       -s      Quiet" \
-
-#define comm_trivial_usage \
-       "[-123] FILE1 FILE2"
-#define comm_full_usage "\n\n" \
-       "Compare FILE1 with FILE2\n" \
-     "\nOptions:" \
-     "\n       -1      Suppress lines unique to FILE1" \
-     "\n       -2      Suppress lines unique to FILE2" \
-     "\n       -3      Suppress lines common to both files" \
-
-#define bbconfig_trivial_usage \
-       ""
-#define bbconfig_full_usage "\n\n" \
-       "Print the config file used by busybox build"
-
-#define chrt_trivial_usage \
-       "[OPTIONS] [PRIO] [PID | PROG ARGS]"
-#define chrt_full_usage "\n\n" \
-       "Change scheduling priority and class for a process\n" \
-     "\nOptions:" \
-     "\n       -p      Operate on PID" \
-     "\n       -r      Set SCHED_RR class" \
-     "\n       -f      Set SCHED_FIFO class" \
-     "\n       -o      Set SCHED_OTHER class" \
-     "\n       -m      Show min/max priorities" \
-
-#define chrt_example_usage \
-       "$ chrt -r 4 sleep 900; x=$!\n" \
-       "$ chrt -f -p 3 $x\n" \
-       "You need CAP_SYS_NICE privileges to set scheduling attributes of a process"
-
-#define nice_trivial_usage \
-       "[-n ADJUST] [PROG ARGS]"
-#define nice_full_usage "\n\n" \
-       "Change scheduling priority, run PROG\n" \
-     "\nOptions:" \
-     "\n       -n ADJUST       Adjust priority by ADJUST" \
-
-#define renice_trivial_usage \
-       "{{-n INCREMENT} | PRIORITY} [[-p | -g | -u] ID...]"
-#define renice_full_usage "\n\n" \
-       "Change scheduling priority for a running process\n" \
-     "\nOptions:" \
-     "\n       -n      Adjust current nice value (smaller is faster)" \
-     "\n       -p      Process id(s) (default)" \
-     "\n       -g      Process group id(s)" \
-     "\n       -u      Process user name(s) and/or id(s)" \
-
-#define ionice_trivial_usage \
-       "[-c 1-3] [-n 0-7] [-p PID] [PROG]"
-#define ionice_full_usage "\n\n" \
-       "Change I/O priority and class\n" \
-     "\nOptions:" \
-     "\n       -c      Class. 1:realtime 2:best-effort 3:idle" \
-     "\n       -n      Priority" \
-
-#define cp_trivial_usage \
-       "[OPTIONS] SOURCE DEST"
-#define cp_full_usage "\n\n" \
-       "Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY\n" \
-     "\nOptions:" \
-     "\n       -a      Same as -dpR" \
-       IF_SELINUX( \
-     "\n       -c      Preserve security context" \
-       ) \
-     "\n       -R,-r   Recurse" \
-     "\n       -d,-P   Preserve symlinks (default if -R)" \
-     "\n       -L      Follow all symlinks" \
-     "\n       -H      Follow symlinks on command line" \
-     "\n       -p      Preserve file attributes if possible" \
-     "\n       -f      Overwrite" \
-     "\n       -i      Prompt before overwrite" \
-     "\n       -l,-s   Create (sym)links" \
-
-#define cpio_trivial_usage \
-       "[-dmvu] [-F FILE]" IF_FEATURE_CPIO_O(" [-H newc]") \
-       " [-ti"IF_FEATURE_CPIO_O("o")"]" IF_FEATURE_CPIO_P(" [-p DIR]")
-#define cpio_full_usage "\n\n" \
-       "Extract or list files from a cpio archive" \
-       IF_FEATURE_CPIO_O(", or" \
-     "\ncreate an archive" IF_FEATURE_CPIO_P(" (-o) or copy files (-p)") \
-               " using file list on stdin" \
-       ) \
-     "\n" \
-     "\nMain operation mode:" \
-     "\n       -t      List" \
-     "\n       -i      Extract" \
-       IF_FEATURE_CPIO_O( \
-     "\n       -o      Create (requires -H newc)" \
-       ) \
-       IF_FEATURE_CPIO_P( \
-     "\n       -p DIR  Copy files to DIR" \
-       ) \
-     "\nOptions:" \
-     "\n       -d      Make leading directories" \
-     "\n       -m      Preserve mtime" \
-     "\n       -v      Verbose" \
-     "\n       -u      Overwrite" \
-     "\n       -F FILE Input (-t,-i,-p) or output (-o) file" \
-       IF_FEATURE_CPIO_O( \
-     "\n       -H newc Archive format" \
-       ) \
-
-#define crond_trivial_usage \
-       "-fbS -l N " IF_FEATURE_CROND_D("-d N ") "-L LOGFILE -c DIR"
-#define crond_full_usage "\n\n" \
-       "       -f      Foreground" \
-     "\n       -b      Background (default)" \
-     "\n       -S      Log to syslog (default)" \
-     "\n       -l      Set log level. 0 is the most verbose, default 8" \
-       IF_FEATURE_CROND_D( \
-     "\n       -d      Set log level, log to stderr" \
-       ) \
-     "\n       -L      Log to file" \
-     "\n       -c      Working dir" \
-
-#define crontab_trivial_usage \
-       "[-c DIR] [-u USER] [-ler]|[FILE]"
-#define crontab_full_usage "\n\n" \
-       "       -c      Crontab directory" \
-     "\n       -u      User" \
-     "\n       -l      List crontab" \
-     "\n       -e      Edit crontab" \
-     "\n       -r      Delete crontab" \
-     "\n       FILE    Replace crontab by FILE ('-': stdin)" \
-
-#define cryptpw_trivial_usage \
-       "[OPTIONS] [PASSWORD] [SALT]"
-/* We do support -s, we just don't mention it */
-#define cryptpw_full_usage "\n\n" \
-       "Crypt the PASSWORD using crypt(3)\n" \
-     "\nOptions:" \
-       IF_LONG_OPTS( \
-     "\n       -P,--password-fd=N      Read password from fd N" \
-/*   "\n       -s,--stdin              Use stdin; like -P0" */ \
-     "\n       -m,--method=TYPE        Encryption method TYPE" \
-     "\n       -S,--salt=SALT" \
-       ) \
-       IF_NOT_LONG_OPTS( \
-     "\n       -P N    Read password from fd N" \
-/*   "\n       -s      Use stdin; like -P0" */ \
-     "\n       -m TYPE Encryption method TYPE" \
-     "\n       -S SALT" \
-       ) \
-
-/* mkpasswd is an alias to cryptpw */
-
-#define mkpasswd_trivial_usage \
-       "[OPTIONS] [PASSWORD] [SALT]"
-/* We do support -s, we just don't mention it */
-#define mkpasswd_full_usage "\n\n" \
-       "Crypt the PASSWORD using crypt(3)\n" \
-     "\nOptions:" \
-       IF_LONG_OPTS( \
-     "\n       -P,--password-fd=N      Read password from fd N" \
-/*   "\n       -s,--stdin              Use stdin; like -P0" */ \
-     "\n       -m,--method=TYPE        Encryption method TYPE" \
-     "\n       -S,--salt=SALT" \
-       ) \
-       IF_NOT_LONG_OPTS( \
-     "\n       -P N    Read password from fd N" \
-/*   "\n       -s      Use stdin; like -P0" */ \
-     "\n       -m TYPE Encryption method TYPE" \
-     "\n       -S SALT" \
-       ) \
-
-#define cttyhack_trivial_usage \
-       "PROG ARGS"
-#define cttyhack_full_usage "\n\n" \
-       "Give PROG a controlling tty if possible." \
-     "\nExample for /etc/inittab (for busybox init):" \
-     "\n       ::respawn:/bin/cttyhack /bin/sh" \
-     "\nGiving controlling tty to shell running with PID 1:" \
-     "\n       $ exec cttyhack sh" \
-     "\nStarting interactive shell from boot shell script:" \
-     "\n       setsid cttyhack sh" \
-
-#define cut_trivial_usage \
-       "[OPTIONS] [FILE]..."
-#define cut_full_usage "\n\n" \
-       "Print selected fields from each input FILE to stdout\n" \
-     "\nOptions:" \
-     "\n       -b LIST Output only bytes from LIST" \
-     "\n       -c LIST Output only characters from LIST" \
-     "\n       -d CHAR Use CHAR instead of tab as the field delimiter" \
-     "\n       -s      Output only the lines containing delimiter" \
-     "\n       -f N    Print only these fields" \
-     "\n       -n      Ignored" \
-
-#define cut_example_usage \
-       "$ echo \"Hello world\" | cut -f 1 -d ' '\n" \
-       "Hello\n" \
-       "$ echo \"Hello world\" | cut -f 2 -d ' '\n" \
-       "world\n"
-
-#define date_trivial_usage \
-       "[OPTIONS] [+FMT] [TIME]"
-#define date_full_usage "\n\n" \
-       "Display time (using +FMT), or set time\n" \
-     "\nOptions:" \
-       IF_NOT_LONG_OPTS( \
-     "\n       [-s] TIME       Set time to TIME" \
-     "\n       -u              Work in UTC (don't convert to local time)" \
-     "\n       -R              Output RFC-2822 compliant date string" \
-       ) IF_LONG_OPTS( \
-     "\n       [-s,--set] TIME Set time to TIME" \
-     "\n       -u,--utc        Work in UTC (don't convert to local time)" \
-     "\n       -R,--rfc-2822   Output RFC-2822 compliant date string" \
-       ) \
-       IF_FEATURE_DATE_ISOFMT( \
-     "\n       -I[SPEC]        Output ISO-8601 compliant date string" \
-     "\n                       SPEC='date' (default) for date only," \
-     "\n                       'hours', 'minutes', or 'seconds' for date and" \
-     "\n                       time to the indicated precision" \
-       ) IF_NOT_LONG_OPTS( \
-     "\n       -r FILE         Display last modification time of FILE" \
-     "\n       -d TIME         Display TIME, not 'now'" \
-       ) IF_LONG_OPTS( \
-     "\n       -r,--reference FILE     Display last modification time of FILE" \
-     "\n       -d,--date TIME  Display TIME, not 'now'" \
-       ) \
-       IF_FEATURE_DATE_ISOFMT( \
-     "\n       -D FMT          Use FMT for -d TIME conversion" \
-       ) \
-     "\n" \
-     "\nRecognized TIME formats:" \
-     "\n       hh:mm[:ss]" \
-     "\n       [YYYY.]MM.DD-hh:mm[:ss]" \
-     "\n       YYYY-MM-DD hh:mm[:ss]" \
-     "\n       [[[[[YY]YY]MM]DD]hh]mm[.ss]" \
-
-#define date_example_usage \
-       "$ date\n" \
-       "Wed Apr 12 18:52:41 MDT 2000\n"
-
-#define dc_trivial_usage \
-       "expression..."
-#define dc_full_usage "\n\n" \
-       "Tiny RPN calculator. Operations:\n" \
-       "+, add, -, sub, *, mul, /, div, %, mod, **, exp, and, or, not, eor,\n" \
-       "p - print top of the stack (without altering the stack),\n" \
-       "f - print entire stack, o - pop the value and set output radix\n" \
-       "(value must be 10 or 16).\n" \
-       "Examples: 'dc 2 2 add' -> 4, 'dc 8 8 * 2 2 + /' -> 16\n" \
-
-#define dc_example_usage \
-       "$ dc 2 2 + p\n" \
-       "4\n" \
-       "$ dc 8 8 \\* 2 2 + / p\n" \
-       "16\n" \
-       "$ dc 0 1 and p\n" \
-       "0\n" \
-       "$ dc 0 1 or p\n" \
-       "1\n" \
-       "$ echo 72 9 div 8 mul p | dc\n" \
-       "64\n"
-
-#define dd_trivial_usage \
-       "[if=FILE] [of=FILE] " IF_FEATURE_DD_IBS_OBS("[ibs=N] [obs=N] ") "[bs=N] [count=N] [skip=N]\n" \
-       "       [seek=N]" IF_FEATURE_DD_IBS_OBS(" [conv=notrunc|noerror|sync|fsync]")
-#define dd_full_usage "\n\n" \
-       "Copy a file with converting and formatting\n" \
-     "\nOptions:" \
-     "\n       if=FILE         Read from FILE instead of stdin" \
-     "\n       of=FILE         Write to FILE instead of stdout" \
-     "\n       bs=N            Read and write N bytes at a time" \
-       IF_FEATURE_DD_IBS_OBS( \
-     "\n       ibs=N           Read N bytes at a time" \
-       ) \
-       IF_FEATURE_DD_IBS_OBS( \
-     "\n       obs=N           Write N bytes at a time" \
-       ) \
-     "\n       count=N         Copy only N input blocks" \
-     "\n       skip=N          Skip N input blocks" \
-     "\n       seek=N          Skip N output blocks" \
-       IF_FEATURE_DD_IBS_OBS( \
-     "\n       conv=notrunc    Don't truncate output file" \
-     "\n       conv=noerror    Continue after read errors" \
-     "\n       conv=sync       Pad blocks with zeros" \
-     "\n       conv=fsync      Physically write data out before finishing" \
-       ) \
-     "\n" \
-     "\nNumbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024)," \
-     "\nMD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)" \
-
-#define dd_example_usage \
-       "$ dd if=/dev/zero of=/dev/ram1 bs=1M count=4\n" \
-       "4+0 records in\n" \
-       "4+0 records out\n"
-
-#define deallocvt_trivial_usage \
-       "[N]"
-#define deallocvt_full_usage "\n\n" \
-       "Deallocate unused virtual terminal /dev/ttyN"
-
-#define delgroup_trivial_usage \
-       IF_FEATURE_DEL_USER_FROM_GROUP("[USER] ")"GROUP"
-#define delgroup_full_usage "\n\n" \
-       "Delete group GROUP from the system" \
-       IF_FEATURE_DEL_USER_FROM_GROUP(" or user USER from group GROUP")
-
-#define deluser_trivial_usage \
-       "USER"
-#define deluser_full_usage "\n\n" \
-       "Delete USER from the system"
-
-#define depmod_trivial_usage NOUSAGE_STR
-#define depmod_full_usage ""
-
-#define devmem_trivial_usage \
-       "ADDRESS [WIDTH [VALUE]]"
-
-#define devmem_full_usage "\n\n" \
-       "Read/write from physical address\n" \
-     "\n       ADDRESS Address to act upon" \
-     "\n       WIDTH   Width (8/16/...)" \
-     "\n       VALUE   Data to be written" \
-
-#define devfsd_trivial_usage \
-       "mntpnt [-v]" IF_DEVFSD_FG_NP("[-fg][-np]")
-#define devfsd_full_usage "\n\n" \
-       "Manage devfs permissions and old device name symlinks\n" \
-     "\nOptions:" \
-     "\n       mntpnt  The mount point where devfs is mounted" \
-     "\n       -v      Print the protocol version numbers for devfsd" \
-     "\n               and the kernel-side protocol version and exit" \
-       IF_DEVFSD_FG_NP( \
-     "\n       -fg     Run in foreground" \
-     "\n       -np     Exit after parsing the configuration file" \
-     "\n               and processing synthetic REGISTER events," \
-     "\n               don't poll for events" \
-       )
-
-#define df_trivial_usage \
-       "[-Pk" \
-       IF_FEATURE_HUMAN_READABLE("mh") \
-       IF_FEATURE_DF_FANCY("ai] [-B SIZE") \
-       "] [FILESYSTEM]..."
-#define df_full_usage "\n\n" \
-       "Print filesystem usage statistics\n" \
-     "\nOptions:" \
-     "\n       -P      POSIX output format" \
-     "\n       -k      1024-byte blocks (default)" \
-       IF_FEATURE_HUMAN_READABLE( \
-     "\n       -m      1M-byte blocks" \
-     "\n       -h      Human readable (e.g. 1K 243M 2G)" \
-       ) \
-       IF_FEATURE_DF_FANCY( \
-     "\n       -a      Show all filesystems" \
-     "\n       -i      Inodes" \
-     "\n       -B SIZE Blocksize" \
-       ) \
-
-#define df_example_usage \
-       "$ df\n" \
-       "Filesystem           1K-blocks      Used Available Use% Mounted on\n" \
-       "/dev/sda3              8690864   8553540    137324  98% /\n" \
-       "/dev/sda1                64216     36364     27852  57% /boot\n" \
-       "$ df /dev/sda3\n" \
-       "Filesystem           1K-blocks      Used Available Use% Mounted on\n" \
-       "/dev/sda3              8690864   8553540    137324  98% /\n" \
-       "$ POSIXLY_CORRECT=sure df /dev/sda3\n" \
-       "Filesystem         512B-blocks      Used Available Use% Mounted on\n" \
-       "/dev/sda3             17381728  17107080    274648  98% /\n" \
-       "$ POSIXLY_CORRECT=yep df -P /dev/sda3\n" \
-       "Filesystem          512-blocks      Used Available Capacity Mounted on\n" \
-       "/dev/sda3             17381728  17107080    274648      98% /\n"
-
-#define dhcprelay_trivial_usage \
-       "CLIENT_IFACE[,CLIENT_IFACE2]... SERVER_IFACE [SERVER_IP]"
-#define dhcprelay_full_usage "\n\n" \
-       "Relay DHCP requests between clients and server" \
-
-#define diff_trivial_usage \
-       "[-abBdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] FILE1 FILE2"
-#define diff_full_usage "\n\n" \
-       "Compare files line by line and output the differences between them.\n" \
-       "This implementation supports unified diffs only.\n" \
-     "\nOptions:" \
-     "\n       -a      Treat all files as text" \
-     "\n       -b      Ignore changes in the amount of whitespace" \
-     "\n       -B      Ignore changes whose lines are all blank" \
-     "\n       -d      Try hard to find a smaller set of changes" \
-     "\n       -i      Ignore case differences" \
-     "\n       -L      Use LABEL instead of the filename in the unified header" \
-     "\n       -N      Treat absent files as empty" \
-     "\n       -q      Output only whether files differ" \
-     "\n       -r      Recurse" \
-     "\n       -S      Start with FILE when comparing directories" \
-     "\n       -T      Make tabs line up by prefixing a tab when necessary" \
-     "\n       -s      Report when two files are the same" \
-     "\n       -t      Expand tabs to spaces in output" \
-     "\n       -U      Output LINES lines of context" \
-     "\n       -w      Ignore all whitespace" \
-
-#define dirname_trivial_usage \
-       "FILENAME"
-#define dirname_full_usage "\n\n" \
-       "Strip non-directory suffix from FILENAME"
-#define dirname_example_usage \
-       "$ dirname /tmp/foo\n" \
-       "/tmp\n" \
-       "$ dirname /tmp/foo/\n" \
-       "/tmp\n"
-
-#define dmesg_trivial_usage \
-       "[-c] [-n LEVEL] [-s SIZE]"
-#define dmesg_full_usage "\n\n" \
-       "Print or control the kernel ring buffer\n" \
-     "\nOptions:" \
-     "\n       -c              Clear ring buffer after printing" \
-     "\n       -n LEVEL        Set console logging level" \
-     "\n       -s SIZE         Buffer size" \
-
-#define dnsd_trivial_usage \
-       "[-dvs] [-c CONFFILE] [-t TTL_SEC] [-p PORT] [-i ADDR]"
-#define dnsd_full_usage "\n\n" \
-       "Small static DNS server daemon\n" \
-     "\nOptions:" \
-     "\n       -c FILE Config file" \
-     "\n       -t SEC  TTL" \
-     "\n       -p PORT Listen on PORT" \
-     "\n       -i ADDR Listen on ADDR" \
-     "\n       -d      Daemonize" \
-     "\n       -v      Verbose" \
-     "\n       -s      Send successful replies only. Use this if you want" \
-     "\n               to use /etc/resolv.conf with two nameserver lines:" \
-     "\n                       nameserver DNSD_SERVER" \
-     "\n                       nameserver NORNAL_DNS_SERVER" \
-
-#define dos2unix_trivial_usage \
-       "[OPTIONS] [FILE]"
-#define dos2unix_full_usage "\n\n" \
-       "Convert FILE in-place from DOS to Unix format.\n" \
-       "When no file is given, use stdin/stdout.\n" \
-     "\nOptions:" \
-     "\n       -u      dos2unix" \
-     "\n       -d      unix2dos" \
-
-#define unix2dos_trivial_usage \
-       "[OPTIONS] [FILE]"
-#define unix2dos_full_usage "\n\n" \
-       "Convert FILE in-place from Unix to DOS format.\n" \
-       "When no file is given, use stdin/stdout.\n" \
-     "\nOptions:" \
-     "\n       -u      dos2unix" \
-     "\n       -d      unix2dos" \
-
-#define dpkg_trivial_usage \
-       "[-ilCPru] [-F OPT] PACKAGE"
-#define dpkg_full_usage "\n\n" \
-       "Install, remove and manage Debian packages\n" \
-     "\nOptions:" \
-       IF_LONG_OPTS( \
-     "\n       -i,--install    Install the package" \
-     "\n       -l,--list       List of installed packages" \
-     "\n       --configure     Configure an unpackaged package" \
-     "\n       -P,--purge      Purge all files of a package" \
-     "\n       -r,--remove     Remove all but the configuration files for a package" \
-     "\n       --unpack        Unpack a package, but don't configure it" \
-     "\n       --force-depends Ignore dependency problems" \
-     "\n       --force-confnew Overwrite existing config files when installing" \
-     "\n       --force-confold Keep old config files when installing" \
-       ) \
-       IF_NOT_LONG_OPTS( \
-     "\n       -i              Install the package" \
-     "\n       -l              List of installed packages" \
-     "\n       -C              Configure an unpackaged package" \
-     "\n       -P              Purge all files of a package" \
-     "\n       -r              Remove all but the configuration files for a package" \
-     "\n       -u              Unpack a package, but don't configure it" \
-     "\n       -F depends      Ignore dependency problems" \
-     "\n       -F confnew      Overwrite existing config files when installing" \
-     "\n       -F confold      Keep old config files when installing" \
-       )
-
-#define dpkg_deb_trivial_usage \
-       "[-cefxX] FILE [argument]"
-#define dpkg_deb_full_usage "\n\n" \
-       "Perform actions on Debian packages (.debs)\n" \
-     "\nOptions:" \
-     "\n       -c      List contents of filesystem tree" \
-     "\n       -e      Extract control files to [argument] directory" \
-     "\n       -f      Display control field name starting with [argument]" \
-     "\n       -x      Extract packages filesystem tree to directory" \
-     "\n       -X      Verbose extract" \
-
-#define dpkg_deb_example_usage \
-       "$ dpkg-deb -X ./busybox_0.48-1_i386.deb /tmp\n"
-
-#define du_trivial_usage \
-       "[-aHLdclsx" IF_FEATURE_HUMAN_READABLE("hm") "k] [FILE]..."
-#define du_full_usage "\n\n" \
-       "Summarize disk space used for each FILE and/or directory.\n" \
-       "Disk space is printed in units of " \
-       IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K("1024") \
-       IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K("512") \
-       " bytes.\n" \
-     "\nOptions:" \
-     "\n       -a      Show file sizes too" \
-     "\n       -L      Follow all symlinks" \
-     "\n       -H      Follow symlinks on command line" \
-     "\n       -d N    Limit output to directories (and files with -a) of depth < N" \
-     "\n       -c      Show grand total" \
-     "\n       -l      Count sizes many times if hard linked" \
-     "\n       -s      Display only a total for each argument" \
-     "\n       -x      Skip directories on different filesystems" \
-       IF_FEATURE_HUMAN_READABLE( \
-     "\n       -h      Sizes in human readable format (e.g., 1K 243M 2G )" \
-     "\n       -m      Sizes in megabytes" \
-       ) \
-     "\n       -k      Sizes in kilobytes" \
-                       IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(" (default)") \
-
-#define du_example_usage \
-       "$ du\n" \
-       "16      ./CVS\n" \
-       "12      ./kernel-patches/CVS\n" \
-       "80      ./kernel-patches\n" \
-       "12      ./tests/CVS\n" \
-       "36      ./tests\n" \
-       "12      ./scripts/CVS\n" \
-       "16      ./scripts\n" \
-       "12      ./docs/CVS\n" \
-       "104     ./docs\n" \
-       "2417    .\n"
-
-#define dumpkmap_trivial_usage \
-       "> keymap"
-#define dumpkmap_full_usage "\n\n" \
-       "Print a binary keyboard translation table to stdout"
-#define dumpkmap_example_usage \
-       "$ dumpkmap > keymap\n"
-
-#define dumpleases_trivial_usage \
-       "[-r|-a] [-f LEASEFILE]"
-#define dumpleases_full_usage "\n\n" \
-       "Display DHCP leases granted by udhcpd\n" \
-     "\nOptions:" \
-       IF_LONG_OPTS( \
-     "\n       -f,--file=FILE  Lease file" \
-     "\n       -r,--remaining  Show remaining time" \
-     "\n       -a,--absolute   Show expiration time" \
-       ) \
-       IF_NOT_LONG_OPTS( \
-     "\n       -f FILE Lease file" \
-     "\n       -r      Show remaining time" \
-     "\n       -a      Show expiration time" \
-       )
-
-/*
-#define e2fsck_trivial_usage \
-       "[-panyrcdfvstDFSV] [-b superblock] [-B blocksize] " \
-       "[-I inode_buffer_blocks] [-P process_inode_size] " \
-       "[-l|-L bad_blocks_file] [-C fd] [-j external_journal] " \
-       "[-E extended-options] device"
-#define e2fsck_full_usage "\n\n" \
-       "Check ext2/ext3 file system\n" \
-     "\nOptions:" \
-     "\n       -p              Automatic repair (no questions)" \
-     "\n       -n              Make no changes to the filesystem" \
-     "\n       -y              Assume 'yes' to all questions" \
-     "\n       -c              Check for bad blocks and add them to the badblock list" \
-     "\n       -f              Force checking even if filesystem is marked clean" \
-     "\n       -v              Verbose" \
-     "\n       -b superblock   Use alternative superblock" \
-     "\n       -B blocksize    Force blocksize when looking for superblock" \
-     "\n       -j journal      Set location of the external journal" \
-     "\n       -l file         Add to badblocks list" \
-     "\n       -L file         Set badblocks list" \
-*/
-
-#define echo_trivial_usage \
-       IF_FEATURE_FANCY_ECHO("[-neE] ") "[ARG]..."
-#define echo_full_usage "\n\n" \
-       "Print the specified ARGs to stdout" \
-       IF_FEATURE_FANCY_ECHO( "\n" \
-     "\nOptions:" \
-     "\n       -n      Suppress trailing newline" \
-     "\n       -e      Interpret backslash escapes (i.e., \\t=tab)" \
-     "\n       -E      Don't interpret backslash escapes (default)" \
-       )
-#define echo_example_usage \
-       "$ echo \"Erik is cool\"\n" \
-       "Erik is cool\n" \
-       IF_FEATURE_FANCY_ECHO("$ echo -e \"Erik\\nis\\ncool\"\n" \
-       "Erik\n" \
-       "is\n" \
-       "cool\n" \
-       "$ echo \"Erik\\nis\\ncool\"\n" \
-       "Erik\\nis\\ncool\n")
-
-#define eject_trivial_usage \
-       "[-t] [-T] [DEVICE]"
-#define eject_full_usage "\n\n" \
-       "Eject DEVICE or default /dev/cdrom\n" \
-     "\nOptions:" \
-       IF_FEATURE_EJECT_SCSI( \
-     "\n       -s      SCSI device" \
-       ) \
-     "\n       -t      Close tray" \
-     "\n       -T      Open/close tray (toggle)" \
-
-#define ed_trivial_usage ""
-#define ed_full_usage ""
-
-#define env_trivial_usage \
-       "[-iu] [-] [name=value]... [PROG ARGS]"
-#define env_full_usage "\n\n" \
-       "Print the current environment or run PROG after setting up\n" \
-       "the specified environment\n" \
-     "\nOptions:" \
-     "\n       -, -i   Start with an empty environment" \
-     "\n       -u      Remove variable from the environment" \
-
-#define ether_wake_trivial_usage \
-       "[-b] [-i iface] [-p aa:bb:cc:dd[:ee:ff]] MAC"
-#define ether_wake_full_usage "\n\n" \
-       "Send a magic packet to wake up sleeping machines.\n" \
-       "MAC must be a station address (00:11:22:33:44:55) or\n" \
-       "a hostname with a known 'ethers' entry.\n" \
-     "\nOptions:" \
-     "\n       -b              Send wake-up packet to the broadcast address" \
-     "\n       -i iface        Interface to use (default eth0)" \
-     "\n       -p pass         Append four or six byte password PW to the packet" \
-
-#define expand_trivial_usage \
-       "[-i] [-t N] [FILE]..."
-#define expand_full_usage "\n\n" \
-       "Convert tabs to spaces, writing to stdout\n" \
-     "\nOptions:" \
-       IF_FEATURE_EXPAND_LONG_OPTIONS( \
-     "\n       -i,--initial    Don't convert tabs after non blanks" \
-     "\n       -t,--tabs=N     Tabstops every N chars" \
-       ) \
-       IF_NOT_FEATURE_EXPAND_LONG_OPTIONS( \
-     "\n       -i      Don't convert tabs after non blanks" \
-     "\n       -t      Tabstops every N chars" \
-       )
-
-#define expr_trivial_usage \
-       "EXPRESSION"
-#define expr_full_usage "\n\n" \
-       "Print the value of EXPRESSION to stdout\n" \
-    "\n" \
-       "EXPRESSION may be:\n" \
-       "       ARG1 | ARG2     ARG1 if it is neither null nor 0, otherwise ARG2\n" \
-       "       ARG1 & ARG2     ARG1 if neither argument is null or 0, otherwise 0\n" \
-       "       ARG1 < ARG2     1 if ARG1 is less than ARG2, else 0. Similarly:\n" \
-       "       ARG1 <= ARG2\n" \
-       "       ARG1 = ARG2\n" \
-       "       ARG1 != ARG2\n" \
-       "       ARG1 >= ARG2\n" \
-       "       ARG1 > ARG2\n" \
-       "       ARG1 + ARG2     Sum of ARG1 and ARG2. Similarly:\n" \
-       "       ARG1 - ARG2\n" \
-       "       ARG1 * ARG2\n" \
-       "       ARG1 / ARG2\n" \
-       "       ARG1 % ARG2\n" \
-       "       STRING : REGEXP         Anchored pattern match of REGEXP in STRING\n" \
-       "       match STRING REGEXP     Same as STRING : REGEXP\n" \
-       "       substr STRING POS LENGTH Substring of STRING, POS counted from 1\n" \
-       "       index STRING CHARS      Index in STRING where any CHARS is found, or 0\n" \
-       "       length STRING           Length of STRING\n" \
-       "       quote TOKEN             Interpret TOKEN as a string, even if\n" \
-       "                               it is a keyword like 'match' or an\n" \
-       "                               operator like '/'\n" \
-       "       (EXPRESSION)            Value of EXPRESSION\n" \
-       "\n" \
-       "Beware that many operators need to be escaped or quoted for shells.\n" \
-       "Comparisons are arithmetic if both ARGs are numbers, else\n" \
-       "lexicographical. Pattern matches return the string matched between\n" \
-       "\\( and \\) or null; if \\( and \\) are not used, they return the number\n" \
-       "of characters matched or 0."
-
-#define fakeidentd_trivial_usage \
-       "[-fiw] [-b ADDR] [STRING]"
-#define fakeidentd_full_usage "\n\n" \
-       "Provide fake ident (auth) service\n" \
-     "\nOptions:" \
-     "\n       -f      Run in foreground" \
-     "\n       -i      Inetd mode" \
-     "\n       -w      Inetd 'wait' mode" \
-     "\n       -b ADDR Bind to specified address" \
-     "\n       STRING  Ident answer string (default: nobody)" \
-
-#define false_trivial_usage \
-       ""
-#define false_full_usage "\n\n" \
-       "Return an exit code of FALSE (1)"
-
-#define false_example_usage \
-       "$ false\n" \
-       "$ echo $?\n" \
-       "1\n"
-
-#define fbsplash_trivial_usage \
-       "-s IMGFILE [-c] [-d DEV] [-i INIFILE] [-f CMD]"
-#define fbsplash_full_usage "\n\n" \
-       "Options:" \
-     "\n       -s      Image" \
-     "\n       -c      Hide cursor" \
-     "\n       -d      Framebuffer device (default /dev/fb0)" \
-     "\n       -i      Config file (var=value):" \
-     "\n                       BAR_LEFT,BAR_TOP,BAR_WIDTH,BAR_HEIGHT" \
-     "\n                       BAR_R,BAR_G,BAR_B" \
-     "\n       -f      Control pipe (else exit after drawing image)" \
-     "\n                       commands: 'NN' (% for progress bar) or 'exit'" \
-
-#define fbset_trivial_usage \
-       "[OPTIONS] [MODE]"
-#define fbset_full_usage "\n\n" \
-       "Show and modify frame buffer settings"
-
-#define fbset_example_usage \
-       "$ fbset\n" \
-       "mode \"1024x768-76\"\n" \
-       "       # D: 78.653 MHz, H: 59.949 kHz, V: 75.694 Hz\n" \
-       "       geometry 1024 768 1024 768 16\n" \
-       "       timings 12714 128 32 16 4 128 4\n" \
-       "       accel false\n" \
-       "       rgba 5/11,6/5,5/0,0/0\n" \
-       "endmode\n"
-
-#define fdflush_trivial_usage \
-       "DEVICE"
-#define fdflush_full_usage "\n\n" \
-       "Force floppy disk drive to detect disk change"
-
-#define fdformat_trivial_usage \
-       "[-n] DEVICE"
-#define fdformat_full_usage "\n\n" \
-       "Format floppy disk\n" \
-     "\nOptions:" \
-     "\n       -n      Don't verify after format" \
-
-/* Looks like someone forgot to add this to config system */
-#ifndef ENABLE_FEATURE_FDISK_BLKSIZE
-# define ENABLE_FEATURE_FDISK_BLKSIZE 0
-# define IF_FEATURE_FDISK_BLKSIZE(a)
-#endif
-
-#define fdisk_trivial_usage \
-       "[-ul" IF_FEATURE_FDISK_BLKSIZE("s") "] " \
-       "[-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SSZ] DISK"
-#define fdisk_full_usage "\n\n" \
-       "Change partition table\n" \
-     "\nOptions:" \
-     "\n       -u              Start and End are in sectors (instead of cylinders)" \
-     "\n       -l              Show partition table for each DISK, then exit" \
-       IF_FEATURE_FDISK_BLKSIZE( \
-     "\n       -s              Show partition sizes in kb for each DISK, then exit" \
-       ) \
-     "\n       -b 2048         (for certain MO disks) use 2048-byte sectors" \
-     "\n       -C CYLINDERS    Set number of cylinders/heads/sectors" \
-     "\n       -H HEADS" \
-     "\n       -S SECTORS" \
-
-#define fgconsole_trivial_usage \
-       ""
-#define fgconsole_full_usage "\n\n" \
-       "Get active console"
-
-#define findfs_trivial_usage \
-       "LABEL=label or UUID=uuid"
-#define findfs_full_usage "\n\n" \
-       "Find a filesystem device based on a label or UUID"
-#define findfs_example_usage \
-       "$ findfs LABEL=MyDevice"
-
-#define flash_lock_trivial_usage \
-       "MTD_DEVICE OFFSET SECTORS"
-#define flash_lock_full_usage "\n\n" \
-       "Lock part or all of an MTD device. If SECTORS is -1, then all sectors\n" \
-       "will be locked, regardless of the value of OFFSET"
-
-#define flash_unlock_trivial_usage \
-       "MTD_DEVICE"
-#define flash_unlock_full_usage "\n\n" \
-       "Unlock an MTD device"
-
-#define flash_eraseall_trivial_usage \
-       "[-jq] MTD_DEVICE"
-#define flash_eraseall_full_usage "\n\n" \
-       "Erase an MTD device\n" \
-     "\nOptions:" \
-     "\n       -j      Format the device for jffs2" \
-     "\n       -q      Don't display progress messages" \
-
-#define flashcp_trivial_usage \
-       "-v FILE MTD_DEVICE"
-#define flashcp_full_usage "\n\n" \
-       "Copy an image to MTD device\n" \
-     "\nOptions:" \
-     "\n       -v      Verbose" \
-
-#define flock_trivial_usage \
-       "[-sxun] FD|{FILE [-c] PROG ARGS}"
-#define flock_full_usage "\n\n" \
-       "[Un]lock file descriptor, or lock FILE, run PROG\n" \
-     "\nOptions:" \
-     "\n       -s      Shared lock" \
-     "\n       -x      Exclusive lock (default)" \
-     "\n       -u      Unlock FD" \
-     "\n       -n      Fail rather than wait" \
-
-#define fold_trivial_usage \
-       "[-bs] [-w WIDTH] [FILE]..."
-#define fold_full_usage "\n\n" \
-       "Wrap input lines in each FILE (or stdin), writing to stdout\n" \
-     "\nOptions:" \
-     "\n       -b      Count bytes rather than columns" \
-     "\n       -s      Break at spaces" \
-     "\n       -w      Use WIDTH columns instead of 80" \
-
-#define free_trivial_usage \
-       ""
-#define free_full_usage "\n\n" \
-       "Display the amount of free and used system memory"
-#define free_example_usage \
-       "$ free\n" \
-       "              total         used         free       shared      buffers\n" \
-       "  Mem:       257628       248724         8904        59644        93124\n" \
-       " Swap:       128516         8404       120112\n" \
-       "Total:       386144       257128       129016\n" \
-
-#define freeramdisk_trivial_usage \
-       "DEVICE"
-#define freeramdisk_full_usage "\n\n" \
-       "Free all memory used by the specified ramdisk"
-#define freeramdisk_example_usage \
-       "$ freeramdisk /dev/ram2\n"
-
-#define fsck_trivial_usage \
-       "[-ANPRTV] [-C FD] [-t FSTYPE] [FS_OPTS] [BLOCKDEV]..."
-#define fsck_full_usage "\n\n" \
-       "Check and repair filesystems\n" \
-     "\nOptions:" \
-     "\n       -A      Walk /etc/fstab and check all filesystems" \
-     "\n       -N      Don't execute, just show what would be done" \
-     "\n       -P      With -A, check filesystems in parallel" \
-     "\n       -R      With -A, skip the root filesystem" \
-     "\n       -T      Don't show title on startup" \
-     "\n       -V      Verbose" \
-     "\n       -C n    Write status information to specified filedescriptor" \
-     "\n       -t TYPE List of filesystem types to check" \
-
-#define fsck_minix_trivial_usage \
-       "[-larvsmf] BLOCKDEV"
-#define fsck_minix_full_usage "\n\n" \
-       "Check MINIX filesystem\n" \
-     "\nOptions:" \
-     "\n       -l      List all filenames" \
-     "\n       -r      Perform interactive repairs" \
-     "\n       -a      Perform automatic repairs" \
-     "\n       -v      Verbose" \
-     "\n       -s      Output superblock information" \
-     "\n       -m      Show \"mode not cleared\" warnings" \
-     "\n       -f      Force file system check" \
-
-#define ftpd_trivial_usage \
-       "[-wvS] [-t N] [-T N] [DIR]"
-#define ftpd_full_usage "\n\n" \
-       "Anonymous FTP server\n" \
-       "\n" \
-       "ftpd should be used as an inetd service.\n" \
-       "ftpd's line for inetd.conf:\n" \
-       "       21 stream tcp nowait root ftpd ftpd /files/to/serve\n" \
-       "It also can be ran from tcpsvd:\n" \
-       "       tcpsvd -vE 0.0.0.0 21 ftpd /files/to/serve\n" \
-     "\nOptions:" \
-     "\n       -w      Allow upload" \
-     "\n       -v      Log to stderr" \
-     "\n       -S      Log to syslog" \
-     "\n       -t,-T   Idle and absolute timeouts" \
-     "\n       DIR     Change root to this directory" \
-
-#define ftpget_trivial_usage \
-       "[OPTIONS] HOST [LOCAL_FILE] REMOTE_FILE"
-#define ftpget_full_usage "\n\n" \
-       "Retrieve a remote file via FTP\n" \
-     "\nOptions:" \
-       IF_FEATURE_FTPGETPUT_LONG_OPTIONS( \
-     "\n       -c,--continue   Continue previous transfer" \
-     "\n       -v,--verbose    Verbose" \
-     "\n       -u,--username   Username" \
-     "\n       -p,--password   Password" \
-     "\n       -P,--port       Port number" \
-       ) \
-       IF_NOT_FEATURE_FTPGETPUT_LONG_OPTIONS( \
-     "\n       -c      Continue previous transfer" \
-     "\n       -v      Verbose" \
-     "\n       -u      Username" \
-     "\n       -p      Password" \
-     "\n       -P      Port number" \
-       )
-
-#define ftpput_trivial_usage \
-       "[OPTIONS] HOST [REMOTE_FILE] LOCAL_FILE"
-#define ftpput_full_usage "\n\n" \
-       "Store a local file on a remote machine via FTP\n" \
-     "\nOptions:" \
-       IF_FEATURE_FTPGETPUT_LONG_OPTIONS( \
-     "\n       -v,--verbose    Verbose" \
-     "\n       -u,--username   Username" \
-     "\n       -p,--password   Password" \
-     "\n       -P,--port       Port number" \
-       ) \
-       IF_NOT_FEATURE_FTPGETPUT_LONG_OPTIONS( \
-     "\n       -v      Verbose" \
-     "\n       -u      Username" \
-     "\n       -p      Password" \
-     "\n       -P      Port number" \
-       )
-
-#define fuser_trivial_usage \
-       "[OPTIONS] FILE or PORT/PROTO"
-#define fuser_full_usage "\n\n" \
-       "Find processes which use FILEs or PORTs\n" \
-     "\nOptions:" \
-     "\n       -m      Find processes which use same fs as FILEs" \
-     "\n       -4      Search only IPv4 space" \
-     "\n       -6      Search only IPv6 space" \
-     "\n       -s      Don't display PIDs" \
-     "\n       -k      Kill found processes" \
-     "\n       -SIGNAL Signal to send (default: KILL)" \
-
-#define getenforce_trivial_usage NOUSAGE_STR
-#define getenforce_full_usage ""
-
-#define getopt_trivial_usage \
-       "[OPTIONS]"
-#define getopt_full_usage "\n\n" \
-       "Options:" \
-       IF_LONG_OPTS( \
-     "\n       -a,--alternative                Allow long options starting with single -" \
-     "\n       -l,--longoptions=longopts       Long options to be recognized" \
-     "\n       -n,--name=progname              The name under which errors are reported" \
-     "\n       -o,--options=optstring          Short options to be recognized" \
-     "\n       -q,--quiet                      Disable error reporting by getopt(3)" \
-     "\n       -Q,--quiet-output               No normal output" \
-     "\n       -s,--shell=shell                Set shell quoting conventions" \
-     "\n       -T,--test                       Test for getopt(1) version" \
-     "\n       -u,--unquoted                   Don't quote the output" \
-       ) \
-       IF_NOT_LONG_OPTS( \
-     "\n       -a              Allow long options starting with single -" \
-     "\n       -l longopts     Long options to be recognized" \
-     "\n       -n progname     The name under which errors are reported" \
-     "\n       -o optstring    Short options to be recognized" \
-     "\n       -q              Disable error reporting by getopt(3)" \
-     "\n       -Q              No normal output" \
-     "\n       -s shell        Set shell quoting conventions" \
-     "\n       -T              Test for getopt(1) version" \
-     "\n       -u              Don't quote the output" \
-       )
-#define getopt_example_usage \
-       "$ cat getopt.test\n" \
-       "#!/bin/sh\n" \
-       "GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \\\n" \
-       "       -n 'example.busybox' -- \"$@\"`\n" \
-       "if [ $? != 0 ]; then exit 1; fi\n" \
-       "eval set -- \"$GETOPT\"\n" \
-       "while true; do\n" \
-       " case $1 in\n" \
-       "   -a|--a-long) echo \"Option a\"; shift;;\n" \
-       "   -b|--b-long) echo \"Option b, argument '$2'\"; shift 2;;\n" \
-       "   -c|--c-long)\n" \
-       "     case \"$2\" in\n" \
-       "       \"\") echo \"Option c, no argument\"; shift 2;;\n" \
-       "       *)  echo \"Option c, argument '$2'\"; shift 2;;\n" \
-       "     esac;;\n" \
-       "   --) shift; break;;\n" \
-       "   *) echo \"Internal error!\"; exit 1;;\n" \
-       " esac\n" \
-       "done\n"
-
-#define getsebool_trivial_usage \
-       "-a or getsebool boolean..."
-#define getsebool_full_usage "\n\n" \
-       "       -a      Show all selinux booleans"
-
-#define getty_trivial_usage \
-       "[OPTIONS] BAUD_RATE TTY [TERMTYPE]"
-#define getty_full_usage "\n\n" \
-       "Open a tty, prompt for a login name, then invoke /bin/login\n" \
-     "\nOptions:" \
-     "\n       -h              Enable hardware (RTS/CTS) flow control" \
-     "\n       -i              Don't display /etc/issue before running login" \
-     "\n       -L              Local line, don't do carrier detect" \
-     "\n       -m              Get baud rate from modem's CONNECT status message" \
-     "\n       -w              Wait for a CR or LF before sending /etc/issue" \
-     "\n       -n              Don't prompt the user for a login name" \
-     "\n       -f ISSUE_FILE   Display ISSUE_FILE instead of /etc/issue" \
-     "\n       -l LOGIN        Invoke LOGIN instead of /bin/login" \
-     "\n       -t SEC          Terminate after SEC if no username is read" \
-     "\n       -I INITSTR      Send INITSTR before anything else" \
-     "\n       -H HOST         Log HOST into the utmp file as the hostname" \
-
-#define gunzip_trivial_usage \
-       "[OPTIONS] [FILE]..."
-#define gunzip_full_usage "\n\n" \
-       "Decompress FILEs (or stdin)\n" \
-     "\nOptions:" \
-     "\n       -c      Write to stdout" \
-     "\n       -f      Force" \
-     "\n       -t      Test file integrity" \
-
-#define gunzip_example_usage \
-       "$ ls -la /tmp/BusyBox*\n" \
-       "-rw-rw-r--    1 andersen andersen   557009 Apr 11 10:55 /tmp/BusyBox-0.43.tar.gz\n" \
-       "$ gunzip /tmp/BusyBox-0.43.tar.gz\n" \
-       "$ ls -la /tmp/BusyBox*\n" \
-       "-rw-rw-r--    1 andersen andersen  1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar\n"
-
-#define gzip_trivial_usage \
-       "[OPTIONS] [FILE]..."
-#define gzip_full_usage "\n\n" \
-       "Compress FILEs (or stdin)\n" \
-     "\nOptions:" \
-     "\n       -d      Decompress" \
-     "\n       -c      Write to stdout" \
-     "\n       -f      Force" \
-
-#define gzip_example_usage \
-       "$ ls -la /tmp/busybox*\n" \
-       "-rw-rw-r--    1 andersen andersen  1761280 Apr 14 17:47 /tmp/busybox.tar\n" \
-       "$ gzip /tmp/busybox.tar\n" \
-       "$ ls -la /tmp/busybox*\n" \
-       "-rw-rw-r--    1 andersen andersen   554058 Apr 14 17:49 /tmp/busybox.tar.gz\n"
-
-#define halt_trivial_usage \
-       "[-d DELAY] [-n] [-f]" IF_FEATURE_WTMP(" [-w]")
-#define halt_full_usage "\n\n" \
-       "Halt the system\n" \
-     "\nOptions:" \
-     "\n       -d      Delay interval for halting" \
-     "\n       -n      No call to sync()" \
-     "\n       -f      Force halt (don't go through init)" \
-       IF_FEATURE_WTMP( \
-     "\n       -w      Only write a wtmp record" \
-       )
-
-#define hdparm_trivial_usage \
-       "[OPTIONS] [DEVICE]"
-#define hdparm_full_usage "\n\n" \
-       "Options:" \
-     "\n       -a      Get/set fs readahead" \
-     "\n       -A      Set drive read-lookahead flag (0/1)" \
-     "\n       -b      Get/set bus state (0 == off, 1 == on, 2 == tristate)" \
-     "\n       -B      Set Advanced Power Management setting (1-255)" \
-     "\n       -c      Get/set IDE 32-bit IO setting" \
-     "\n       -C      Check IDE power mode status" \
-       IF_FEATURE_HDPARM_HDIO_GETSET_DMA( \
-     "\n       -d      Get/set using_dma flag") \
-     "\n       -D      Enable/disable drive defect-mgmt" \
-     "\n       -f      Flush buffer cache for device on exit" \
-     "\n       -g      Display drive geometry" \
-     "\n       -h      Display terse usage information" \
-       IF_FEATURE_HDPARM_GET_IDENTITY( \
-     "\n       -i      Display drive identification") \
-       IF_FEATURE_HDPARM_GET_IDENTITY( \
-     "\n       -I      Detailed/current information directly from drive") \
-     "\n       -k      Get/set keep_settings_over_reset flag (0/1)" \
-     "\n       -K      Set drive keep_features_over_reset flag (0/1)" \
-     "\n       -L      Set drive doorlock (0/1) (removable harddisks only)" \
-     "\n       -m      Get/set multiple sector count" \
-     "\n       -n      Get/set ignore-write-errors flag (0/1)" \
-     "\n       -p      Set PIO mode on IDE interface chipset (0,1,2,3,4,...)" \
-     "\n       -P      Set drive prefetch count" \
-/*   "\n       -q      Change next setting quietly" - not supported ib bbox */ \
-     "\n       -Q      Get/set DMA tagged-queuing depth (if supported)" \
-     "\n       -r      Get/set readonly flag (DANGEROUS to set)" \
-       IF_FEATURE_HDPARM_HDIO_SCAN_HWIF( \
-     "\n       -R      Register an IDE interface (DANGEROUS)") \
-     "\n       -S      Set standby (spindown) timeout" \
-     "\n       -t      Perform device read timings" \
-     "\n       -T      Perform cache read timings" \
-     "\n       -u      Get/set unmaskirq flag (0/1)" \
-       IF_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF( \
-     "\n       -U      Unregister an IDE interface (DANGEROUS)") \
-     "\n       -v      Defaults; same as -mcudkrag for IDE drives" \
-     "\n       -V      Display program version and exit immediately" \
-       IF_FEATURE_HDPARM_HDIO_DRIVE_RESET( \
-     "\n       -w      Perform device reset (DANGEROUS)") \
-     "\n       -W      Set drive write-caching flag (0/1) (DANGEROUS)" \
-       IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF( \
-     "\n       -x      Tristate device for hotswap (0/1) (DANGEROUS)") \
-     "\n       -X      Set IDE xfer mode (DANGEROUS)" \
-     "\n       -y      Put IDE drive in standby mode" \
-     "\n       -Y      Put IDE drive to sleep" \
-     "\n       -Z      Disable Seagate auto-powersaving mode" \
-     "\n       -z      Reread partition table" \
-
-#define head_trivial_usage \
-       "[OPTIONS] [FILE]..."
-#define head_full_usage "\n\n" \
-       "Print first 10 lines of each FILE (or stdin) to stdout.\n" \
-       "With more than one FILE, precede each with a filename header.\n" \
-     "\nOptions:" \
-     "\n       -n N[kbm]       Print first N lines" \
-       IF_FEATURE_FANCY_HEAD( \
-     "\n       -c N[kbm]       Print first N bytes" \
-     "\n       -q              Never print headers" \
-     "\n       -v              Always print headers" \
-       ) \
-     "\n" \
-     "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)." \
-
-#define head_example_usage \
-       "$ head -n 2 /etc/passwd\n" \
-       "root:x:0:0:root:/root:/bin/bash\n" \
-       "daemon:x:1:1:daemon:/usr/sbin:/bin/sh\n"
-
-#define tail_trivial_usage \
-       "[OPTIONS] [FILE]..."
-#define tail_full_usage "\n\n" \
-       "Print last 10 lines of each FILE (or stdin) to stdout.\n" \
-       "With more than one FILE, precede each with a filename header.\n" \
-     "\nOptions:" \
-     "\n       -f              Print data as file grows" \
-       IF_FEATURE_FANCY_TAIL( \
-     "\n       -s SECONDS      Wait SECONDS between reads with -f" \
-       ) \
-     "\n       -n N[kbm]       Print last N lines" \
-       IF_FEATURE_FANCY_TAIL( \
-     "\n       -c N[kbm]       Print last N bytes" \
-     "\n       -q              Never print headers" \
-     "\n       -v              Always print headers" \
-     "\n" \
-     "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)." \
-     "\nIf N starts with a '+', output begins with the Nth item from the start" \
-     "\nof each file, not from the end." \
-       ) \
-
-#define tail_example_usage \
-       "$ tail -n 1 /etc/resolv.conf\n" \
-       "nameserver 10.0.0.1\n"
-
-#define hexdump_trivial_usage \
-       "[-bcCdefnosvx" IF_FEATURE_HEXDUMP_REVERSE("R") "] [FILE]..."
-#define hexdump_full_usage "\n\n" \
-       "Display FILEs (or stdin) in a user specified format\n" \
-     "\nOptions:" \
-     "\n       -b              One-byte octal display" \
-     "\n       -c              One-byte character display" \
-     "\n       -C              Canonical hex+ASCII, 16 bytes per line" \
-     "\n       -d              Two-byte decimal display" \
-     "\n       -e FORMAT STRING" \
-     "\n       -f FORMAT FILE" \
-     "\n       -n LENGTH       Interpret only LENGTH bytes of input" \
-     "\n       -o              Two-byte octal display" \
-     "\n       -s OFFSET       Skip OFFSET bytes" \
-     "\n       -v              Display all input data" \
-     "\n       -x              Two-byte hexadecimal display" \
-       IF_FEATURE_HEXDUMP_REVERSE( \
-     "\n       -R              Reverse of 'hexdump -Cv'") \
-
-#define hd_trivial_usage \
-       "FILE..."
-#define hd_full_usage "\n\n" \
-       "hd is an alias for hexdump -C"
-
-#define hostid_trivial_usage \
-       ""
-#define hostid_full_usage "\n\n" \
-       "Print out a unique 32-bit identifier for the machine"
-
-#define hostname_trivial_usage \
-       "[OPTIONS] [HOSTNAME | -F FILE]"
-#define hostname_full_usage "\n\n" \
-       "Get or set hostname or DNS domain name\n" \
-     "\nOptions:" \
-     "\n       -s      Short" \
-     "\n       -i      Addresses for the hostname" \
-     "\n       -d      DNS domain name" \
-     "\n       -f      Fully qualified domain name" \
-     "\n       -F FILE Use FILE's content as hostname" \
-
-#define hostname_example_usage \
-       "$ hostname\n" \
-       "sage\n"
-
-#define dnsdomainname_trivial_usage NOUSAGE_STR
-#define dnsdomainname_full_usage ""
-
-#define httpd_trivial_usage \
-       "[-ifv[v]]" \
-       " [-c CONFFILE]" \
-       " [-p [IP:]PORT]" \
-       IF_FEATURE_HTTPD_SETUID(" [-u USER[:GRP]]") \
-       IF_FEATURE_HTTPD_BASIC_AUTH(" [-r REALM]") \
-       " [-h HOME]\n" \
-       "or httpd -d/-e" IF_FEATURE_HTTPD_AUTH_MD5("/-m") " STRING"
-#define httpd_full_usage "\n\n" \
-       "Listen for incoming HTTP requests\n" \
-     "\nOptions:" \
-     "\n       -i              Inetd mode" \
-     "\n       -f              Don't daemonize" \
-     "\n       -v[v]           Verbose" \
-     "\n       -p [IP:]PORT    Bind to ip:port (default *:80)" \
-       IF_FEATURE_HTTPD_SETUID( \
-     "\n       -u USER[:GRP]   Set uid/gid after binding to port") \
-       IF_FEATURE_HTTPD_BASIC_AUTH( \
-     "\n       -r REALM        Authentication Realm for Basic Authentication") \
-     "\n       -h HOME         Home directory (default .)" \
-     "\n       -c FILE         Configuration file (default {/etc,HOME}/httpd.conf)" \
-       IF_FEATURE_HTTPD_AUTH_MD5( \
-     "\n       -m STRING       MD5 crypt STRING") \
-     "\n       -e STRING       HTML encode STRING" \
-     "\n       -d STRING       URL decode STRING" \
-
-#define hwclock_trivial_usage \
-       IF_FEATURE_HWCLOCK_LONG_OPTIONS( \
-       "[-r|--show] [-s|--hctosys] [-w|--systohc]" \
-       " [-l|--localtime] [-u|--utc]" \
-       " [-f FILE]" \
-       ) \
-       IF_NOT_FEATURE_HWCLOCK_LONG_OPTIONS( \
-       "[-r] [-s] [-w] [-l] [-u] [-f FILE]" \
-       )
-#define hwclock_full_usage "\n\n" \
-       "Query and set hardware clock (RTC)\n" \
-     "\nOptions:" \
-     "\n       -r      Show hardware clock time" \
-     "\n       -s      Set system time from hardware clock" \
-     "\n       -w      Set hardware clock to system time" \
-     "\n       -u      Hardware clock is in UTC" \
-     "\n       -l      Hardware clock is in local time" \
-     "\n       -f FILE Use specified device (e.g. /dev/rtc2)" \
-
-#define id_trivial_usage \
-       "[OPTIONS] [USER]"
-#define id_full_usage "\n\n" \
-       "Print information about USER or the current user\n" \
-     "\nOptions:" \
-       IF_SELINUX( \
-     "\n       -Z      Print the security context" \
-       ) \
-     "\n       -u      Print user ID" \
-     "\n       -g      Print group ID" \
-     "\n       -G      Print supplementary group IDs" \
-     "\n       -n      Print name instead of a number" \
-     "\n       -r      Print real user ID instead of effective ID" \
-
-#define id_example_usage \
-       "$ id\n" \
-       "uid=1000(andersen) gid=1000(andersen)\n"
-
-#define ifconfig_trivial_usage \
-       IF_FEATURE_IFCONFIG_STATUS("[-a]") " interface [address]"
-#define ifconfig_full_usage "\n\n" \
-       "Configure a network interface\n" \
-     "\nOptions:" \
-     "\n" \
-       IF_FEATURE_IPV6( \
-       "       [add ADDRESS[/PREFIXLEN]]\n") \
-       IF_FEATURE_IPV6( \
-       "       [del ADDRESS[/PREFIXLEN]]\n") \
-       "       [[-]broadcast [ADDRESS]] [[-]pointopoint [ADDRESS]]\n" \
-       "       [netmask ADDRESS] [dstaddr ADDRESS]\n" \
-       IF_FEATURE_IFCONFIG_SLIP( \
-       "       [outfill NN] [keepalive NN]\n") \
-       "       " IF_FEATURE_IFCONFIG_HW("[hw ether" IF_FEATURE_HWIB("|infiniband")" ADDRESS] ") "[metric NN] [mtu NN]\n" \
-       "       [[-]trailers] [[-]arp] [[-]allmulti]\n" \
-       "       [multicast] [[-]promisc] [txqueuelen NN] [[-]dynamic]\n" \
-       IF_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ( \
-       "       [mem_start NN] [io_addr NN] [irq NN]\n") \
-       "       [up|down] ..."
-
-#define ifenslave_trivial_usage \
-       "[-cdf] MASTER_IFACE SLAVE_IFACE..."
-#define ifenslave_full_usage "\n\n" \
-       "Configure network interfaces for parallel routing\n" \
-     "\nOptions:" \
-     "\n       -c, --change-active     Change active slave" \
-     "\n       -d, --detach            Remove slave interface from bonding device" \
-     "\n       -f, --force             Force, even if interface is not Ethernet" \
-/*   "\n       -r, --receive-slave     Create a receive-only slave" */
-
-#define ifenslave_example_usage \
-       "To create a bond device, simply follow these three steps:\n" \
-       "- ensure that the required drivers are properly loaded:\n" \
-       "  # modprobe bonding ; modprobe <3c59x|eepro100|pcnet32|tulip|...>\n" \
-       "- assign an IP address to the bond device:\n" \
-       "  # ifconfig bond0 <addr> netmask <mask> broadcast <bcast>\n" \
-       "- attach all the interfaces you need to the bond device:\n" \
-       "  # ifenslave bond0 eth0 eth1 eth2\n" \
-       "  If bond0 didn't have a MAC address, it will take eth0's. Then, all\n" \
-       "  interfaces attached AFTER this assignment will get the same MAC addr.\n\n" \
-       "  To detach a dead interface without setting the bond device down:\n" \
-       "  # ifenslave -d bond0 eth1\n\n" \
-       "  To set the bond device down and automatically release all the slaves:\n" \
-       "  # ifconfig bond0 down\n\n" \
-       "  To change active slave:\n" \
-       "  # ifenslave -c bond0 eth0\n" \
-
-#define ifplugd_trivial_usage \
-       "[OPTIONS]"
-#define ifplugd_full_usage "\n\n" \
-       "Network interface plug detection daemon\n" \
-     "\nOptions:" \
-     "\n       -n              Don't daemonize" \
-     "\n       -s              Don't log to syslog" \
-     "\n       -i IFACE        Interface" \
-     "\n       -f/-F           Treat link detection error as link down/link up" \
-     "\n                       (otherwise exit on error)" \
-     "\n       -a              Don't up interface at each link probe" \
-     "\n       -M              Monitor creation/destruction of interface" \
-     "\n                       (otherwise it must exist)" \
-     "\n       -r PROG         Script to run" \
-     "\n       -x ARG          Extra argument for script" \
-     "\n       -I              Don't exit on nonzero exit code from script" \
-     "\n       -p              Don't run script on daemon startup" \
-     "\n       -q              Don't run script on daemon quit" \
-     "\n       -l              Run script on startup even if no cable is detected" \
-     "\n       -t SECS         Poll time in seconds" \
-     "\n       -u SECS         Delay before running script after link up" \
-     "\n       -d SECS         Delay after link down" \
-     "\n       -m MODE         API mode (mii, priv, ethtool, wlan, iff, auto)" \
-     "\n       -k              Kill running daemon" \
-
-#define ifup_trivial_usage \
-       "[-ain"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] IFACE..."
-#define ifup_full_usage "\n\n" \
-       "Options:" \
-     "\n       -a      De/configure all interfaces automatically" \
-     "\n       -i FILE Use FILE for interface definitions" \
-     "\n       -n      Print out what would happen, but don't do it" \
-       IF_FEATURE_IFUPDOWN_MAPPING( \
-     "\n               (note: doesn't disable mappings)" \
-     "\n       -m      Don't run any mappings" \
-       ) \
-     "\n       -v      Print out what would happen before doing it" \
-     "\n       -f      Force de/configuration" \
-
-#define ifdown_trivial_usage \
-       "[-ain"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] ifaces..."
-#define ifdown_full_usage "\n\n" \
-       "Options:" \
-     "\n       -a      De/configure all interfaces automatically" \
-     "\n       -i FILE Use FILE for interface definitions" \
-     "\n       -n      Print out what would happen, but don't do it" \
-       IF_FEATURE_IFUPDOWN_MAPPING( \
-     "\n               (note: doesn't disable mappings)" \
-     "\n       -m      Don't run any mappings" \
-       ) \
-     "\n       -v      Print out what would happen before doing it" \
-     "\n       -f      Force de/configuration" \
-
-#define inetd_trivial_usage \
-       "[-fe] [-q N] [-R N] [CONFFILE]"
-#define inetd_full_usage "\n\n" \
-       "Listen for network connections and launch programs\n" \
-     "\nOptions:" \
-     "\n       -f      Run in foreground" \
-     "\n       -e      Log to stderr" \
-     "\n       -q N    Socket listen queue (default: 128)" \
-     "\n       -R N    Pause services after N connects/min" \
-     "\n               (default: 0 - disabled)" \
-
-#define init_trivial_usage \
-       ""
-#define init_full_usage "\n\n" \
-       "Init is the parent of all processes"
-
-#define init_notes_usage \
-"This version of init is designed to be run only by the kernel.\n" \
-"\n" \
-"BusyBox init doesn't support multiple runlevels. The runlevels field of\n" \
-"the /etc/inittab file is completely ignored by BusyBox init. If you want\n" \
-"runlevels, use sysvinit.\n" \
-"\n" \
-"BusyBox init works just fine without an inittab. If no inittab is found,\n" \
-"it has the following default behavior:\n" \
-"\n" \
-"      ::sysinit:/etc/init.d/rcS\n" \
-"      ::askfirst:/bin/sh\n" \
-"      ::ctrlaltdel:/sbin/reboot\n" \
-"      ::shutdown:/sbin/swapoff -a\n" \
-"      ::shutdown:/bin/umount -a -r\n" \
-"      ::restart:/sbin/init\n" \
-"\n" \
-"if it detects that /dev/console is _not_ a serial console, it will also run:\n" \
-"\n" \
-"      tty2::askfirst:/bin/sh\n" \
-"      tty3::askfirst:/bin/sh\n" \
-"      tty4::askfirst:/bin/sh\n" \
-"\n" \
-"If you choose to use an /etc/inittab file, the inittab entry format is as follows:\n" \
-"\n" \
-"      <id>:<runlevels>:<action>:<process>\n" \
-"\n" \
-"      <id>:\n" \
-"\n" \
-"              WARNING: This field has a non-traditional meaning for BusyBox init!\n" \
-"              The id field is used by BusyBox init to specify the controlling tty for\n" \
-"              the specified process to run on. The contents of this field are\n" \
-"              appended to \"/dev/\" and used as-is. There is no need for this field to\n" \
-"              be unique, although if it isn't you may have strange results. If this\n" \
-"              field is left blank, the controlling tty is set to the console. Also\n" \
-"              note that if BusyBox detects that a serial console is in use, then only\n" \
-"              entries whose controlling tty is either the serial console or /dev/null\n" \
-"              will be run. BusyBox init does nothing with utmp. We don't need no\n" \
-"              stinkin' utmp.\n" \
-"\n" \
-"      <runlevels>:\n" \
-"\n" \
-"              The runlevels field is completely ignored.\n" \
-"\n" \
-"      <action>:\n" \
-"\n" \
-"              Valid actions include: sysinit, respawn, askfirst, wait,\n" \
-"              once, restart, ctrlaltdel, and shutdown.\n" \
-"\n" \
-"              The available actions can be classified into two groups: actions\n" \
-"              that are run only once, and actions that are re-run when the specified\n" \
-"              process exits.\n" \
-"\n" \
-"              Run only-once actions:\n" \
-"\n" \
-"                      'sysinit' is the first item run on boot. init waits until all\n" \
-"                      sysinit actions are completed before continuing. Following the\n" \
-"                      completion of all sysinit actions, all 'wait' actions are run.\n" \
-"                      'wait' actions, like 'sysinit' actions, cause init to wait until\n" \
-"                      the specified task completes. 'once' actions are asynchronous,\n" \
-"                      therefore, init does not wait for them to complete. 'restart' is\n" \
-"                      the action taken to restart the init process. By default this should\n" \
-"                      simply run /sbin/init, but can be a script which runs pivot_root or it\n" \
-"                      can do all sorts of other interesting things. The 'ctrlaltdel' init\n" \
-"                      actions are run when the system detects that someone on the system\n" \
-"                      console has pressed the CTRL-ALT-DEL key combination. Typically one\n" \
-"                      wants to run 'reboot' at this point to cause the system to reboot.\n" \
-"                      Finally the 'shutdown' action specifies the actions to taken when\n" \
-"                      init is told to reboot. Unmounting filesystems and disabling swap\n" \
-"                      is a very good here.\n" \
-"\n" \
-"              Run repeatedly actions:\n" \
-"\n" \
-"                      'respawn' actions are run after the 'once' actions. When a process\n" \
-"                      started with a 'respawn' action exits, init automatically restarts\n" \
-"                      it. Unlike sysvinit, BusyBox init does not stop processes from\n" \
-"                      respawning out of control. The 'askfirst' actions acts just like\n" \
-"                      respawn, except that before running the specified process it\n" \
-"                      displays the line \"Please press Enter to activate this console.\"\n" \
-"                      and then waits for the user to press enter before starting the\n" \
-"                      specified process.\n" \
-"\n" \
-"              Unrecognized actions (like initdefault) will cause init to emit an\n" \
-"              error message, and then go along with its business. All actions are\n" \
-"              run in the order they appear in /etc/inittab.\n" \
-"\n" \
-"      <process>:\n" \
-"\n" \
-"              Specifies the process to be executed and its command line.\n" \
-"\n" \
-"Example /etc/inittab file:\n" \
-"\n" \
-"      # This is run first except when booting in single-user mode\n" \
-"      #\n" \
-"      ::sysinit:/etc/init.d/rcS\n" \
-"      \n" \
-"      # /bin/sh invocations on selected ttys\n" \
-"      #\n" \
-"      # Start an \"askfirst\" shell on the console (whatever that may be)\n" \
-"      ::askfirst:-/bin/sh\n" \
-"      # Start an \"askfirst\" shell on /dev/tty2-4\n" \
-"      tty2::askfirst:-/bin/sh\n" \
-"      tty3::askfirst:-/bin/sh\n" \
-"      tty4::askfirst:-/bin/sh\n" \
-"      \n" \
-"      # /sbin/getty invocations for selected ttys\n" \
-"      #\n" \
-"      tty4::respawn:/sbin/getty 38400 tty4\n" \
-"      tty5::respawn:/sbin/getty 38400 tty5\n" \
-"      \n" \
-"      \n" \
-"      # Example of how to put a getty on a serial line (for a terminal)\n" \
-"      #\n" \
-"      #::respawn:/sbin/getty -L ttyS0 9600 vt100\n" \
-"      #::respawn:/sbin/getty -L ttyS1 9600 vt100\n" \
-"      #\n" \
-"      # Example how to put a getty on a modem line\n" \
-"      #::respawn:/sbin/getty 57600 ttyS2\n" \
-"      \n" \
-"      # Stuff to do when restarting the init process\n" \
-"      ::restart:/sbin/init\n" \
-"      \n" \
-"      # Stuff to do before rebooting\n" \
-"      ::ctrlaltdel:/sbin/reboot\n" \
-"      ::shutdown:/bin/umount -a -r\n" \
-"      ::shutdown:/sbin/swapoff -a\n"
-
-#define inotifyd_trivial_usage \
-       "PROG FILE1[:MASK]..."
-#define inotifyd_full_usage "\n\n" \
-       "Run PROG on filesystem changes." \
-     "\nWhen a filesystem event matching MASK occurs on FILEn," \
-     "\nPROG ACTUAL_EVENTS FILEn [SUBFILE] is run." \
-     "\nEvents:" \
-     "\n       a       File is accessed" \
-     "\n       c       File is modified" \
-     "\n       e       Metadata changed" \
-     "\n       w       Writable file is closed" \
-     "\n       0       Unwritable file is closed" \
-     "\n       r       File is opened" \
-     "\n       D       File is deleted" \
-     "\n       M       File is moved" \
-     "\n       u       Backing fs is unmounted" \
-     "\n       o       Event queue overflowed" \
-     "\n       x       File can't be watched anymore" \
-     "\nIf watching a directory:" \
-     "\n       m       Subfile is moved into dir" \
-     "\n       y       Subfile is moved out of dir" \
-     "\n       n       Subfile is created" \
-     "\n       d       Subfile is deleted" \
-     "\n" \
-     "\ninotifyd waits for PROG to exit." \
-     "\nWhen x event happens for all FILEs, inotifyd exits." \
-
-/* -v, -b, -c are ignored */
-#define install_trivial_usage \
-       "[-cdDsp] [-o USER] [-g GRP] [-m MODE] [SOURCE]... DEST"
-#define install_full_usage "\n\n" \
-       "Copy files and set attributes\n" \
-     "\nOptions:" \
-     "\n       -c      Just copy (default)" \
-     "\n       -d      Create directories" \
-     "\n       -D      Create leading target directories" \
-     "\n       -s      Strip symbol table" \
-     "\n       -p      Preserve date" \
-     "\n       -o USER Set ownership" \
-     "\n       -g GRP  Set group ownership" \
-     "\n       -m MODE Set permissions" \
-       IF_SELINUX( \
-     "\n       -Z      Set security context" \
-       )
-
-/* would need to make the " | " optional depending on more than one selected: */
-#define ip_trivial_usage \
-       "[OPTIONS] {" \
-       IF_FEATURE_IP_ADDRESS("address | ") \
-       IF_FEATURE_IP_ROUTE("route | ") \
-       IF_FEATURE_IP_LINK("link | ") \
-       IF_FEATURE_IP_TUNNEL("tunnel | ") \
-       IF_FEATURE_IP_RULE("rule") \
-       "} {COMMAND}"
-#define ip_full_usage "\n\n" \
-       "ip [OPTIONS] OBJECT {COMMAND}\n" \
-       "where OBJECT := {" \
-       IF_FEATURE_IP_ADDRESS("address | ") \
-       IF_FEATURE_IP_ROUTE("route | ") \
-       IF_FEATURE_IP_LINK("link | ") \
-       IF_FEATURE_IP_TUNNEL("tunnel | ") \
-       IF_FEATURE_IP_RULE("rule") \
-       "}\n" \
-       "OPTIONS := { -f[amily] { inet | inet6 | link } | -o[neline] }" \
-
-#define ipaddr_trivial_usage \
-       "{ {add|del} IFADDR dev STRING | {show|flush}\n" \
-       "               [dev STRING] [to PREFIX] }"
-#define ipaddr_full_usage "\n\n" \
-       "ipaddr {add|delete} IFADDR dev STRING\n" \
-       "ipaddr {show|flush} [dev STRING] [scope SCOPE-ID]\n" \
-       "       [to PREFIX] [label PATTERN]\n" \
-       "       IFADDR := PREFIX | ADDR peer PREFIX\n" \
-       "       [broadcast ADDR] [anycast ADDR]\n" \
-       "       [label STRING] [scope SCOPE-ID]\n" \
-       "       SCOPE-ID := [host | link | global | NUMBER]" \
-
-#define ipcalc_trivial_usage \
-       "[OPTIONS] ADDRESS[[/]NETMASK] [NETMASK]"
-#define ipcalc_full_usage "\n\n" \
-       "Calculate IP network settings from a IP address\n" \
-     "\nOptions:" \
-       IF_FEATURE_IPCALC_LONG_OPTIONS( \
-     "\n       -b,--broadcast  Display calculated broadcast address" \
-     "\n       -n,--network    Display calculated network address" \
-     "\n       -m,--netmask    Display default netmask for IP" \
-       IF_FEATURE_IPCALC_FANCY( \
-     "\n       -p,--prefix     Display the prefix for IP/NETMASK" \
-     "\n       -h,--hostname   Display first resolved host name" \
-     "\n       -s,--silent     Don't ever display error messages" \
-       ) \
-       ) \
-       IF_NOT_FEATURE_IPCALC_LONG_OPTIONS( \
-     "\n       -b      Display calculated broadcast address" \
-     "\n       -n      Display calculated network address" \
-     "\n       -m      Display default netmask for IP" \
-       IF_FEATURE_IPCALC_FANCY( \
-     "\n       -p      Display the prefix for IP/NETMASK" \
-     "\n       -h      Display first resolved host name" \
-     "\n       -s      Don't ever display error messages" \
-       ) \
-       )
-
-#define ipcrm_trivial_usage \
-       "[-MQS key] [-mqs id]"
-#define ipcrm_full_usage "\n\n" \
-       "Upper-case options MQS remove an object by shmkey value.\n" \
-       "Lower-case options remove an object by shmid value.\n" \
-     "\nOptions:" \
-     "\n       -mM     Remove memory segment after last detach" \
-     "\n       -qQ     Remove message queue" \
-     "\n       -sS     Remove semaphore" \
-
-#define ipcs_trivial_usage \
-       "[[-smq] -i shmid] | [[-asmq] [-tcplu]]"
-#define ipcs_full_usage "\n\n" \
-       "       -i      Show specific resource" \
-     "\nResource specification:" \
-     "\n       -m      Shared memory segments" \
-     "\n       -q      Message queues" \
-     "\n       -s      Semaphore arrays" \
-     "\n       -a      All (default)" \
-     "\nOutput format:" \
-     "\n       -t      Time" \
-     "\n       -c      Creator" \
-     "\n       -p      Pid" \
-     "\n       -l      Limits" \
-     "\n       -u      Summary" \
-
-#define iplink_trivial_usage \
-       "{ set DEVICE { up | down | arp { on | off } | show [DEVICE] }"
-#define iplink_full_usage "\n\n" \
-       "iplink set DEVICE { up | down | arp | multicast { on | off } |\n" \
-       "                       dynamic { on | off } |\n" \
-       "                       mtu MTU }\n" \
-       "iplink show [DEVICE]" \
-
-#define iproute_trivial_usage \
-       "{ list | flush | { add | del | change | append |\n" \
-       "               replace | monitor } ROUTE }"
-#define iproute_full_usage "\n\n" \
-       "iproute { list | flush } SELECTOR\n" \
-       "iproute get ADDRESS [from ADDRESS iif STRING]\n" \
-       "                       [oif STRING]  [tos TOS]\n" \
-       "iproute { add | del | change | append | replace | monitor } ROUTE\n" \
-       "                       SELECTOR := [root PREFIX] [match PREFIX] [proto RTPROTO]\n" \
-       "                       ROUTE := [TYPE] PREFIX [tos TOS] [proto RTPROTO]\n" \
-       "                               [metric METRIC]" \
-
-#define iprule_trivial_usage \
-       "{[list | add | del] RULE}"
-#define iprule_full_usage "\n\n" \
-       "iprule [list | add | del] SELECTOR ACTION\n" \
-       "       SELECTOR := [from PREFIX] [to PREFIX] [tos TOS] [fwmark FWMARK]\n" \
-       "                       [dev STRING] [pref NUMBER]\n" \
-       "       ACTION := [table TABLE_ID] [nat ADDRESS]\n" \
-       "                       [prohibit | reject | unreachable]\n" \
-       "                       [realms [SRCREALM/]DSTREALM]\n" \
-       "       TABLE_ID := [local | main | default | NUMBER]" \
-
-#define iptunnel_trivial_usage \
-       "{ add | change | del | show } [NAME]\n" \
-       "       [mode { ipip | gre | sit }]\n" \
-       "       [remote ADDR] [local ADDR] [ttl TTL]"
-#define iptunnel_full_usage "\n\n" \
-       "iptunnel { add | change | del | show } [NAME]\n" \
-       "       [mode { ipip | gre | sit }] [remote ADDR] [local ADDR]\n" \
-       "       [[i|o]seq] [[i|o]key KEY] [[i|o]csum]\n" \
-       "       [ttl TTL] [tos TOS] [[no]pmtudisc] [dev PHYS_DEV]" \
-
-#define kbd_mode_trivial_usage \
-       "[-a|k|s|u] [-C TTY]"
-#define kbd_mode_full_usage "\n\n" \
-       "Report or set the keyboard mode\n" \
-     "\nOptions:" \
-     "\n       -a      Default (ASCII)" \
-     "\n       -k      Medium-raw (keyboard)" \
-     "\n       -s      Raw (scancode)" \
-     "\n       -u      Unicode (utf-8)" \
-     "\n       -C TTY  Affect TTY instead of /dev/tty" \
-
-#define kill_trivial_usage \
-       "[-l] [-SIG] PID..."
-#define kill_full_usage "\n\n" \
-       "Send a signal (default: TERM) to given PIDs\n" \
-     "\nOptions:" \
-     "\n       -l      List all signal names and numbers" \
-/*   "\n       -s SIG  Yet another way of specifying SIG" */ \
-
-#define kill_example_usage \
-       "$ ps | grep apache\n" \
-       "252 root     root     S [apache]\n" \
-       "263 www-data www-data S [apache]\n" \
-       "264 www-data www-data S [apache]\n" \
-       "265 www-data www-data S [apache]\n" \
-       "266 www-data www-data S [apache]\n" \
-       "267 www-data www-data S [apache]\n" \
-       "$ kill 252\n"
-
-#define killall_trivial_usage \
-       "[-l] [-q] [-SIG] PROCESS_NAME..."
-#define killall_full_usage "\n\n" \
-       "Send a signal (default: TERM) to given processes\n" \
-     "\nOptions:" \
-     "\n       -l      List all signal names and numbers" \
-/*   "\n       -s SIG  Yet another way of specifying SIG" */ \
-     "\n       -q      Don't complain if no processes were killed" \
-
-#define killall_example_usage \
-       "$ killall apache\n"
-
-#define killall5_trivial_usage \
-       "[-l] [-SIG] [-o PID]..."
-#define killall5_full_usage "\n\n" \
-       "Send a signal (default: TERM) to all processes outside current session\n" \
-     "\nOptions:" \
-     "\n       -l      List all signal names and numbers" \
-     "\n       -o PID  Don't signal this PID" \
-/*   "\n       -s SIG  Yet another way of specifying SIG" */ \
-
-#define klogd_trivial_usage \
-       "[-c N] [-n]"
-#define klogd_full_usage "\n\n" \
-       "Kernel logger\n" \
-     "\nOptions:" \
-     "\n       -c N    Only messages with level < N are printed to console" \
-     "\n       -n      Run in foreground" \
-
-#define length_trivial_usage \
-       "STRING"
-#define length_full_usage "\n\n" \
-       "Print STRING's length"
-
-#define length_example_usage \
-       "$ length Hello\n" \
-       "5\n"
-
-#define less_trivial_usage \
-       "[-EMNmh~I?] [FILE]..."
-#define less_full_usage "\n\n" \
-       "View FILE (or stdin) one screenful at a time\n" \
-     "\nOptions:" \
-     "\n       -E      Quit once the end of a file is reached" \
-     "\n       -M,-m   Display status line with line numbers" \
-     "\n               and percentage through the file" \
-     "\n       -N      Prefix line number to each line" \
-     "\n       -I      Ignore case in all searches" \
-     "\n       -~      Suppress ~s displayed past the end of the file" \
-
-#define linux32_trivial_usage NOUSAGE_STR
-#define linux32_full_usage ""
-#define linux64_trivial_usage NOUSAGE_STR
-#define linux64_full_usage ""
-
-#define linuxrc_trivial_usage NOUSAGE_STR
-#define linuxrc_full_usage ""
-
-#define setarch_trivial_usage \
-       "personality PROG ARGS"
-#define setarch_full_usage "\n\n" \
-       "Personality may be:\n" \
-       "       linux32         Set 32bit uname emulation\n" \
-       "       linux64         Set 64bit uname emulation" \
-
-#define ln_trivial_usage \
-       "[OPTIONS] TARGET... LINK|DIR"
-#define ln_full_usage "\n\n" \
-       "Create a link LINK or DIR/TARGET to the specified TARGET(s)\n" \
-     "\nOptions:" \
-     "\n       -s      Make symlinks instead of hardlinks" \
-     "\n       -f      Remove existing destinations" \
-     "\n       -n      Don't dereference symlinks - treat like normal file" \
-     "\n       -b      Make a backup of the target (if exists) before link operation" \
-     "\n       -S suf  Use suffix instead of ~ when making backup files" \
-
-#define ln_example_usage \
-       "$ ln -s BusyBox /tmp/ls\n" \
-       "$ ls -l /tmp/ls\n" \
-       "lrwxrwxrwx    1 root     root            7 Apr 12 18:39 ls -> BusyBox*\n"
-
-#define load_policy_trivial_usage NOUSAGE_STR
-#define load_policy_full_usage ""
-
-#define loadfont_trivial_usage \
-       "< font"
-#define loadfont_full_usage "\n\n" \
-       "Load a console font from stdin" \
-/*   "\n       -C TTY  Affect TTY instead of /dev/tty" */ \
-
-#define loadfont_example_usage \
-       "$ loadfont < /etc/i18n/fontname\n"
-
-#define loadkmap_trivial_usage \
-       "< keymap"
-#define loadkmap_full_usage "\n\n" \
-       "Load a binary keyboard translation table from stdin\n" \
-/*   "\n       -C TTY  Affect TTY instead of /dev/tty" */ \
-
-#define loadkmap_example_usage \
-       "$ loadkmap < /etc/i18n/lang-keymap\n"
-
-#define logger_trivial_usage \
-       "[OPTIONS] [MESSAGE]"
-#define logger_full_usage "\n\n" \
-       "Write MESSAGE (or stdin) to syslog\n" \
-     "\nOptions:" \
-     "\n       -s      Log to stderr as well as the system log" \
-     "\n       -t TAG  Log using the specified tag (defaults to user name)" \
-     "\n       -p PRIO Priority (numeric or facility.level pair)" \
-
-#define logger_example_usage \
-       "$ logger \"hello\"\n"
-
-#define login_trivial_usage \
-       "[-p] [-h HOST] [[-f] USER]"
-#define login_full_usage "\n\n" \
-       "Begin a new session on the system\n" \
-     "\nOptions:" \
-     "\n       -f      Don't authenticate (user already authenticated)" \
-     "\n       -h      Name of the remote host" \
-     "\n       -p      Preserve environment" \
-
-#define logname_trivial_usage \
-       ""
-#define logname_full_usage "\n\n" \
-       "Print the name of the current user"
-#define logname_example_usage \
-       "$ logname\n" \
-       "root\n"
-
-#define logread_trivial_usage \
-       "[OPTIONS]"
-#define logread_full_usage "\n\n" \
-       "Show messages in syslogd's circular buffer\n" \
-     "\nOptions:" \
-     "\n       -f      Output data as log grows" \
-
-#define losetup_trivial_usage \
-       "[-o OFS] LOOPDEV FILE - associate loop devices\n" \
-       "       losetup -d LOOPDEV - disassociate\n" \
-       "       losetup [-f] - show"
-#define losetup_full_usage "\n\n" \
-       "Options:" \
-     "\n       -o OFS  Start OFS bytes into FILE" \
-     "\n       -f      Show first free loop device" \
-
-#define losetup_notes_usage \
-       "No arguments will display all current associations.\n" \
-       "One argument (losetup /dev/loop1) will display the current association\n" \
-       "(if any), or disassociate it (with -d). The display shows the offset\n" \
-       "and filename of the file the loop device is currently bound to.\n\n" \
-       "Two arguments (losetup /dev/loop1 file.img) create a new association,\n" \
-       "with an optional offset (-o 12345). Encryption is not yet supported.\n" \
-       "losetup -f will show the first loop free loop device\n\n"
-
-#define lpd_trivial_usage \
-       "SPOOLDIR [HELPER [ARGS]]"
-#define lpd_full_usage "\n\n" \
-       "SPOOLDIR must contain (symlinks to) device nodes or directories" \
-     "\nwith names matching print queue names. In the first case, jobs are" \
-     "\nsent directly to the device. Otherwise each job is stored in queue" \
-     "\ndirectory and HELPER program is called. Name of file to print" \
-     "\nis passed in $DATAFILE variable." \
-     "\nExample:" \
-     "\n       tcpsvd -E 0 515 softlimit -m 999999 lpd /var/spool ./print" \
-
-#define lpq_trivial_usage \
-       "[-P queue[@host[:port]]] [-U USERNAME] [-d JOBID]... [-fs]"
-#define lpq_full_usage "\n\n" \
-       "Options:" \
-     "\n       -P      lp service to connect to (else uses $PRINTER)" \
-     "\n       -d      Delete jobs" \
-     "\n       -f      Force any waiting job to be printed" \
-     "\n       -s      Short display" \
-
-#define lpr_trivial_usage \
-       "-P queue[@host[:port]] -U USERNAME -J TITLE -Vmh [FILE]..."
-/* -C CLASS exists too, not shown.
- * CLASS is supposed to be printed on banner page, if one is requested */
-#define lpr_full_usage "\n\n" \
-       "Options:" \
-     "\n       -P      lp service to connect to (else uses $PRINTER)"\
-     "\n       -m      Send mail on completion" \
-     "\n       -h      Print banner page too" \
-     "\n       -V      Verbose" \
-
-#define ls_trivial_usage \
-       "[-1Aa" IF_FEATURE_LS_TIMESTAMPS("c") "Cd" \
-       IF_FEATURE_LS_TIMESTAMPS("e") IF_FEATURE_LS_FILETYPES("F") "iln" \
-       IF_FEATURE_LS_FILETYPES("p") IF_FEATURE_LS_FOLLOWLINKS("L") \
-       IF_FEATURE_LS_RECURSIVE("R") IF_FEATURE_LS_SORTFILES("rS") "s" \
-       IF_FEATURE_AUTOWIDTH("T") IF_FEATURE_LS_TIMESTAMPS("tu") \
-       IF_FEATURE_LS_SORTFILES("v") IF_FEATURE_AUTOWIDTH("w") "x" \
-       IF_FEATURE_LS_SORTFILES("X") IF_FEATURE_HUMAN_READABLE("h") "k" \
-       IF_SELINUX("K") "] [FILE]..."
-#define ls_full_usage "\n\n" \
-       "List directory contents\n" \
-     "\nOptions:" \
-     "\n       -1      List in a single column" \
-     "\n       -A      Don't list . and .." \
-     "\n       -a      Don't hide entries starting with ." \
-     "\n       -C      List by columns" \
-       IF_FEATURE_LS_TIMESTAMPS( \
-     "\n       -c      With -l: sort by ctime") \
-       IF_FEATURE_LS_COLOR( \
-     "\n       --color[={always,never,auto}]   Control coloring") \
-     "\n       -d      List directory entries instead of contents" \
-       IF_FEATURE_LS_TIMESTAMPS( \
-     "\n       -e      List full date and time") \
-       IF_FEATURE_LS_FILETYPES( \
-     "\n       -F      Append indicator (one of */=@|) to entries") \
-     "\n       -i      List inode numbers" \
-     "\n       -l      Long listing format" \
-     "\n       -n      List numeric UIDs and GIDs instead of names" \
-       IF_FEATURE_LS_FILETYPES( \
-     "\n       -p      Append indicator (one of /=@|) to entries") \
-       IF_FEATURE_LS_FOLLOWLINKS( \
-     "\n       -L      List entries pointed to by symlinks") \
-       IF_FEATURE_LS_RECURSIVE( \
-     "\n       -R      Recurse") \
-       IF_FEATURE_LS_SORTFILES( \
-     "\n       -r      Sort in reverse order") \
-       IF_FEATURE_LS_SORTFILES( \
-     "\n       -S      Sort by file size") \
-     "\n       -s      List the size of each file, in blocks" \
-       IF_FEATURE_AUTOWIDTH( \
-     "\n       -T N    Assume tabstop every N columns") \
-       IF_FEATURE_LS_TIMESTAMPS( \
-     "\n       -t      With -l: sort by modification time") \
-       IF_FEATURE_LS_TIMESTAMPS( \
-     "\n       -u      With -l: sort by access time") \
-       IF_FEATURE_LS_SORTFILES( \
-     "\n       -v      Sort by version") \
-       IF_FEATURE_AUTOWIDTH( \
-     "\n       -w N    Assume the terminal is N columns wide") \
-     "\n       -x      List by lines" \
-       IF_FEATURE_LS_SORTFILES( \
-     "\n       -X      Sort by extension") \
-       IF_FEATURE_HUMAN_READABLE( \
-     "\n       -h      List sizes in human readable format (1K 243M 2G)") \
-       IF_SELINUX( \
-     "\n       -k      List security context") \
-       IF_SELINUX( \
-     "\n       -K      List security context in long format") \
-       IF_SELINUX( \
-     "\n       -Z      List security context and permission") \
-
-#define lsattr_trivial_usage \
-       "[-Radlv] [FILE]..."
-#define lsattr_full_usage "\n\n" \
-       "List file attributes on an ext2 fs\n" \
-     "\nOptions:" \
-     "\n       -R      Recurse" \
-     "\n       -a      Don't hide entries starting with ." \
-     "\n       -d      List directory entries instead of contents" \
-     "\n       -l      List long flag names" \
-     "\n       -v      List the file's version/generation number" \
-
-#define lsmod_trivial_usage \
-       ""
-#define lsmod_full_usage "\n\n" \
-       "List the currently loaded kernel modules"
-
-#define lspci_trivial_usage \
-       "[-mk]"
-#define lspci_full_usage "\n\n" \
-       "List all PCI devices" \
-     "\n" \
-     "\n       -m      Parseable output" \
-     "\n       -k      Show driver" \
-
-#define lsusb_trivial_usage NOUSAGE_STR
-#define lsusb_full_usage ""
-
-#if ENABLE_FEATURE_MAKEDEVS_LEAF
-#define makedevs_trivial_usage \
-       "NAME TYPE MAJOR MINOR FIRST LAST [s]"
-#define makedevs_full_usage "\n\n" \
-       "Create a range of block or character special files" \
-     "\n" \
-     "\nTYPE is:" \
-     "\n       b       Block device" \
-     "\n       c       Character device" \
-     "\n       f       FIFO, MAJOR and MINOR are ignored" \
-     "\n" \
-     "\nFIRST..LAST specify numbers appended to NAME." \
-     "\nIf 's' is the last argument, the base device is created as well." \
-     "\n" \
-     "\nExamples:" \
-     "\n       makedevs /dev/ttyS c 4 66 2 63   ->  ttyS2-ttyS63" \
-     "\n       makedevs /dev/hda b 3 0 0 8 s    ->  hda,hda1-hda8"
-#define makedevs_example_usage \
-       "# makedevs /dev/ttyS c 4 66 2 63\n" \
-       "[creates ttyS2-ttyS63]\n" \
-       "# makedevs /dev/hda b 3 0 0 8 s\n" \
-       "[creates hda,hda1-hda8]\n"
-#endif
-
-#if ENABLE_FEATURE_MAKEDEVS_TABLE
-#define makedevs_trivial_usage \
-       "[-d device_table] rootdir"
-#define makedevs_full_usage "\n\n" \
-       "Create a range of special files as specified in a device table.\n" \
-       "Device table entries take the form of:\n" \
-       "<type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>\n" \
-       "Where name is the file name, type can be one of:\n" \
-       "       f       Regular file\n" \
-       "       d       Directory\n" \
-       "       c       Character device\n" \
-       "       b       Block device\n" \
-       "       p       Fifo (named pipe)\n" \
-       "uid is the user id for the target file, gid is the group id for the\n" \
-       "target file. The rest of the entries (major, minor, etc) apply to\n" \
-       "to device special files. A '-' may be used for blank entries."
-#define makedevs_example_usage \
-       "For example:\n" \
-       "<name>    <type> <mode><uid><gid><major><minor><start><inc><count>\n" \
-       "/dev         d   755    0    0    -      -      -      -    -\n" \
-       "/dev/console c   666    0    0    5      1      -      -    -\n" \
-       "/dev/null    c   666    0    0    1      3      0      0    -\n" \
-       "/dev/zero    c   666    0    0    1      5      0      0    -\n" \
-       "/dev/hda     b   640    0    0    3      0      0      0    -\n" \
-       "/dev/hda     b   640    0    0    3      1      1      1    15\n\n" \
-       "Will Produce:\n" \
-       "/dev\n" \
-       "/dev/console\n" \
-       "/dev/null\n" \
-       "/dev/zero\n" \
-       "/dev/hda\n" \
-       "/dev/hda[0-15]\n"
-#endif
-
-#define makemime_trivial_usage \
-       "[OPTIONS] [FILE]..."
-#define makemime_full_usage "\n\n" \
-       "Create multipart MIME-encoded message from FILEs\n" \
-/*     "Transfer encoding is base64, disposition is inline (not attachment)\n" */ \
-     "\nOptions:" \
-     "\n       -o FILE Output. Default: stdout" \
-     "\n       -a HDR  Add header. Examples:" \
-     "\n               \"From: user@host.org\", \"Date: `date -R`\"" \
-     "\n       -c CT   Content type. Default: text/plain" \
-     "\n       -C CS   Charset. Default: " CONFIG_FEATURE_MIME_CHARSET \
-/*   "\n       -e ENC  Transfer encoding. Ignored. base64 is assumed" */ \
-     "\n" \
-     "\nOther options are silently ignored" \
-
-#define man_trivial_usage \
-       "[OPTIONS] [MANPAGE]..."
-#define man_full_usage "\n\n" \
-       "Format and display manual page\n" \
-     "\nOptions:" \
-     "\n       -a      Display all pages" \
-     "\n       -w      Show page locations" \
-
-#define matchpathcon_trivial_usage \
-       "[-n] [-N] [-f file_contexts_file] [-p prefix] [-V]"
-#define matchpathcon_full_usage "\n\n" \
-       "       -n      Don't display path" \
-     "\n       -N      Don't use translations" \
-     "\n       -f      Use alternate file_context file" \
-     "\n       -p      Use prefix to speed translations" \
-     "\n       -V      Verify file context on disk matches defaults" \
-
-#define md5sum_trivial_usage \
-       "[OPTIONS] [FILE]..." \
-       IF_FEATURE_MD5_SHA1_SUM_CHECK("\n   or: md5sum [OPTIONS] -c [FILE]")
-#define md5sum_full_usage "\n\n" \
-       "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " MD5 checksums" \
-       IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" \
-     "\nOptions:" \
-     "\n       -c      Check sums against given list" \
-     "\n       -s      Don't output anything, status code shows success" \
-     "\n       -w      Warn about improperly formatted checksum lines" \
-       )
-
-#define md5sum_example_usage \
-       "$ md5sum < busybox\n" \
-       "6fd11e98b98a58f64ff3398d7b324003\n" \
-       "$ md5sum busybox\n" \
-       "6fd11e98b98a58f64ff3398d7b324003  busybox\n" \
-       "$ md5sum -c -\n" \
-       "6fd11e98b98a58f64ff3398d7b324003  busybox\n" \
-       "busybox: OK\n" \
-       "^D\n"
-
-#define sha1sum_trivial_usage \
-       "[OPTIONS] [FILE]..." \
-       IF_FEATURE_MD5_SHA1_SUM_CHECK("\n   or: sha1sum [OPTIONS] -c [FILE]")
-#define sha1sum_full_usage "\n\n" \
-       "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA1 checksums" \
-       IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" \
-     "\nOptions:" \
-     "\n       -c      Check sums against given list" \
-     "\n       -s      Don't output anything, status code shows success" \
-     "\n       -w      Warn about improperly formatted checksum lines" \
-       )
-
-#define sha256sum_trivial_usage \
-       "[OPTIONS] [FILE]..." \
-       IF_FEATURE_MD5_SHA1_SUM_CHECK("\n   or: sha256sum [OPTIONS] -c [FILE]")
-#define sha256sum_full_usage "\n\n" \
-       "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA256 checksums" \
-       IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" \
-     "\nOptions:" \
-     "\n       -c      Check sums against given list" \
-     "\n       -s      Don't output anything, status code shows success" \
-     "\n       -w      Warn about improperly formatted checksum lines" \
-       )
-
-#define sha512sum_trivial_usage \
-       "[OPTIONS] [FILE]..." \
-       IF_FEATURE_MD5_SHA1_SUM_CHECK("\n   or: sha512sum [OPTIONS] -c [FILE]")
-#define sha512sum_full_usage "\n\n" \
-       "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA512 checksums" \
-       IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n" \
-     "\nOptions:" \
-     "\n       -c      Check sums against given list" \
-     "\n       -s      Don't output anything, status code shows success" \
-     "\n       -w      Warn about improperly formatted checksum lines" \
-       )
-
-#define mdev_trivial_usage \
-       "[-s]"
-#define mdev_full_usage "\n\n" \
-       "       -s      Scan /sys and populate /dev during system boot\n" \
-       "\n" \
-       "It can be run by kernel as a hotplug helper. To activate it:\n" \
-       " echo /sbin/mdev > /proc/sys/kernel/hotplug\n" \
-       IF_FEATURE_MDEV_CONF( \
-       "It uses /etc/mdev.conf with lines\n" \
-       "[-]DEVNAME UID:GID PERM" \
-                       IF_FEATURE_MDEV_RENAME(" [>|=PATH]") \
-                       IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]") \
-       ) \
-
-#define mdev_notes_usage "" \
-       IF_FEATURE_MDEV_CONFIG( \
-       "The mdev config file contains lines that look like:\n" \
-       "  hd[a-z][0-9]* 0:3 660\n\n" \
-       "That's device name (with regex match), uid:gid, and permissions.\n\n" \
-       IF_FEATURE_MDEV_EXEC( \
-       "Optionally, that can be followed (on the same line) by a special character\n" \
-       "and a command line to run after creating/before deleting the corresponding\n" \
-       "device(s). The environment variable $MDEV indicates the active device node\n" \
-       "(which is useful if it's a regex match). For example:\n\n" \
-       "  hdc root:cdrom 660  *ln -s $MDEV cdrom\n\n" \
-       "The special characters are @ (run after creating), $ (run before deleting),\n" \
-       "and * (run both after creating and before deleting). The commands run in\n" \
-       "the /dev directory, and use system() which calls /bin/sh.\n\n" \
-       ) \
-       "Config file parsing stops on the first matching line. If no config\n" \
-       "entry is matched, devices are created with default 0:0 660. (Make\n" \
-       "the last line match .* to override this.)\n\n" \
-       )
-
-#define mesg_trivial_usage \
-       "[y|n]"
-#define mesg_full_usage "\n\n" \
-       "Control write access to your terminal\n" \
-       "       y       Allow write access to your terminal\n" \
-       "       n       Disallow write access to your terminal"
-
-#define microcom_trivial_usage \
-       "[-d DELAY] [-t TIMEOUT] [-s SPEED] [-X] TTY"
-#define microcom_full_usage "\n\n" \
-       "Copy bytes for stdin to TTY and from TTY to stdout\n" \
-     "\nOptions:" \
-     "\n       -d      Wait up to DELAY ms for TTY output before sending every" \
-     "\n               next byte to it" \
-     "\n       -t      Exit if both stdin and TTY are silent for TIMEOUT ms" \
-     "\n       -s      Set serial line to SPEED" \
-     "\n       -X      Disable special meaning of NUL and Ctrl-X from stdin" \
-
-#define mkdir_trivial_usage \
-       "[OPTIONS] DIRECTORY..."
-#define mkdir_full_usage "\n\n" \
-       "Create DIRECTORY\n" \
-     "\nOptions:" \
-     "\n       -m      Mode" \
-     "\n       -p      No error if exists; make parent directories as needed" \
-       IF_SELINUX( \
-     "\n       -Z      Set security context" \
-       )
-
-#define mkdir_example_usage \
-       "$ mkdir /tmp/foo\n" \
-       "$ mkdir /tmp/foo\n" \
-       "/tmp/foo: File exists\n" \
-       "$ mkdir /tmp/foo/bar/baz\n" \
-       "/tmp/foo/bar/baz: No such file or directory\n" \
-       "$ mkdir -p /tmp/foo/bar/baz\n"
-
-#define mkfifo_trivial_usage \
-       "[OPTIONS] name"
-#define mkfifo_full_usage "\n\n" \
-       "Create named pipe (identical to 'mknod name p')\n" \
-     "\nOptions:" \
-     "\n       -m MODE Mode (default a=rw)" \
-       IF_SELINUX( \
-     "\n       -Z      Set security context" \
-       )
-
-#define mkfs_ext2_trivial_usage \
-       "[-Fn] " \
-       /* "[-c|-l filename] " */ \
-       "[-b BLK_SIZE] " \
-       /* "[-f fragment-size] [-g blocks-per-group] " */ \
-       "[-i INODE_RATIO] [-I INODE_SIZE] " \
-       /* "[-j] [-J journal-options] [-N number-of-inodes] " */ \
-       "[-m RESERVED_PERCENT] " \
-       /* "[-o creator-os] [-O feature[,...]] [-q] " */ \
-       /* "[r fs-revision-level] [-E extended-options] [-v] [-F] " */ \
-       "[-L LABEL] " \
-       /* "[-M last-mounted-directory] [-S] [-T filesystem-type] " */ \
-       "BLOCKDEV [KBYTES]"
-#define mkfs_ext2_full_usage "\n\n" \
-       "       -b BLK_SIZE     Block size, bytes" \
-/*   "\n       -c              Check device for bad blocks" */ \
-/*   "\n       -E opts         Set extended options" */ \
-/*   "\n       -f size         Fragment size in bytes" */ \
-     "\n       -F              Force" \
-/*   "\n       -g N            Number of blocks in a block group" */ \
-     "\n       -i RATIO        Max number of files is filesystem_size / RATIO" \
-     "\n       -I BYTES        Inode size (min 128)" \
-/*   "\n       -j              Create a journal (ext3)" */ \
-/*   "\n       -J opts         Set journal options (size/device)" */ \
-/*   "\n       -l file         Read bad blocks list from file" */ \
-     "\n       -L LBL          Volume label" \
-     "\n       -m PERCENT      Percent of blocks to reserve for admin" \
-/*   "\n       -M dir          Set last mounted directory" */ \
-     "\n       -n              Dry run" \
-/*   "\n       -N N            Number of inodes to create" */ \
-/*   "\n       -o os           Set the 'creator os' field" */ \
-/*   "\n       -O features     Dir_index/filetype/has_journal/journal_dev/sparse_super" */ \
-/*   "\n       -q              Quiet" */ \
-/*   "\n       -r rev          Set filesystem revision" */ \
-/*   "\n       -S              Write superblock and group descriptors only" */ \
-/*   "\n       -T fs-type      Set usage type (news/largefile/largefile4)" */ \
-/*   "\n       -v              Verbose" */ \
-
-#define mkfs_minix_trivial_usage \
-       "[-c | -l FILE] [-nXX] [-iXX] BLOCKDEV [KBYTES]"
-#define mkfs_minix_full_usage "\n\n" \
-       "Make a MINIX filesystem\n" \
-     "\nOptions:" \
-     "\n       -c              Check device for bad blocks" \
-     "\n       -n [14|30]      Maximum length of filenames" \
-     "\n       -i INODES       Number of inodes for the filesystem" \
-     "\n       -l FILE         Read bad blocks list from FILE" \
-     "\n       -v              Make version 2 filesystem" \
-
-#define mkfs_reiser_trivial_usage \
-       "[-f] [-l LABEL] BLOCKDEV [4K-BLOCKS]"
-
-#define mkfs_reiser_full_usage "\n\n" \
-       "Make a ReiserFS V3 filesystem\n" \
-     "\nOptions:" \
-     "\n       -f      Force" \
-     "\n       -l LBL  Volume label" \
-
-#define mkfs_vfat_trivial_usage \
-       "[-v] [-n LABEL] BLOCKDEV [KBYTES]"
-/* Accepted but ignored:
-       "[-c] [-C] [-I] [-l bad-block-file] [-b backup-boot-sector] "
-       "[-m boot-msg-file] [-i volume-id] "
-       "[-s sectors-per-cluster] [-S logical-sector-size] [-f number-of-FATs] "
-       "[-h hidden-sectors] [-F fat-size] [-r root-dir-entries] [-R reserved-sectors] "
-*/
-#define mkfs_vfat_full_usage "\n\n" \
-       "Make a FAT32 filesystem\n" \
-     "\nOptions:" \
-/*   "\n       -c      Check device for bad blocks" */ \
-     "\n       -v      Verbose" \
-/*   "\n       -I      Allow to use entire disk device (e.g. /dev/hda)" */ \
-     "\n       -n LBL  Volume label" \
-
-#define mknod_trivial_usage \
-       "[OPTIONS] NAME TYPE MAJOR MINOR"
-#define mknod_full_usage "\n\n" \
-       "Create a special file (block, character, or pipe)\n" \
-     "\nOptions:" \
-     "\n       -m      Create the special file using the specified mode (default a=rw)" \
-     "\nTYPEs include:" \
-     "\n       b:      Make a block device" \
-     "\n       c or u: Make a character device" \
-     "\n       p:      Make a named pipe (MAJOR and MINOR are ignored)" \
-       IF_SELINUX( \
-     "\n       -Z      Set security context" \
-       )
-
-#define mknod_example_usage \
-       "$ mknod /dev/fd0 b 2 0\n" \
-       "$ mknod -m 644 /tmp/pipe p\n"
-
-#define mkswap_trivial_usage \
-       "[OPTIONS] BLOCKDEV [KBYTES]"
-#define mkswap_full_usage "\n\n" \
-       "Prepare BLOCKDEV to be used as swap partition\n" \
-     "\nOptions:" \
-     "\n       -L LBL  Label" \
-
-#define mktemp_trivial_usage \
-       "[-dt] [-p DIR] [TEMPLATE]"
-#define mktemp_full_usage "\n\n" \
-       "Create a temporary file with name based on TEMPLATE and print its name.\n" \
-       "TEMPLATE must end with XXXXXX (e.g. [/dir/]nameXXXXXX).\n" \
-     "\nOptions:" \
-     "\n       -d      Make a directory instead of a file" \
-/*   "\n       -q      Fail silently if an error occurs" - we ignore it */ \
-     "\n       -t      Generate a path rooted in temporary directory" \
-     "\n       -p DIR  Use DIR as a temporary directory (implies -t)" \
-     "\n" \
-     "\nFor -t or -p, directory is chosen as follows:" \
-     "\n$TMPDIR if set, else -p DIR, else /tmp" \
-
-#define mktemp_example_usage \
-       "$ mktemp /tmp/temp.XXXXXX\n" \
-       "/tmp/temp.mWiLjM\n" \
-       "$ ls -la /tmp/temp.mWiLjM\n" \
-       "-rw-------    1 andersen andersen        0 Apr 25 17:10 /tmp/temp.mWiLjM\n"
-
-#define more_trivial_usage \
-       "[FILE]..."
-#define more_full_usage "\n\n" \
-       "View FILE (or stdin) one screenful at a time"
-
-#define more_example_usage \
-       "$ dmesg | more\n"
-
-#define mount_trivial_usage \
-       "[OPTIONS] [-o OPTS] DEVICE NODE"
-#define mount_full_usage "\n\n" \
-       "Mount a filesystem. Filesystem autodetection requires /proc.\n" \
-     "\nOptions:" \
-     "\n       -a              Mount all filesystems in fstab" \
-       IF_FEATURE_MOUNT_FAKE( \
-       IF_FEATURE_MTAB_SUPPORT( \
-     "\n       -f              Update /etc/mtab, but don't mount" \
-       ) \
-       IF_NOT_FEATURE_MTAB_SUPPORT( \
-     "\n       -f              Dry run" \
-       ) \
-       ) \
-       IF_FEATURE_MOUNT_HELPERS( \
-     "\n       -i              Don't run mount helper" \
-       ) \
-       IF_FEATURE_MTAB_SUPPORT( \
-     "\n       -n              Don't update /etc/mtab" \
-       ) \
-     "\n       -r              Read-only mount" \
-     "\n       -w              Read-write mount (default)" \
-     "\n       -t FSTYPE       Filesystem type (supports comma-separated list of types)" \
-     "\n       -O OPT          Mount only filesystems with option OPT (-a only)" \
-     "\n-o OPT:" \
-       IF_FEATURE_MOUNT_LOOP( \
-     "\n       loop            Ignored (loop devices are autodetected)" \
-       ) \
-       IF_FEATURE_MOUNT_FLAGS( \
-     "\n       [a]sync         Writes are [a]synchronous" \
-     "\n       [no]atime       Disable/enable updates to inode access times" \
-     "\n       [no]diratime    Disable/enable atime updates to directories" \
-     "\n       [no]relatime    Disable/enable atime updates relative to modification time" \
-     "\n       [no]dev         (Dis)allow use of special device files" \
-     "\n       [no]exec        (Dis)allow use of executable files" \
-     "\n       [no]suid        (Dis)allow set-user-id-root programs" \
-     "\n       [r]shared       Convert [recursively] to a shared subtree" \
-     "\n       [r]slave        Convert [recursively] to a slave subtree" \
-     "\n       [r]private      Convert [recursively] to a private subtree" \
-     "\n       [un]bindable    Make mount point [un]able to be bind mounted" \
-     "\n       bind            Bind a file or directory to another location" \
-     "\n       move            Relocate an existing mount point" \
-       ) \
-     "\n       remount         Remount a mounted filesystem, changing flags" \
-     "\n       ro/rw           Same as -r/-w" \
-     "\n" \
-     "\nThere are filesystem-specific -o flags." \
-
-#define mount_example_usage \
-       "$ mount\n" \
-       "/dev/hda3 on / type minix (rw)\n" \
-       "proc on /proc type proc (rw)\n" \
-       "devpts on /dev/pts type devpts (rw)\n" \
-       "$ mount /dev/fd0 /mnt -t msdos -o ro\n" \
-       "$ mount /tmp/diskimage /opt -t ext2 -o loop\n" \
-       "$ mount cd_image.iso mydir\n"
-#define mount_notes_usage \
-       "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
-
-#define mountpoint_trivial_usage \
-       "[-q] <[-dn] DIR | -x DEVICE>"
-#define mountpoint_full_usage "\n\n" \
-       "Check if the directory is a mountpoint\n" \
-     "\nOptions:" \
-     "\n       -q      Quiet" \
-     "\n       -d      Print major/minor device number of the filesystem" \
-     "\n       -n      Print device name of the filesystem" \
-     "\n       -x      Print major/minor device number of the blockdevice" \
-
-#define mountpoint_example_usage \
-       "$ mountpoint /proc\n" \
-       "/proc is not a mountpoint\n" \
-       "$ mountpoint /sys\n" \
-       "/sys is a mountpoint\n"
-
-#define mt_trivial_usage \
-       "[-f device] opcode value"
-#define mt_full_usage "\n\n" \
-       "Control magnetic tape drive operation\n" \
-       "\n" \
-       "Available Opcodes:\n" \
-       "\n" \
-       "bsf bsfm bsr bss datacompression drvbuffer eof eom erase\n" \
-       "fsf fsfm fsr fss load lock mkpart nop offline ras1 ras2\n" \
-       "ras3 reset retension rewind rewoffline seek setblk setdensity\n" \
-       "setpart tell unload unlock weof wset" \
-
-#define mv_trivial_usage \
-       "[OPTIONS] SOURCE DEST\n" \
-       "or: mv [OPTIONS] SOURCE... DIRECTORY"
-#define mv_full_usage "\n\n" \
-       "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY\n" \
-     "\nOptions:" \
-     "\n       -f      Don't prompt before overwriting" \
-     "\n       -i      Interactive, prompt before overwrite" \
-
-#define mv_example_usage \
-       "$ mv /tmp/foo /bin/bar\n"
-
-#define nameif_trivial_usage \
-       "[-s] [-c FILE] [{IFNAME MACADDR}]"
-#define nameif_full_usage "\n\n" \
-       "Rename network interface while it in the down state\n" \
-     "\nOptions:" \
-     "\n       -c FILE         Use configuration file (default: /etc/mactab)" \
-     "\n       -s              Use syslog (LOCAL0 facility)" \
-     "\n       IFNAME MACADDR  new_interface_name interface_mac_address" \
-
-#define nameif_example_usage \
-       "$ nameif -s dmz0 00:A0:C9:8C:F6:3F\n" \
-       " or\n" \
-       "$ nameif -c /etc/my_mactab_file\n" \
-
-#define netstat_trivial_usage \
-       "[-laentuwxr"IF_FEATURE_NETSTAT_WIDE("W")IF_FEATURE_NETSTAT_PRG("p")"]"
-#define netstat_full_usage "\n\n" \
-       "Display networking information\n" \
-     "\nOptions:" \
-     "\n       -l      Display listening server sockets" \
-     "\n       -a      Display all sockets (default: connected)" \
-     "\n       -e      Display other/more information" \
-     "\n       -n      Don't resolve names" \
-     "\n       -t      Tcp sockets" \
-     "\n       -u      Udp sockets" \
-     "\n       -w      Raw sockets" \
-     "\n       -x      Unix sockets" \
-     "\n       -r      Display routing table" \
-       IF_FEATURE_NETSTAT_WIDE( \
-     "\n       -W      Display with no column truncation" \
-       ) \
-       IF_FEATURE_NETSTAT_PRG( \
-     "\n       -p      Display PID/Program name for sockets" \
-       )
-
-#define nmeter_trivial_usage \
-       "format_string"
-#define nmeter_full_usage "\n\n" \
-       "Monitor system in real time\n\n" \
-       "Format specifiers:\n" \
-       " %Nc or %[cN]  Monitor CPU. N - bar size, default 10\n" \
-       "               (displays: S:system U:user N:niced D:iowait I:irq i:softirq)\n" \
-       " %[niface]     Monitor network interface 'iface'\n" \
-       " %m            Monitor allocated memory\n" \
-       " %[mf]         Monitor free memory\n" \
-       " %[mt]         Monitor total memory\n" \
-       " %s            Monitor allocated swap\n" \
-       " %f            Monitor number of used file descriptors\n" \
-       " %Ni           Monitor total/specific IRQ rate\n" \
-       " %x            Monitor context switch rate\n" \
-       " %p            Monitor forks\n" \
-       " %[pn]         Monitor # of processes\n" \
-       " %b            Monitor block io\n" \
-       " %Nt           Show time (with N decimal points)\n" \
-       " %Nd           Milliseconds between updates (default:1000)\n" \
-       " %r            Print <cr> instead of <lf> at EOL" \
-
-#define nmeter_example_usage \
-       "nmeter '%250d%t %20c int %i bio %b mem %m forks%p'"
-
-#define nohup_trivial_usage \
-       "PROG ARGS"
-#define nohup_full_usage "\n\n" \
-       "Run PROG immune to hangups, with output to a non-tty"
-#define nohup_example_usage \
-       "$ nohup make &"
-
-#define nslookup_trivial_usage \
-       "[HOST] [SERVER]"
-#define nslookup_full_usage "\n\n" \
-       "Query the nameserver for the IP address of the given HOST\n" \
-       "optionally using a specified DNS server"
-#define nslookup_example_usage \
-       "$ nslookup localhost\n" \
-       "Server:     default\n" \
-       "Address:    default\n" \
-       "\n" \
-       "Name:       debian\n" \
-       "Address:    127.0.0.1\n"
-
-#define ntpd_trivial_usage \
-       "[-dnqwl] [-S PROG] [-p PEER]..."
-#define ntpd_full_usage "\n\n" \
-       "NTP client/server\n" \
-     "\nOptions:" \
-     "\n       -d      Verbose" \
-     "\n       -n      Do not daemonize" \
-     "\n       -q      Quit after clock is set" \
-/* -N exists for mostly compat reasons, thus not essential to inform */ \
-/* the user that it exists: user may use nice as well */ \
-/*   "\n       -N      Run at high priority" */ \
-     "\n       -w      Do not set time (only query peers), implies -n" \
-     "\n       -l      Run as server on port 123" \
-     "\n       -S PROG Run PROG after stepping time, stratum change, and every 11 mins" \
-     "\n       -p PEER Obtain time from PEER (may be repeated)" \
-
-#define od_trivial_usage \
-       "[-aBbcDdeFfHhIiLlOovXx] " IF_DESKTOP("[-t TYPE] ") "[FILE]"
-#define od_full_usage "\n\n" \
-       "Write an unambiguous representation, octal bytes by default, of FILE\n" \
-       "(or stdin) to stdout"
-
-#define openvt_trivial_usage \
-       "[-c N] [-sw] [PROG ARGS]"
-#define openvt_full_usage "\n\n" \
-       "Start PROG on a new virtual terminal\n" \
-     "\nOptions:" \
-     "\n       -c N    Use specified VT" \
-     "\n       -s      Switch to the VT" \
-/*   "\n       -l      Run PROG as login shell (by prepending '-')" */ \
-     "\n       -w      Wait for PROG to exit" \
-
-#define openvt_example_usage \
-       "openvt 2 /bin/ash\n"
-
-/*
-#define parse_trivial_usage \
-       "[-n MAXTOKENS] [-m MINTOKENS] [-d DELIMS] [-f FLAGS] FILE..."
-#define parse_full_usage ""
-*/
-
-#define passwd_trivial_usage \
-       "[OPTIONS] [USER]"
-#define passwd_full_usage "\n\n" \
-       "Change USER's password. If no USER is specified,\n" \
-       "changes the password for the current user.\n" \
-     "\nOptions:" \
-     "\n       -a      Algorithm to use for password (des, md5)" /* ", sha1)" */ \
-     "\n       -d      Delete password for the account" \
-     "\n       -l      Lock (disable) account" \
-     "\n       -u      Unlock (re-enable) account" \
-
-#define chpasswd_trivial_usage \
-       IF_LONG_OPTS("[--md5|--encrypted]") IF_NOT_LONG_OPTS("[-m|-e]")
-#define chpasswd_full_usage "\n\n" \
-       "Read user:password from stdin and update /etc/passwd\n" \
-     "\nOptions:" \
-       IF_LONG_OPTS( \
-     "\n       -e,--encrypted  Supplied passwords are in encrypted form" \
-     "\n       -m,--md5        Use MD5 encryption instead of DES" \
-       ) \
-       IF_NOT_LONG_OPTS( \
-     "\n       -e      Supplied passwords are in encrypted form" \
-     "\n       -m      Use MD5 encryption instead of DES" \
-       )
-
-#define patch_trivial_usage \
-       "[OPTIONS] [ORIGFILE [PATCHFILE]]"
-#define patch_full_usage "\n\n" \
-       IF_LONG_OPTS( \
-       "       -p,--strip N    Strip N leading components from file names" \
-     "\n       -i,--input DIFF Read DIFF instead of stdin" \
-     "\n       -R,--reverse    Reverse patch" \
-     "\n       -N,--forward    Ignore already applied patches" \
-     "\n       --dry-run       Don't actually change files" \
-       ) \
-       IF_NOT_LONG_OPTS( \
-       "       -p N    Strip N leading components from file names" \
-     "\n       -i DIFF Read DIFF instead of stdin" \
-     "\n       -R      Reverse patch" \
-     "\n       -N      Ignore already applied patches" \
-       )
-
-#define patch_example_usage \
-       "$ patch -p1 < example.diff\n" \
-       "$ patch -p0 -i example.diff"
-
-#define pgrep_trivial_usage \
-       "[-flnovx] [-s SID|-P PPID|PATTERN]"
-#define pgrep_full_usage "\n\n" \
-       "Display process(es) selected by regex PATTERN\n" \
-     "\nOptions:" \
-     "\n       -l      Show command name too" \
-     "\n       -f      Match against entire command line" \
-     "\n       -n      Show the newest process only" \
-     "\n       -o      Show the oldest process only" \
-     "\n       -v      Negate the match" \
-     "\n       -x      Match whole name (not substring)" \
-     "\n       -s      Match session ID (0 for current)" \
-     "\n       -P      Match parent process ID" \
-
-#if (ENABLE_FEATURE_PIDOF_SINGLE || ENABLE_FEATURE_PIDOF_OMIT)
-#define pidof_trivial_usage \
-       "[OPTIONS] [NAME]..."
-#define USAGE_PIDOF "\n\nOptions:"
-#else
-#define pidof_trivial_usage \
-       "[NAME]..."
-#define USAGE_PIDOF /* none */
-#endif
-#define pidof_full_usage "\n\n" \
-       "List PIDs of all processes with names that match NAMEs" \
-       USAGE_PIDOF \
-       IF_FEATURE_PIDOF_SINGLE( \
-     "\n       -s      Show only one PID") \
-       IF_FEATURE_PIDOF_OMIT( \
-     "\n       -o PID  Omit given pid" \
-     "\n               Use %PPID to omit pid of pidof's parent") \
-
-#define pidof_example_usage \
-       "$ pidof init\n" \
-       "1\n" \
-       IF_FEATURE_PIDOF_OMIT( \
-       "$ pidof /bin/sh\n20351 5973 5950\n") \
-       IF_FEATURE_PIDOF_OMIT( \
-       "$ pidof /bin/sh -o %PPID\n20351 5950")
-
-#if !ENABLE_FEATURE_FANCY_PING
-#define ping_trivial_usage \
-       "host"
-#define ping_full_usage "\n\n" \
-       "Send ICMP ECHO_REQUEST packets to network hosts"
-#define ping6_trivial_usage \
-       "host"
-#define ping6_full_usage "\n\n" \
-       "Send ICMP ECHO_REQUEST packets to network hosts"
-#else
-#define ping_trivial_usage \
-       "[OPTIONS] HOST"
-#define ping_full_usage "\n\n" \
-       "Send ICMP ECHO_REQUEST packets to network hosts\n" \
-     "\nOptions:" \
-     "\n       -4, -6          Force IP or IPv6 name resolution" \
-     "\n       -c CNT          Send only CNT pings" \
-     "\n       -s SIZE         Send SIZE data bytes in packets (default:56)" \
-     "\n       -I IFACE/IP     Use interface or IP address as source" \
-     "\n       -W SEC          Seconds to wait for the first response (default:10)" \
-     "\n                       (after all -c CNT packets are sent)" \
-     "\n       -w SEC          Seconds until ping exits (default:infinite)" \
-     "\n                       (can exit earlier with -c CNT)" \
-     "\n       -q              Quiet, only displays output at start" \
-     "\n                       and when finished" \
-
-#define ping6_trivial_usage \
-       "[OPTIONS] HOST"
-#define ping6_full_usage "\n\n" \
-       "Send ICMP ECHO_REQUEST packets to network hosts\n" \
-     "\nOptions:" \
-     "\n       -c CNT          Send only CNT pings" \
-     "\n       -s SIZE         Send SIZE data bytes in packets (default:56)" \
-     "\n       -I IFACE/IP     Use interface or IP address as source" \
-     "\n       -q              Quiet, only displays output at start" \
-     "\n                       and when finished" \
-
-#endif
-#define ping_example_usage \
-       "$ ping localhost\n" \
-       "PING slag (127.0.0.1): 56 data bytes\n" \
-       "64 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=20.1 ms\n" \
-       "\n" \
-       "--- debian ping statistics ---\n" \
-       "1 packets transmitted, 1 packets received, 0% packet loss\n" \
-       "round-trip min/avg/max = 20.1/20.1/20.1 ms\n"
-#define ping6_example_usage \
-       "$ ping6 ip6-localhost\n" \
-       "PING ip6-localhost (::1): 56 data bytes\n" \
-       "64 bytes from ::1: icmp6_seq=0 ttl=64 time=20.1 ms\n" \
-       "\n" \
-       "--- ip6-localhost ping statistics ---\n" \
-       "1 packets transmitted, 1 packets received, 0% packet loss\n" \
-       "round-trip min/avg/max = 20.1/20.1/20.1 ms\n"
-
-#define pipe_progress_trivial_usage NOUSAGE_STR
-#define pipe_progress_full_usage ""
-
-#define pivot_root_trivial_usage \
-       "NEW_ROOT PUT_OLD"
-#define pivot_root_full_usage "\n\n" \
-       "Move the current root file system to PUT_OLD and make NEW_ROOT\n" \
-       "the new root file system"
-
-#define pkill_trivial_usage \
-       "[-l|-SIGNAL] [-fnovx] [-s SID|-P PPID|PATTERN]"
-#define pkill_full_usage "\n\n" \
-       "Send a signal to process(es) selected by regex PATTERN\n" \
-     "\nOptions:" \
-     "\n       -l      List all signals" \
-     "\n       -f      Match against entire command line" \
-     "\n       -n      Signal the newest process only" \
-     "\n       -o      Signal the oldest process only" \
-     "\n       -v      Negate the match" \
-     "\n       -x      Match whole name (not substring)" \
-     "\n       -s      Match session ID (0 for current)" \
-     "\n       -P      Match parent process ID" \
-
-#define popmaildir_trivial_usage \
-       "[OPTIONS] MAILDIR [CONN_HELPER ARGS]"
-#define popmaildir_full_usage "\n\n" \
-       "Fetch content of remote mailbox to local maildir\n" \
-     "\nOptions:" \
-/*   "\n       -b              Binary mode. Ignored" */ \
-/*   "\n       -d              Debug. Ignored" */ \
-/*   "\n       -m              Show used memory. Ignored" */ \
-/*   "\n       -V              Show version. Ignored" */ \
-/*   "\n       -c              Use tcpclient. Ignored" */ \
-/*   "\n       -a              Use APOP protocol. Implied. If server supports APOP -> use it" */ \
-     "\n       -s              Skip authorization" \
-     "\n       -T              Get messages with TOP instead of RETR" \
-     "\n       -k              Keep retrieved messages on the server" \
-     "\n       -t SEC          Network timeout" \
-       IF_FEATURE_POPMAILDIR_DELIVERY( \
-     "\n       -F \"PROG ARGS\"        Filter program (may be repeated)" \
-     "\n       -M \"PROG ARGS\"        Delivery program" \
-       ) \
-     "\n" \
-     "\nFetch from plain POP3 server:" \
-     "\npopmaildir -k DIR nc pop3.server.com 110 <user_and_pass.txt" \
-     "\nFetch from SSLed POP3 server and delete fetched emails:" \
-     "\npopmaildir DIR -- openssl s_client -quiet -connect pop3.server.com:995 <user_and_pass.txt"
-/*   "\n       -R BYTES        Remove old messages on the server >= BYTES. Ignored" */
-/*   "\n       -Z N1-N2        Remove messages from N1 to N2 (dangerous). Ignored" */
-/*   "\n       -L BYTES        Don't retrieve new messages >= BYTES. Ignored" */
-/*   "\n       -H LINES        Type first LINES of a message. Ignored" */
-#define popmaildir_example_usage \
-       "$ popmaildir -k ~/Maildir -- nc pop.drvv.ru 110 [<password_file]\n" \
-       "$ popmaildir ~/Maildir -- openssl s_client -quiet -connect pop.gmail.com:995 [<password_file]\n"
-
-#define poweroff_trivial_usage \
-       "[-d DELAY] [-n] [-f]"
-#define poweroff_full_usage "\n\n" \
-       "Halt and shut off power\n" \
-     "\nOptions:" \
-     "\n       -d      Delay interval for halting" \
-     "\n       -n      Do not sync" \
-     "\n       -f      Force power off (don't go through init)" \
-
-#define printenv_trivial_usage \
-       "[VARIABLE]..."
-#define printenv_full_usage "\n\n" \
-       "Print environment VARIABLEs.\n" \
-       "If no VARIABLE specified, print all."
-
-#define printf_trivial_usage \
-       "FORMAT [ARGUMENT]..."
-#define printf_full_usage "\n\n" \
-       "Format and print ARGUMENT(s) according to FORMAT,\n" \
-       "where FORMAT controls the output exactly as in C printf"
-#define printf_example_usage \
-       "$ printf \"Val=%d\\n\" 5\n" \
-       "Val=5\n"
-
-
-#if ENABLE_DESKTOP
-
-#define ps_trivial_usage \
-       "[-o COL1,COL2=HEADER]" IF_FEATURE_SHOW_THREADS(" [-T]")
-#define ps_full_usage "\n\n" \
-       "Show list of processes\n" \
-     "\nOptions:" \
-     "\n       -o COL1,COL2=HEADER     Select columns for display" \
-       IF_FEATURE_SHOW_THREADS( \
-     "\n       -T                      Show threads" \
-       )
-
-#else /* !ENABLE_DESKTOP */
-
-#if !ENABLE_SELINUX && !ENABLE_FEATURE_PS_WIDE
-#define USAGE_PS "\nThis version of ps accepts no options"
-#else
-#define USAGE_PS "\nOptions:"
-#endif
-
-#define ps_trivial_usage \
-       ""
-#define ps_full_usage "\n\n" \
-       "Show list of processes\n" \
-       USAGE_PS \
-       IF_SELINUX( \
-     "\n       -Z      Show selinux context" \
-       ) \
-       IF_FEATURE_PS_WIDE( \
-     "\n       w       Wide output" \
-       )
-
-#endif /* ENABLE_DESKTOP */
-
-#define ps_example_usage \
-       "$ ps\n" \
-       "  PID  Uid      Gid State Command\n" \
-       "    1 root     root     S init\n" \
-       "    2 root     root     S [kflushd]\n" \
-       "    3 root     root     S [kupdate]\n" \
-       "    4 root     root     S [kpiod]\n" \
-       "    5 root     root     S [kswapd]\n" \
-       "  742 andersen andersen S [bash]\n" \
-       "  743 andersen andersen S -bash\n" \
-       "  745 root     root     S [getty]\n" \
-       " 2990 andersen andersen R ps\n" \
-
-#define pscan_trivial_usage \
-       "[-cb] [-p MIN_PORT] [-P MAX_PORT] [-t TIMEOUT] [-T MIN_RTT] HOST"
-#define pscan_full_usage "\n\n" \
-       "Scan a host, print all open ports\n" \
-     "\nOptions:" \
-     "\n       -c      Show closed ports too" \
-     "\n       -b      Show blocked ports too" \
-     "\n       -p      Scan from this port (default 1)" \
-     "\n       -P      Scan up to this port (default 1024)" \
-     "\n       -t      Timeout (default 5000 ms)" \
-     "\n       -T      Minimum rtt (default 5 ms, increase for congested hosts)" \
-
-#define pwd_trivial_usage \
-       ""
-#define pwd_full_usage "\n\n" \
-       "Print the full filename of the current working directory"
-#define pwd_example_usage \
-       "$ pwd\n" \
-       "/root\n"
-
-#define raidautorun_trivial_usage \
-       "DEVICE"
-#define raidautorun_full_usage "\n\n" \
-       "Tell the kernel to automatically search and start RAID arrays"
-#define raidautorun_example_usage \
-       "$ raidautorun /dev/md0"
-
-#define rdate_trivial_usage \
-       "[-sp] HOST"
-#define rdate_full_usage "\n\n" \
-       "Get and possibly set the system date and time from a remote HOST\n" \
-     "\nOptions:" \
-     "\n       -s      Set the system date and time (default)" \
-     "\n       -p      Print the date and time" \
-
-#define rdev_trivial_usage \
-       ""
-#define rdev_full_usage "\n\n" \
-       "Print the device node associated with the filesystem mounted at '/'"
-#define rdev_example_usage \
-       "$ rdev\n" \
-       "/dev/mtdblock9 /\n"
-
-#define readahead_trivial_usage \
-       "[FILE]..."
-#define readahead_full_usage "\n\n" \
-       "Preload FILEs to RAM"
-
-#define readlink_trivial_usage \
-       IF_FEATURE_READLINK_FOLLOW("[-fnv] ") "FILE"
-#define readlink_full_usage "\n\n" \
-       "Display the value of a symlink" \
-       IF_FEATURE_READLINK_FOLLOW( "\n" \
-     "\nOptions:" \
-     "\n       -f      Canonicalize by following all symlinks" \
-     "\n       -n      Don't add newline" \
-     "\n       -v      Verbose" \
-       ) \
-
-#define readprofile_trivial_usage \
-       "[OPTIONS]"
-#define readprofile_full_usage "\n\n" \
-       "Options:" \
-     "\n       -m mapfile      (Default: /boot/System.map)" \
-     "\n       -p profile      (Default: /proc/profile)" \
-     "\n       -M NUM          Set the profiling multiplier to NUM" \
-     "\n       -i              Print only info about the sampling step" \
-     "\n       -v              Verbose" \
-     "\n       -a              Print all symbols, even if count is 0" \
-     "\n       -b              Print individual histogram-bin counts" \
-     "\n       -s              Print individual counters within functions" \
-     "\n       -r              Reset all the counters (root only)" \
-     "\n       -n              Disable byte order auto-detection" \
-
-#define realpath_trivial_usage \
-       "FILE..."
-#define realpath_full_usage "\n\n" \
-       "Return the absolute pathnames of given FILE"
-
-#define reboot_trivial_usage \
-       "[-d DELAY] [-n] [-f]"
-#define reboot_full_usage "\n\n" \
-       "Reboot the system\n" \
-     "\nOptions:" \
-     "\n       -d      Delay interval for rebooting" \
-     "\n       -n      No call to sync()" \
-     "\n       -f      Force reboot (don't go through init)" \
-
-#define reformime_trivial_usage \
-       "[OPTIONS] [FILE]..."
-#define reformime_full_usage "\n\n" \
-       "Parse MIME-encoded message\n" \
-     "\nOptions:" \
-     "\n       -x PREFIX       Extract content of MIME sections to files" \
-     "\n       -X PROG ARGS    Filter content of MIME sections through PROG" \
-     "\n                       Must be the last option" \
-     "\n" \
-     "\nOther options are silently ignored" \
-
-#define scriptreplay_trivial_usage \
-       "timingfile [typescript [divisor]]"
-#define scriptreplay_full_usage "\n\n" \
-       "Play back typescripts, using timing information"
-
-#define reset_trivial_usage \
-       ""
-#define reset_full_usage "\n\n" \
-       "Reset the screen"
-
-#define resize_trivial_usage \
-       ""
-#define resize_full_usage "\n\n" \
-       "Resize the screen"
-
-#define restorecon_trivial_usage \
-       "[-iFnRv] [-e EXCLUDEDIR]... [-o FILE] [-f FILE]"
-#define restorecon_full_usage "\n\n" \
-       "Reset security contexts of files in pathname\n" \
-     "\n       -i      Ignore files that don't exist" \
-     "\n       -f FILE File with list of files to process" \
-     "\n       -e DIR  Directory to exclude" \
-     "\n       -R,-r   Recurse" \
-     "\n       -n      Don't change any file labels" \
-     "\n       -o FILE Save list of files with incorrect context" \
-     "\n       -v      Verbose" \
-     "\n       -vv     Show changed labels" \
-     "\n       -F      Force reset of context to match file_context" \
-     "\n               for customizable files, or the user section," \
-     "\n               if it has changed" \
-
-#define rfkill_trivial_usage \
-       "COMMAND [INDEX|TYPE]"
-#define rfkill_full_usage "\n\n" \
-       "Enable/disable wireless devices\n" \
-       "\nCommands:" \
-     "\n       list [INDEX|TYPE]       List current state" \
-     "\n       block INDEX|TYPE        Disable device" \
-     "\n       unblock INDEX|TYPE      Enable device" \
-     "\n" \
-     "\n       TYPE: all, wlan(wifi), bluetooth, uwb(ultrawideband)," \
-     "\n               wimax, wwan, gps, fm" \
-
-#define rm_trivial_usage \
-       "[OPTIONS] FILE..."
-#define rm_full_usage "\n\n" \
-       "Remove (unlink) FILEs\n" \
-     "\nOptions:" \
-     "\n       -i      Always prompt before removing" \
-     "\n       -f      Never prompt" \
-     "\n       -R,-r   Recurse" \
-
-#define rm_example_usage \
-       "$ rm -rf /tmp/foo\n"
-
-#define rmdir_trivial_usage \
-       "[OPTIONS] DIRECTORY..."
-#define rmdir_full_usage "\n\n" \
-       "Remove DIRECTORY if it is empty\n" \
-     "\nOptions:" \
-       IF_FEATURE_RMDIR_LONG_OPTIONS( \
-     "\n       -p|--parents    Include parents" \
-     "\n       --ignore-fail-on-non-empty" \
-       ) \
-       IF_NOT_FEATURE_RMDIR_LONG_OPTIONS( \
-     "\n       -p      Include parents" \
-       )
-
-#define rmdir_example_usage \
-       "# rmdir /tmp/foo\n"
-
-#define rmmod_trivial_usage \
-       "[OPTIONS] [MODULE]..."
-#define rmmod_full_usage "\n\n" \
-       "Unload the specified kernel modules from the kernel\n" \
-     "\nOptions:" \
-     "\n       -w      Wait until the module is no longer used" \
-     "\n       -f      Force unloading" \
-     "\n       -a      Remove all unused modules (recursively)" \
-
-#define rmmod_example_usage \
-       "$ rmmod tulip\n"
-
-#define route_trivial_usage \
-       "[{add|del|delete}]"
-#define route_full_usage "\n\n" \
-       "Edit kernel routing tables\n" \
-     "\nOptions:" \
-     "\n       -n      Don't resolve names" \
-     "\n       -e      Display other/more information" \
-     "\n       -A inet" IF_FEATURE_IPV6("{6}") "       Select address family" \
-
-#define rpm_trivial_usage \
-       "-i PACKAGE.rpm; rpm -qp[ildc] PACKAGE.rpm"
-#define rpm_full_usage "\n\n" \
-       "Manipulate RPM packages\n" \
-     "\nCommands:" \
-     "\n       -i      Install package" \
-     "\n       -qp     Query package" \
-     "\nOptions:" \
-     "\n       -i      Show information" \
-     "\n       -l      List contents" \
-     "\n       -d      List documents" \
-     "\n       -c      List config files" \
-
-#define rpm2cpio_trivial_usage \
-       "package.rpm"
-#define rpm2cpio_full_usage "\n\n" \
-       "Output a cpio archive of the rpm file"
-
-#define rtcwake_trivial_usage \
-       "[-a | -l | -u] [-d DEV] [-m MODE] [-s SEC | -t TIME]"
-#define rtcwake_full_usage "\n\n" \
-       "Enter a system sleep state until specified wakeup time\n" \
-       IF_LONG_OPTS( \
-     "\n       -a,--auto       Read clock mode from adjtime" \
-     "\n       -l,--local      Clock is set to local time" \
-     "\n       -u,--utc        Clock is set to UTC time" \
-     "\n       -d,--device=DEV Specify the RTC device" \
-     "\n       -m,--mode=MODE  Set the sleep state (default: standby)" \
-     "\n       -s,--seconds=SEC Set the timeout in SEC seconds from now" \
-     "\n       -t,--time=TIME  Set the timeout to TIME seconds from epoch" \
-       ) \
-       IF_NOT_LONG_OPTS( \
-     "\n       -a      Read clock mode from adjtime" \
-     "\n       -l      Clock is set to local time" \
-     "\n       -u      Clock is set to UTC time" \
-     "\n       -d DEV  Specify the RTC device" \
-     "\n       -m MODE Set the sleep state (default: standby)" \
-     "\n       -s SEC  Set the timeout in SEC seconds from now" \
-     "\n       -t TIME Set the timeout to TIME seconds from epoch" \
-       )
-
-#define runcon_trivial_usage \
-       "[-c] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] PROG ARGS\n" \
-       "runcon CONTEXT PROG ARGS"
-#define runcon_full_usage "\n\n" \
-       "Run PROG in a different security context\n" \
-     "\n       CONTEXT         Complete security context\n" \
-       IF_FEATURE_RUNCON_LONG_OPTIONS( \
-     "\n       -c,--compute    Compute process transition context before modifying" \
-     "\n       -t,--type=TYPE  Type (for same role as parent)" \
-     "\n       -u,--user=USER  User identity" \
-     "\n       -r,--role=ROLE  Role" \
-     "\n       -l,--range=RNG  Levelrange" \
-       ) \
-       IF_NOT_FEATURE_RUNCON_LONG_OPTIONS( \
-     "\n       -c      Compute process transition context before modifying" \
-     "\n       -t TYPE Type (for same role as parent)" \
-     "\n       -u USER User identity" \
-     "\n       -r ROLE Role" \
-     "\n       -l RNG  Levelrange" \
-       )
-
-#define run_parts_trivial_usage \
-       "[-t] "IF_FEATURE_RUN_PARTS_FANCY("[-l] ")"[-a ARG] [-u MASK] DIRECTORY"
-#define run_parts_full_usage "\n\n" \
-       "Run a bunch of scripts in DIRECTORY\n" \
-     "\nOptions:" \
-     "\n       -t      Print what would be run, but don't actually run anything" \
-     "\n       -a ARG  Pass ARG as argument for every program" \
-     "\n       -u MASK Set the umask to MASK before running every program" \
-       IF_FEATURE_RUN_PARTS_FANCY( \
-     "\n       -l      Print names of all matching files even if they are not executable" \
-       )
-
-#define run_parts_example_usage \
-       "$ run-parts -a start /etc/init.d\n" \
-       "$ run-parts -a stop=now /etc/init.d\n\n" \
-       "Let's assume you have a script foo/dosomething:\n" \
-       "#!/bin/sh\n" \
-       "for i in $*; do eval $i; done; unset i\n" \
-       "case \"$1\" in\n" \
-       "start*) echo starting something;;\n" \
-       "stop*) set -x; shutdown -h $stop;;\n" \
-       "esac\n\n" \
-       "Running this yields:\n" \
-       "$run-parts -a stop=+4m foo/\n" \
-       "+ shutdown -h +4m"
-
-#define runlevel_trivial_usage \
-       "[FILE]"
-#define runlevel_full_usage "\n\n" \
-       "Find the current and previous system runlevel\n" \
-       "\n" \
-       "If no utmp FILE exists or if no runlevel record can be found,\n" \
-       "print \"unknown\""
-#define runlevel_example_usage \
-       "$ runlevel /var/run/utmp\n" \
-       "N 2"
-
-#define runsv_trivial_usage \
-       "DIR"
-#define runsv_full_usage "\n\n" \
-       "Start and monitor a service and optionally an appendant log service"
-
-#define runsvdir_trivial_usage \
-       "[-P] [-s SCRIPT] DIR"
-#define runsvdir_full_usage "\n\n" \
-       "Start a runsv process for each subdirectory. If it exits, restart it.\n" \
-     "\n       -P              Put each runsv in a new session" \
-     "\n       -s SCRIPT       Run SCRIPT <signo> after signal is processed" \
-
-#define rx_trivial_usage \
-       "FILE"
-#define rx_full_usage "\n\n" \
-       "Receive a file using the xmodem protocol"
-#define rx_example_usage \
-       "$ rx /tmp/foo\n"
-
-#define script_trivial_usage \
-       "[-afq" IF_SCRIPTREPLAY("t") "] [-c PROG] [OUTFILE]"
-#define script_full_usage "\n\n" \
-       "Options:" \
-     "\n       -a      Append output" \
-     "\n       -c PROG Run PROG, not shell" \
-     "\n       -f      Flush output after each write" \
-     "\n       -q      Quiet" \
-       IF_SCRIPTREPLAY( \
-     "\n       -t      Send timing to stderr" \
-       )
-
-#define sed_trivial_usage \
-       "[-efinr] SED_CMD [FILE]..."
-#define sed_full_usage "\n\n" \
-       "Options:" \
-     "\n       -e CMD  Add CMD to sed commands to be executed" \
-     "\n       -f FILE Add FILE contents to sed commands to be executed" \
-     "\n       -i      Edit files in-place (else sends result to stdout)" \
-     "\n       -n      Suppress automatic printing of pattern space" \
-     "\n       -r      Use extended regex syntax" \
-     "\n" \
-     "\nIf no -e or -f, the first non-option argument is the sed command string." \
-     "\nRemaining arguments are input files (stdin if none)."
-
-#define sed_example_usage \
-       "$ echo \"foo\" | sed -e 's/f[a-zA-Z]o/bar/g'\n" \
-       "bar\n"
-
-#define selinuxenabled_trivial_usage NOUSAGE_STR
-#define selinuxenabled_full_usage ""
-
-#define sendmail_trivial_usage \
-       "[OPTIONS] [RECIPIENT_EMAIL]..."
-#define sendmail_full_usage "\n\n" \
-       "Read email from stdin and send it\n" \
-     "\nStandard options:" \
-     "\n       -t              Read additional recipients from message body" \
-     "\n       -f sender       Sender (required)" \
-     "\n       -o options      Various options. -oi implied, others are ignored" \
-     "\n       -i              -oi synonym. implied and ignored" \
-     "\n" \
-     "\nBusybox specific options:" \
-     "\n       -w seconds      Network timeout" \
-     "\n       -H 'PROG ARGS'  Run connection helper" \
-     "\n                       Examples:" \
-     "\n                       -H 'exec openssl s_client -quiet -tls1 -starttls smtp" \
-     "\n                               -connect smtp.gmail.com:25' <email.txt" \
-     "\n                               [4<username_and_passwd.txt | -au<username> -ap<password>]" \
-     "\n                       -H 'exec openssl s_client -quiet -tls1" \
-     "\n                               -connect smtp.gmail.com:465' <email.txt" \
-     "\n                               [4<username_and_passwd.txt | -au<username> -ap<password>]" \
-     "\n       -S server[:port] Server" \
-     "\n       -au<username>   Username for AUTH LOGIN" \
-     "\n       -ap<password>   Password for AUTH LOGIN" \
-     "\n       -am<method>     Authentication method. Ignored. LOGIN is implied" \
-     "\n" \
-     "\nOther options are silently ignored; -oi -t is implied" \
-       IF_MAKEMIME( \
-     "\nUse makemime applet to create message with attachments" \
-       )
-
-#define seq_trivial_usage \
-       "[-w] [-s SEP] [FIRST [INC]] LAST"
-#define seq_full_usage "\n\n" \
-       "Print numbers from FIRST to LAST, in steps of INC.\n" \
-       "FIRST, INC default to 1.\n" \
-     "\nOptions:" \
-     "\n       -w      Pad to last with leading zeros" \
-     "\n       -s SEP  String separator" \
-
-#define sestatus_trivial_usage \
-       "[-vb]"
-#define sestatus_full_usage "\n\n" \
-       "       -v      Verbose" \
-     "\n       -b      Display current state of booleans" \
-
-#define setconsole_trivial_usage \
-       "[-r" IF_FEATURE_SETCONSOLE_LONG_OPTIONS("|--reset") "] [DEVICE]"
-#define setconsole_full_usage "\n\n" \
-       "Redirect system console output to DEVICE (default: /dev/tty)\n" \
-     "\nOptions:" \
-     "\n       -r      Reset output to /dev/console" \
-
-#define setenforce_trivial_usage \
-       "[Enforcing | Permissive | 1 | 0]"
-#define setenforce_full_usage ""
-
-#define setfiles_trivial_usage \
-       "[-dnpqsvW] [-e DIR]... [-o FILE] [-r alt_root_path]" \
-       IF_FEATURE_SETFILES_CHECK_OPTION( \
-       " [-c policyfile] spec_file" \
-       ) \
-       " pathname"
-#define setfiles_full_usage "\n\n" \
-       "Reset file contexts under pathname according to spec_file\n" \
-       IF_FEATURE_SETFILES_CHECK_OPTION( \
-     "\n       -c FILE Check the validity of the contexts against the specified binary policy" \
-       ) \
-     "\n       -d      Show which specification matched each file" \
-     "\n       -l      Log changes in file labels to syslog" \
-     "\n       -n      Don't change any file labels" \
-     "\n       -q      Suppress warnings" \
-     "\n       -r DIR  Use an alternate root path" \
-     "\n       -e DIR  Exclude DIR" \
-     "\n       -F      Force reset of context to match file_context for customizable files" \
-     "\n       -o FILE Save list of files with incorrect context" \
-     "\n       -s      Take a list of files from stdin (instead of command line)" \
-     "\n       -v      Show changes in file labels, if type or role are changing" \
-     "\n       -vv     Show changes in file labels, if type, role, or user are changing" \
-     "\n       -W      Display warnings about entries that had no matching files" \
-
-#define setfont_trivial_usage \
-       "FONT [-m MAPFILE] [-C TTY]"
-#define setfont_full_usage "\n\n" \
-       "Load a console font\n" \
-     "\nOptions:" \
-     "\n       -m MAPFILE      Load console screen map" \
-     "\n       -C TTY          Affect TTY instead of /dev/tty" \
-
-#define setfont_example_usage \
-       "$ setfont -m koi8-r /etc/i18n/fontname\n"
-
-#define setkeycodes_trivial_usage \
-       "SCANCODE KEYCODE..."
-#define setkeycodes_full_usage "\n\n" \
-       "Set entries into the kernel's scancode-to-keycode map,\n" \
-       "allowing unusual keyboards to generate usable keycodes.\n\n" \
-       "SCANCODE may be either xx or e0xx (hexadecimal),\n" \
-       "and KEYCODE is given in decimal." \
-
-#define setkeycodes_example_usage \
-       "$ setkeycodes e030 127\n"
-
-#define setlogcons_trivial_usage \
-       "N"
-#define setlogcons_full_usage "\n\n" \
-       "Redirect the kernel output to console N (0 for current)"
-
-#define setsebool_trivial_usage \
-       "boolean value"
-
-#define setsebool_full_usage "\n\n" \
-       "Change boolean setting"
-
-#define setsid_trivial_usage \
-       "PROG ARGS"
-#define setsid_full_usage "\n\n" \
-       "Run PROG in a new session. PROG will have no controlling terminal\n" \
-       "and will not be affected by keyboard signals (Ctrl-C etc).\n" \
-       "See setsid(2) for details." \
-
-#define last_trivial_usage \
-       ""IF_FEATURE_LAST_FANCY("[-HW] [-f FILE]")
-#define last_full_usage "\n\n" \
-       "Show listing of the last users that logged into the system" \
-       IF_FEATURE_LAST_FANCY( "\n" \
-     "\nOptions:" \
-/*   "\n       -H      Show header line" */ \
-     "\n       -W      Display with no host column truncation" \
-     "\n       -f FILE Read from FILE instead of /var/log/wtmp" \
-       )
-
-#define showkey_trivial_usage \
-       "[-a | -k | -s]"
-#define showkey_full_usage "\n\n" \
-       "Show keys pressed\n" \
-     "\nOptions:" \
-     "\n       -a      Display decimal/octal/hex values of the keys" \
-     "\n       -k      Display interpreted keycodes (default)" \
-     "\n       -s      Display raw scan-codes" \
-
-#define slattach_trivial_usage \
-       "[-cehmLF] [-s SPEED] [-p PROTOCOL] DEVICE"
-#define slattach_full_usage "\n\n" \
-       "Attach network interface(s) to serial line(s)\n" \
-     "\nOptions:" \
-     "\n       -p PROT Set protocol (slip, cslip, slip6, clisp6 or adaptive)" \
-     "\n       -s SPD  Set line speed" \
-     "\n       -e      Exit after initializing device" \
-     "\n       -h      Exit when the carrier is lost" \
-     "\n       -c PROG Run PROG when the line is hung up" \
-     "\n       -m      Do NOT initialize the line in raw 8 bits mode" \
-     "\n       -L      Enable 3-wire operation" \
-     "\n       -F      Disable RTS/CTS flow control" \
-
-#define sleep_trivial_usage \
-       IF_FEATURE_FANCY_SLEEP("[") "N" IF_FEATURE_FANCY_SLEEP("]...")
-#define sleep_full_usage "\n\n" \
-       IF_NOT_FEATURE_FANCY_SLEEP("Pause for N seconds") \
-       IF_FEATURE_FANCY_SLEEP( \
-       "Pause for a time equal to the total of the args given, where each arg can\n" \
-       "have an optional suffix of (s)econds, (m)inutes, (h)ours, or (d)ays")
-#define sleep_example_usage \
-       "$ sleep 2\n" \
-       "[2 second delay results]\n" \
-       IF_FEATURE_FANCY_SLEEP( \
-       "$ sleep 1d 3h 22m 8s\n" \
-       "[98528 second delay results]\n")
-
-#define sort_trivial_usage \
-       "[-nru" \
-       IF_FEATURE_SORT_BIG("gMcszbdfimSTokt] [-o FILE] [-k start[.offset][opts][,end[.offset][opts]] [-t CHAR") \
-       "] [FILE]..."
-#define sort_full_usage "\n\n" \
-       "Sort lines of text\n" \
-     "\nOptions:" \
-       IF_FEATURE_SORT_BIG( \
-     "\n       -b      Ignore leading blanks" \
-     "\n       -c      Check whether input is sorted" \
-     "\n       -d      Dictionary order (blank or alphanumeric only)" \
-     "\n       -f      Ignore case" \
-     "\n       -g      General numerical sort" \
-     "\n       -i      Ignore unprintable characters" \
-     "\n       -k      Sort key" \
-     "\n       -M      Sort month" \
-       ) \
-     "\n       -n      Sort numbers" \
-       IF_FEATURE_SORT_BIG( \
-     "\n       -o      Output to file" \
-     "\n       -k      Sort by key" \
-     "\n       -t CHAR Key separator" \
-       ) \
-     "\n       -r      Reverse sort order" \
-       IF_FEATURE_SORT_BIG( \
-     "\n       -s      Stable (don't sort ties alphabetically)" \
-       ) \
-     "\n       -u      Suppress duplicate lines" \
-       IF_FEATURE_SORT_BIG( \
-     "\n       -z      Lines are terminated by NUL, not newline" \
-     "\n       -mST    Ignored for GNU compatibility") \
-
-#define sort_example_usage \
-       "$ echo -e \"e\\nf\\nb\\nd\\nc\\na\" | sort\n" \
-       "a\n" \
-       "b\n" \
-       "c\n" \
-       "d\n" \
-       "e\n" \
-       "f\n" \
-       IF_FEATURE_SORT_BIG( \
-               "$ echo -e \"c 3\\nb 2\\nd 2\" | $SORT -k 2,2n -k 1,1r\n" \
-               "d 2\n" \
-               "b 2\n" \
-               "c 3\n" \
-       ) \
-       ""
-
-#define split_trivial_usage \
-       "[OPTIONS] [INPUT [PREFIX]]"
-#define split_full_usage "\n\n" \
-       "Options:" \
-     "\n       -b n[k|m]       Split by bytes" \
-     "\n       -l n            Split by lines" \
-     "\n       -a n            Use n letters as suffix" \
-
-#define split_example_usage \
-       "$ split TODO foo\n" \
-       "$ cat TODO | split -a 2 -l 2 TODO_\n"
-
-#define start_stop_daemon_trivial_usage \
-       "[OPTIONS] [-S|-K] ... [-- ARGS...]"
-#define start_stop_daemon_full_usage "\n\n" \
-       "Search for matching processes, and then\n" \
-       "-K: stop all matching processes.\n" \
-       "-S: start a process unless a matching process is found.\n" \
-       IF_FEATURE_START_STOP_DAEMON_LONG_OPTIONS( \
-     "\nProcess matching:" \
-     "\n       -u,--user USERNAME|UID  Match only this user's processes" \
-     "\n       -n,--name NAME          Match processes with NAME" \
-     "\n                               in comm field in /proc/PID/stat" \
-     "\n       -x,--exec EXECUTABLE    Match processes with this command" \
-     "\n                               in /proc/PID/cmdline" \
-     "\n       -p,--pidfile FILE       Match a process with PID from the file" \
-     "\n       All specified conditions must match" \
-     "\n-S only:" \
-     "\n       -x,--exec EXECUTABLE    Program to run" \
-     "\n       -a,--startas NAME       Zeroth argument" \
-     "\n       -b,--background         Background" \
-       IF_FEATURE_START_STOP_DAEMON_FANCY( \
-     "\n       -N,--nicelevel N        Change nice level" \
-       ) \
-     "\n       -c,--chuid USER[:[GRP]] Change to user/group" \
-     "\n       -m,--make-pidfile       Write PID to the pidfile specified by -p" \
-     "\n-K only:" \
-     "\n       -s,--signal SIG         Signal to send" \
-     "\n       -t,--test               Match only, exit with 0 if a process is found" \
-     "\nOther:" \
-       IF_FEATURE_START_STOP_DAEMON_FANCY( \
-     "\n       -o,--oknodo             Exit with status 0 if nothing is done" \
-     "\n       -v,--verbose            Verbose" \
-       ) \
-     "\n       -q,--quiet              Quiet" \
-       ) \
-       IF_NOT_FEATURE_START_STOP_DAEMON_LONG_OPTIONS( \
-     "\nProcess matching:" \
-     "\n       -u USERNAME|UID Match only this user's processes" \
-     "\n       -n NAME         Match processes with NAME" \
-     "\n                       in comm field in /proc/PID/stat" \
-     "\n       -x EXECUTABLE   Match processes with this command" \
-     "\n                       command in /proc/PID/cmdline" \
-     "\n       -p FILE         Match a process with PID from the file" \
-     "\n       All specified conditions must match" \
-     "\n-S only:" \
-     "\n       -x EXECUTABLE   Program to run" \
-     "\n       -a NAME         Zeroth argument" \
-     "\n       -b              Background" \
-       IF_FEATURE_START_STOP_DAEMON_FANCY( \
-     "\n       -N N            Change nice level" \
-       ) \
-     "\n       -c USER[:[GRP]] Change to user/group" \
-     "\n       -m              Write PID to the pidfile specified by -p" \
-     "\n-K only:" \
-     "\n       -s SIG          Signal to send" \
-     "\n       -t              Match only, exit with 0 if a process is found" \
-     "\nOther:" \
-       IF_FEATURE_START_STOP_DAEMON_FANCY( \
-     "\n       -o              Exit with status 0 if nothing is done" \
-     "\n       -v              Verbose" \
-       ) \
-     "\n       -q              Quiet" \
-       ) \
-
-#define stat_trivial_usage \
-       "[OPTIONS] FILE..."
-#define stat_full_usage "\n\n" \
-       "Display file (default) or filesystem status\n" \
-     "\nOptions:" \
-       IF_FEATURE_STAT_FORMAT( \
-     "\n       -c fmt  Use the specified format" \
-       ) \
-     "\n       -f      Display filesystem status" \
-     "\n       -L      Follow links" \
-     "\n       -t      Display info in terse form" \
-       IF_SELINUX( \
-     "\n       -Z      Print security context" \
-       ) \
-       IF_FEATURE_STAT_FORMAT( \
-       "\n\nValid format sequences for files:\n" \
-       " %a    Access rights in octal\n" \
-       " %A    Access rights in human readable form\n" \
-       " %b    Number of blocks allocated (see %B)\n" \
-       " %B    The size in bytes of each block reported by %b\n" \
-       " %d    Device number in decimal\n" \
-       " %D    Device number in hex\n" \
-       " %f    Raw mode in hex\n" \
-       " %F    File type\n" \
-       " %g    Group ID of owner\n" \
-       " %G    Group name of owner\n" \
-       " %h    Number of hard links\n" \
-       " %i    Inode number\n" \
-       " %n    File name\n" \
-       " %N    File name, with -> TARGET if symlink\n" \
-       " %o    I/O block size\n" \
-       " %s    Total size, in bytes\n" \
-       " %t    Major device type in hex\n" \
-       " %T    Minor device type in hex\n" \
-       " %u    User ID of owner\n" \
-       " %U    User name of owner\n" \
-       " %x    Time of last access\n" \
-       " %X    Time of last access as seconds since Epoch\n" \
-       " %y    Time of last modification\n" \
-       " %Y    Time of last modification as seconds since Epoch\n" \
-       " %z    Time of last change\n" \
-       " %Z    Time of last change as seconds since Epoch\n" \
-       "\nValid format sequences for file systems:\n" \
-       " %a    Free blocks available to non-superuser\n" \
-       " %b    Total data blocks in file system\n" \
-       " %c    Total file nodes in file system\n" \
-       " %d    Free file nodes in file system\n" \
-       " %f    Free blocks in file system\n" \
-       IF_SELINUX( \
-       " %C    Security context in selinux\n" \
-       ) \
-       " %i    File System ID in hex\n" \
-       " %l    Maximum length of filenames\n" \
-       " %n    File name\n" \
-       " %s    Block size (for faster transfer)\n" \
-       " %S    Fundamental block size (for block counts)\n" \
-       " %t    Type in hex\n" \
-       " %T    Type in human readable form" \
-       ) \
-
-#define strings_trivial_usage \
-       "[-afo] [-n LEN] [FILE]..."
-#define strings_full_usage "\n\n" \
-       "Display printable strings in a binary file\n" \
-     "\nOptions:" \
-     "\n       -a      Scan whole file (default)" \
-     "\n       -f      Precede strings with filenames" \
-     "\n       -n LEN  At least LEN characters form a string (default 4)" \
-     "\n       -o      Precede strings with decimal offsets" \
-
-#define stty_trivial_usage \
-       "[-a|g] [-F DEVICE] [SETTING]..."
-#define stty_full_usage "\n\n" \
-       "Without arguments, prints baud rate, line discipline,\n" \
-       "and deviations from stty sane\n" \
-     "\nOptions:" \
-     "\n       -F DEVICE       Open device instead of stdin" \
-     "\n       -a              Print all current settings in human-readable form" \
-     "\n       -g              Print in stty-readable form" \
-     "\n       [SETTING]       See manpage" \
-
-#define su_trivial_usage \
-       "[OPTIONS] [-] [USERNAME]"
-#define su_full_usage "\n\n" \
-       "Change user id or become root\n" \
-     "\nOptions:" \
-     "\n       -p,-m   Preserve environment" \
-     "\n       -c CMD  Command to pass to 'sh -c'" \
-     "\n       -s SH   Shell to use instead of default shell" \
-
-#define sulogin_trivial_usage \
-       "[-t N] [TTY]"
-#define sulogin_full_usage "\n\n" \
-       "Single user login\n" \
-     "\nOptions:" \
-     "\n       -t N    Timeout" \
-
-#define sum_trivial_usage \
-       "[-rs] [FILE]..."
-#define sum_full_usage "\n\n" \
-       "Checksum and count the blocks in a file\n" \
-     "\nOptions:" \
-     "\n       -r      Use BSD sum algorithm (1K blocks)" \
-     "\n       -s      Use System V sum algorithm (512byte blocks)" \
-
-#define sv_trivial_usage \
-       "[-v] [-w SEC] CMD SERVICE_DIR..."
-#define sv_full_usage "\n\n" \
-       "Control services monitored by runsv supervisor.\n" \
-       "Commands (only first character is enough):\n" \
-       "\n" \
-       "status: query service status\n" \
-       "up: if service isn't running, start it. If service stops, restart it\n" \
-       "once: like 'up', but if service stops, don't restart it\n" \
-       "down: send TERM and CONT signals. If ./run exits, start ./finish\n" \
-       "       if it exists. After it stops, don't restart service\n" \
-       "exit: send TERM and CONT signals to service and log service. If they exit,\n" \
-       "       runsv exits too\n" \
-       "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n" \
-       "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service" \
-
-#define svlogd_trivial_usage \
-       "[-ttv] [-r C] [-R CHARS] [-l MATCHLEN] [-b BUFLEN] DIR..."
-#define svlogd_full_usage "\n\n" \
-       "Continuously read log data from stdin, optionally\n" \
-       "filter log messages, and write the data to one or more automatically\n" \
-       "rotated logs" \
-
-#define swapoff_trivial_usage \
-       "[-a] [DEVICE]"
-#define swapoff_full_usage "\n\n" \
-       "Stop swapping on DEVICE\n" \
-     "\nOptions:" \
-     "\n       -a      Stop swapping on all swap devices" \
-
-#define swapon_trivial_usage \
-       "[-a]" IF_FEATURE_SWAPON_PRI(" [-p PRI]") " [DEVICE]"
-#define swapon_full_usage "\n\n" \
-       "Start swapping on DEVICE\n" \
-     "\nOptions:" \
-     "\n       -a      Start swapping on all swap devices" \
-       IF_FEATURE_SWAPON_PRI( \
-     "\n       -p PRI  Set swap device priority" \
-       ) \
-
-#define switch_root_trivial_usage \
-       "[-c /dev/console] NEW_ROOT NEW_INIT [ARGS]"
-#define switch_root_full_usage "\n\n" \
-       "Free initramfs and switch to another root fs:\n" \
-       "chroot to NEW_ROOT, delete all in /, move NEW_ROOT to /,\n" \
-       "execute NEW_INIT. PID must be 1. NEW_ROOT must be a mountpoint.\n" \
-     "\nOptions:" \
-     "\n       -c DEV  Reopen stdio to DEV after switch" \
-
-#define sync_trivial_usage \
-       ""
-#define sync_full_usage "\n\n" \
-       "Write all buffered blocks to disk"
-
-#define fsync_trivial_usage \
-       "[OPTIONS] FILE..."
-#define fsync_full_usage "\n\n" \
-       "Write files' buffered blocks to disk\n" \
-     "\nOptions:" \
-     "\n       -d      Avoid syncing metadata"
-
-#define sysctl_trivial_usage \
-       "[OPTIONS] [VALUE]..."
-#define sysctl_full_usage "\n\n" \
-       "Configure kernel parameters at runtime\n" \
-     "\nOptions:" \
-     "\n       -n      Don't print key names" \
-     "\n       -e      Don't warn about unknown keys" \
-     "\n       -w      Change sysctl setting" \
-     "\n       -p FILE Load sysctl settings from FILE (default /etc/sysctl.conf)" \
-     "\n       -a      Display all values" \
-     "\n       -A      Display all values in table form" \
-
-#define sysctl_example_usage \
-       "sysctl [-n] [-e] variable...\n" \
-       "sysctl [-n] [-e] -w variable=value...\n" \
-       "sysctl [-n] [-e] -a\n" \
-       "sysctl [-n] [-e] -p file       (default /etc/sysctl.conf)\n" \
-       "sysctl [-n] [-e] -A\n"
-
-#define syslogd_trivial_usage \
-       "[OPTIONS]"
-#define syslogd_full_usage "\n\n" \
-       "System logging utility.\n" \
-       "This version of syslogd ignores /etc/syslog.conf\n" \
-     "\nOptions:" \
-     "\n       -n              Run in foreground" \
-     "\n       -O FILE         Log to given file (default:/var/log/messages)" \
-     "\n       -l N            Set local log level" \
-     "\n       -S              Smaller logging output" \
-       IF_FEATURE_ROTATE_LOGFILE( \
-     "\n       -s SIZE         Max size (KB) before rotate (default:200KB, 0=off)" \
-     "\n       -b N            N rotated logs to keep (default:1, max=99, 0=purge)") \
-       IF_FEATURE_REMOTE_LOG( \
-     "\n       -R HOST[:PORT]  Log to IP or hostname on PORT (default PORT=514/UDP)" \
-     "\n       -L              Log locally and via network (default is network only if -R)") \
-       IF_FEATURE_SYSLOGD_DUP( \
-     "\n       -D              Drop duplicates") \
-       IF_FEATURE_IPC_SYSLOG( \
-     "\n       -C[size(KiB)]   Log to shared mem buffer (read it using logread)") \
-       /* NB: -Csize shouldn't have space (because size is optional) */
-/*   "\n       -m MIN          Minutes between MARK lines (default:20, 0=off)" */
-
-#define syslogd_example_usage \
-       "$ syslogd -R masterlog:514\n" \
-       "$ syslogd -R 192.168.1.1:601\n"
-
-#define tac_trivial_usage \
-       "[FILE]..."
-#define tac_full_usage "\n\n" \
-       "Concatenate FILEs and print them in reverse"
-
-#define taskset_trivial_usage \
-       "[-p] [MASK] [PID | PROG ARGS]"
-#define taskset_full_usage "\n\n" \
-       "Set or get CPU affinity\n" \
-     "\nOptions:" \
-     "\n       -p      Operate on an existing PID" \
-
-#define taskset_example_usage \
-       "$ taskset 0x7 ./dgemm_test&\n" \
-       "$ taskset -p 0x1 $!\n" \
-       "pid 4790's current affinity mask: 7\n" \
-       "pid 4790's new affinity mask: 1\n" \
-       "$ taskset 0x7 /bin/sh -c './taskset -p 0x1 $$'\n" \
-       "pid 6671's current affinity mask: 1\n" \
-       "pid 6671's new affinity mask: 1\n" \
-       "$ taskset -p 1\n" \
-       "pid 1's current affinity mask: 3\n"
-
-#define tee_trivial_usage \
-       "[OPTIONS] [FILE]..."
-#define tee_full_usage "\n\n" \
-       "Copy stdin to each FILE, and also to stdout\n" \
-     "\nOptions:" \
-     "\n       -a      Append to the given FILEs, don't overwrite" \
-     "\n       -i      Ignore interrupt signals (SIGINT)" \
-
-#define tee_example_usage \
-       "$ echo \"Hello\" | tee /tmp/foo\n" \
-       "$ cat /tmp/foo\n" \
-       "Hello\n"
-
-#if ENABLE_FEATURE_TELNET_AUTOLOGIN
-#define telnet_trivial_usage \
-       "[-a] [-l USER] HOST [PORT]"
-#define telnet_full_usage "\n\n" \
-       "Connect to telnet server\n" \
-     "\nOptions:" \
-     "\n       -a      Automatic login with $USER variable" \
-     "\n       -l USER Automatic login as USER" \
-
-#else
-#define telnet_trivial_usage \
-       "HOST [PORT]"
-#define telnet_full_usage "\n\n" \
-       "Connect to telnet server"
-#endif
-
-#define telnetd_trivial_usage \
-       "[OPTIONS]"
-#define telnetd_full_usage "\n\n" \
-       "Handle incoming telnet connections" \
-       IF_NOT_FEATURE_TELNETD_STANDALONE(" via inetd") "\n" \
-     "\nOptions:" \
-     "\n       -l LOGIN        Exec LOGIN on connect" \
-     "\n       -f ISSUE_FILE   Display ISSUE_FILE instead of /etc/issue" \
-     "\n       -K              Close connection as soon as login exits" \
-     "\n                       (normally wait until all programs close slave pty)" \
-       IF_FEATURE_TELNETD_STANDALONE( \
-     "\n       -p PORT         Port to listen on" \
-     "\n       -b ADDR[:PORT]  Address to bind to" \
-     "\n       -F              Run in foreground" \
-     "\n       -i              Inetd mode" \
-       IF_FEATURE_TELNETD_INETD_WAIT( \
-     "\n       -w SEC          Inetd 'wait' mode, linger time SEC" \
-     "\n       -S              Log to syslog (implied by -i or without -F and -w)" \
-       ) \
-       )
-
-/* "test --help" does not print help (POSIX compat), only "[ --help" does.
- * We display "<applet> EXPRESSION ]" here (not "<applet> EXPRESSION")
- * Unfortunately, it screws up generated BusyBox.html. TODO. */
-#define test_trivial_usage \
-       "EXPRESSION ]"
-#define test_full_usage "\n\n" \
-       "Check file types, compare values etc. Return a 0/1 exit code\n" \
-       "depending on logical value of EXPRESSION"
-#define test_example_usage \
-       "$ test 1 -eq 2\n" \
-       "$ echo $?\n" \
-       "1\n" \
-       "$ test 1 -eq 1\n" \
-       "$ echo $?\n" \
-       "0\n" \
-       "$ [ -d /etc ]\n" \
-       "$ echo $?\n" \
-       "0\n" \
-       "$ [ -d /junk ]\n" \
-       "$ echo $?\n" \
-       "1\n"
-
-#define tc_trivial_usage \
-       /*"[OPTIONS] "*/"OBJECT CMD [dev STRING]"
-#define tc_full_usage "\n\n" \
-       "OBJECT: {qdisc|class|filter}\n" \
-       "CMD: {add|del|change|replace|show}\n" \
-       "\n" \
-       "qdisc [ handle QHANDLE ] [ root |"IF_FEATURE_TC_INGRESS(" ingress |")" parent CLASSID ]\n" \
-       /* "[ estimator INTERVAL TIME_CONSTANT ]\n" */ \
-       "       [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n" \
-       "       QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }\n" \
-       "qdisc show [ dev STRING ]"IF_FEATURE_TC_INGRESS(" [ingress]")"\n" \
-       "class [ classid CLASSID ] [ root | parent CLASSID ]\n" \
-       "       [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n" \
-       "class show [ dev STRING ] [ root | parent CLASSID ]\n" \
-       "filter [ pref PRIO ] [ protocol PROTO ]\n" \
-       /* "\t[ estimator INTERVAL TIME_CONSTANT ]\n" */ \
-       "       [ root | classid CLASSID ] [ handle FILTERID ]\n" \
-       "       [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n" \
-       "filter show [ dev STRING ] [ root | parent CLASSID ]"
-
-#define tcpsvd_trivial_usage \
-       "[-hEv] [-c N] [-C N[:MSG]] [-b N] [-u USER] [-l NAME] IP PORT PROG"
-/* with not-implemented options: */
-/*     "[-hpEvv] [-c N] [-C N[:MSG]] [-b N] [-u USER] [-l NAME] [-i DIR|-x CDB] [-t SEC] IP PORT PROG" */
-#define tcpsvd_full_usage "\n\n" \
-       "Create TCP socket, bind to IP:PORT and listen\n" \
-       "for incoming connection. Run PROG for each connection.\n" \
-     "\n       IP              IP to listen on. '0' = all" \
-     "\n       PORT            Port to listen on" \
-     "\n       PROG ARGS       Program to run" \
-     "\n       -l NAME         Local hostname (else looks up local hostname in DNS)" \
-     "\n       -u USER[:GRP]   Change to user/group after bind" \
-     "\n       -c N            Handle up to N connections simultaneously" \
-     "\n       -b N            Allow a backlog of approximately N TCP SYNs" \
-     "\n       -C N[:MSG]      Allow only up to N connections from the same IP" \
-     "\n                       New connections from this IP address are closed" \
-     "\n                       immediately. MSG is written to the peer before close" \
-     "\n       -h              Look up peer's hostname" \
-     "\n       -E              Don't set up environment variables" \
-     "\n       -v              Verbose" \
-
-#define udpsvd_trivial_usage \
-       "[-hEv] [-c N] [-u USER] [-l NAME] IP PORT PROG"
-#define udpsvd_full_usage "\n\n" \
-       "Create UDP socket, bind to IP:PORT and wait\n" \
-       "for incoming packets. Run PROG for each packet,\n" \
-       "redirecting all further packets with same peer ip:port to it.\n" \
-     "\n       IP              IP to listen on. '0' = all" \
-     "\n       PORT            Port to listen on" \
-     "\n       PROG ARGS       Program to run" \
-     "\n       -l NAME         Local hostname (else looks up local hostname in DNS)" \
-     "\n       -u USER[:GRP]   Change to user/group after bind" \
-     "\n       -c N            Handle up to N connections simultaneously" \
-     "\n       -h              Look up peer's hostname" \
-     "\n       -E              Don't set up environment variables" \
-     "\n       -v              Verbose" \
-
-#define tftp_trivial_usage \
-       "[OPTIONS] HOST [PORT]"
-#define tftp_full_usage "\n\n" \
-       "Transfer a file from/to tftp server\n" \
-     "\nOptions:" \
-     "\n       -l FILE Local FILE" \
-     "\n       -r FILE Remote FILE" \
-       IF_FEATURE_TFTP_GET( \
-     "\n       -g      Get file" \
-       ) \
-       IF_FEATURE_TFTP_PUT( \
-     "\n       -p      Put file" \
-       ) \
-       IF_FEATURE_TFTP_BLOCKSIZE( \
-     "\n       -b SIZE Transfer blocks of SIZE octets" \
-       )
-
-#define tftpd_trivial_usage \
-       "[-cr] [-u USER] [DIR]"
-#define tftpd_full_usage "\n\n" \
-       "Transfer a file on tftp client's request\n" \
-       "\n" \
-       "tftpd should be used as an inetd service.\n" \
-       "tftpd's line for inetd.conf:\n" \
-       "       69 dgram udp nowait root tftpd tftpd /files/to/serve\n" \
-       "It also can be ran from udpsvd:\n" \
-       "       udpsvd -vE 0.0.0.0 69 tftpd /files/to/serve\n" \
-     "\nOptions:" \
-     "\n       -r      Prohibit upload" \
-     "\n       -c      Allow file creation via upload" \
-     "\n       -u      Access files as USER" \
-
-#define time_trivial_usage \
-       "[OPTIONS] PROG ARGS"
-#define time_full_usage "\n\n" \
-       "Run PROG, display resource usage when it exits\n" \
-     "\nOptions:" \
-     "\n       -v      Verbose" \
-
-#define timeout_trivial_usage \
-       "[-t SECS] [-s SIG] PROG ARGS"
-#define timeout_full_usage "\n\n" \
-       "Runs PROG. Sends SIG to it if it is not gone in SECS seconds.\n" \
-       "Defaults: SECS: 10, SIG: TERM." \
-
-#define top_trivial_usage \
-       "[-b] [-nCOUNT] [-dSECONDS]" IF_FEATURE_TOPMEM(" [-m]")
-#define top_full_usage "\n\n" \
-       "Provide a view of process activity in real time.\n" \
-       "Read the status of all processes from /proc each SECONDS\n" \
-       "and display a screenful of them." \
-//TODO: add options and keyboard commands
-
-#define touch_trivial_usage \
-       "[-c] [-d DATE] FILE [FILE]..."
-#define touch_full_usage "\n\n" \
-       "Update the last-modified date on the given FILE[s]\n" \
-     "\nOptions:" \
-     "\n       -c      Don't create files" \
-     "\n       -d DT   Date/time to use" \
-
-#define touch_example_usage \
-       "$ ls -l /tmp/foo\n" \
-       "/bin/ls: /tmp/foo: No such file or directory\n" \
-       "$ touch /tmp/foo\n" \
-       "$ ls -l /tmp/foo\n" \
-       "-rw-rw-r--    1 andersen andersen        0 Apr 15 01:11 /tmp/foo\n"
-
-#define tr_trivial_usage \
-       "[-cds] STRING1 [STRING2]"
-#define tr_full_usage "\n\n" \
-       "Translate, squeeze, or delete characters from stdin, writing to stdout\n" \
-     "\nOptions:" \
-     "\n       -c      Take complement of STRING1" \
-     "\n       -d      Delete input characters coded STRING1" \
-     "\n       -s      Squeeze multiple output characters of STRING2 into one character" \
-
-#define tr_example_usage \
-       "$ echo \"gdkkn vnqkc\" | tr [a-y] [b-z]\n" \
-       "hello world\n"
-
-#define traceroute_trivial_usage \
-       "[-"IF_TRACEROUTE6("46")"FIldnrv] [-f 1ST_TTL] [-m MAXTTL] [-p PORT] [-q PROBES]\n" \
-       "       [-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-g GATEWAY] [-i IFACE]\n" \
-       "       [-z PAUSE_MSEC] HOST [BYTES]"
-#define traceroute_full_usage "\n\n" \
-       "Trace the route to HOST\n" \
-     "\nOptions:" \
-       IF_TRACEROUTE6( \
-     "\n       -4, -6  Force IP or IPv6 name resolution" \
-       ) \
-     "\n       -F      Set the don't fragment bit" \
-     "\n       -I      Use ICMP ECHO instead of UDP datagrams" \
-     "\n       -l      Display the TTL value of the returned packet" \
-     "\n       -d      Set SO_DEBUG options to socket" \
-     "\n       -n      Print numeric addresses" \
-     "\n       -r      Bypass routing tables, send directly to HOST" \
-     "\n       -v      Verbose" \
-     "\n       -m      Max time-to-live (max number of hops)" \
-     "\n       -p      Base UDP port number used in probes" \
-     "\n               (default 33434)" \
-     "\n       -q      Number of probes per TTL (default 3)" \
-     "\n       -s      IP address to use as the source address" \
-     "\n       -t      Type-of-service in probe packets (default 0)" \
-     "\n       -w      Time in seconds to wait for a response (default 3)" \
-     "\n       -g      Loose source route gateway (8 max)" \
-
-#define traceroute6_trivial_usage \
-       "[-dnrv] [-m MAXTTL] [-p PORT] [-q PROBES]\n" \
-       "       [-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-i IFACE]\n" \
-       "       HOST [BYTES]"
-#define traceroute6_full_usage "\n\n" \
-       "Trace the route to HOST\n" \
-     "\nOptions:" \
-     "\n       -d      Set SO_DEBUG options to socket" \
-     "\n       -n      Print numeric addresses" \
-     "\n       -r      Bypass routing tables, send directly to HOST" \
-     "\n       -v      Verbose" \
-     "\n       -m      Max time-to-live (max number of hops)" \
-     "\n       -p      Base UDP port number used in probes" \
-     "\n               (default is 33434)" \
-     "\n       -q      Number of probes per TTL (default 3)" \
-     "\n       -s      IP address to use as the source address" \
-     "\n       -t      Type-of-service in probe packets (default 0)" \
-     "\n       -w      Time in seconds to wait for a response (default 3)" \
-
-#define true_trivial_usage \
-       ""
-#define true_full_usage "\n\n" \
-       "Return an exit code of TRUE (0)"
-#define true_example_usage \
-       "$ true\n" \
-       "$ echo $?\n" \
-       "0\n"
-
-#define tty_trivial_usage \
-       ""
-#define tty_full_usage "\n\n" \
-       "Print file name of stdin's terminal" \
-       IF_INCLUDE_SUSv2( "\n" \
-     "\nOptions:" \
-     "\n       -s      Print nothing, only return exit status" \
-       )
-#define tty_example_usage \
-       "$ tty\n" \
-       "/dev/tty2\n"
-
-#define ttysize_trivial_usage \
-       "[w] [h]"
-#define ttysize_full_usage "\n\n" \
-       "Print dimension(s) of stdin's terminal, on error return 80x25"
-
-#define tunctl_trivial_usage \
-       "[-f device] ([-t name] | -d name)" IF_FEATURE_TUNCTL_UG(" [-u owner] [-g group] [-b]")
-#define tunctl_full_usage "\n\n" \
-       "Create or delete tun interfaces\n" \
-     "\nOptions:" \
-     "\n       -f name         tun device (/dev/net/tun)" \
-     "\n       -t name         Create iface 'name'" \
-     "\n       -d name         Delete iface 'name'" \
-       IF_FEATURE_TUNCTL_UG( \
-     "\n       -u owner        Set iface owner" \
-     "\n       -g group        Set iface group" \
-     "\n       -b              Brief output" \
-       )
-#define tunctl_example_usage \
-       "# tunctl\n" \
-       "# tunctl -d tun0\n"
-
-#define tune2fs_trivial_usage \
-/*     "[-c max-mounts-count] [-e errors-behavior] [-g group] " */ \
-/*     "[-i interval[d|m|w]] [-j] [-J journal-options] [-l] [-s sparse-flag] " */ \
-/*     "[-m reserved-blocks-percent] [-o [^]mount-options[,...]] " */ \
-/*     "[-r reserved-blocks-count] [-u user] [-C mount-count] " */ \
-       "[-L LABEL] " \
-/*     "[-M last-mounted-dir] [-O [^]feature[,...]] " */ \
-/*     "[-T last-check-time] [-U UUID] " */ \
-       "BLOCKDEV"
-#define tune2fs_full_usage "\n\n" \
-       "Adjust filesystem options on ext[23] filesystems"
-
-#define udhcpc_trivial_usage \
-       "[-fbnqvoCR] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]\n" \
-       "       [-H HOSTNAME] [-c CID] [-V VENDOR] [-O DHCP_OPT]..." IF_FEATURE_UDHCP_PORT(" [-P N]")
-#define udhcpc_full_usage "\n" \
-       IF_LONG_OPTS( \
-     "\n       -i,--interface IFACE    Interface to use (default eth0)" \
-     "\n       -p,--pidfile FILE       Create pidfile" \
-     "\n       -r,--request IP         IP address to request" \
-     "\n       -s,--script PROG        Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")" \
-     "\n       -t,--retries N          Send up to N discover packets" \
-     "\n       -T,--timeout N          Pause between packets (default 3 seconds)" \
-     "\n       -A,--tryagain N         Wait N seconds after failure (default 20)" \
-     "\n       -f,--foreground         Run in foreground" \
-       USE_FOR_MMU( \
-     "\n       -b,--background         Background if lease is not obtained" \
-       ) \
-     "\n       -S,--syslog             Log to syslog too" \
-     "\n       -n,--now                Exit if lease is not obtained" \
-     "\n       -q,--quit               Exit after obtaining lease" \
-     "\n       -R,--release            Release IP on exit" \
-       IF_FEATURE_UDHCP_PORT( \
-     "\n       -P,--client-port N      Use port N (default 68)" \
-       ) \
-       IF_FEATURE_UDHCPC_ARPING( \
-     "\n       -a,--arping             Use arping to validate offered address" \
-       ) \
-     "\n       -O,--request-option OPT Request DHCP option OPT (cumulative)" \
-     "\n       -o,--no-default-options Don't request any options (unless -O is given)" \
-     "\n       -x OPT:VAL              Include option OPT in sent packets (cumulative)" \
-     "\n       -F,--fqdn NAME          Ask server to update DNS mapping for NAME" \
-     "\n       -H,-h,--hostname NAME   Send NAME as client hostname (default none)" \
-     "\n       -V,--vendorclass VENDOR Vendor identifier (default 'udhcp VERSION')" \
-     "\n       -c,--clientid CLIENTID  Client identifier (default own MAC)" \
-     "\n       -C,--clientid-none      Don't send client identifier" \
-       ) \
-       IF_NOT_LONG_OPTS( \
-     "\n       -i IFACE        Interface to use (default eth0)" \
-     "\n       -p FILE         Create pidfile" \
-     "\n       -r IP           IP address to request" \
-     "\n       -s PROG         Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")" \
-     "\n       -t N            Send up to N discover packets" \
-     "\n       -T N            Pause between packets (default 3 seconds)" \
-     "\n       -A N            Wait N seconds (default 20) after failure" \
-     "\n       -x OPT:VAL      Include option OPT in sent packets" \
-     "\n       -O OPT          Request DHCP option OPT (cumulative)" \
-     "\n       -o              Don't request any options (unless -O is given)" \
-     "\n       -f              Run in foreground" \
-       USE_FOR_MMU( \
-     "\n       -b              Background if lease is not obtained" \
-       ) \
-     "\n       -S              Log to syslog too" \
-     "\n       -n              Exit if lease is not obtained" \
-     "\n       -q              Exit after obtaining lease" \
-     "\n       -R              Release IP on exit" \
-       IF_FEATURE_UDHCP_PORT( \
-     "\n       -P N            Use port N (default 68)" \
-       ) \
-       IF_FEATURE_UDHCPC_ARPING( \
-     "\n       -a              Use arping to validate offered address" \
-       ) \
-     "\n       -F NAME         Ask server to update DNS mapping for NAME" \
-     "\n       -H,-h NAME      Send NAME as client hostname (default none)" \
-     "\n       -V VENDOR       Vendor identifier (default 'udhcp VERSION')" \
-     "\n       -c CLIENTID     Client identifier (default own MAC)" \
-     "\n       -C              Don't send client identifier" \
-       )
-
-#define udhcpd_trivial_usage \
-       "[-fS]" IF_FEATURE_UDHCP_PORT(" [-P N]") " [configfile]" \
-
-#define udhcpd_full_usage "\n\n" \
-       "DHCP server\n" \
-     "\n       -f      Run in foreground" \
-     "\n       -S      Log to syslog too" \
-       IF_FEATURE_UDHCP_PORT( \
-     "\n       -P N    Use port N (default 67)" \
-       )
-
-#define umount_trivial_usage \
-       "[OPTIONS] FILESYSTEM|DIRECTORY"
-#define umount_full_usage "\n\n" \
-       "Unmount file systems\n" \
-     "\nOptions:" \
-       IF_FEATURE_UMOUNT_ALL( \
-     "\n       -a      Unmount all file systems" IF_FEATURE_MTAB_SUPPORT(" in /etc/mtab") \
-       ) \
-       IF_FEATURE_MTAB_SUPPORT( \
-     "\n       -n      Don't erase /etc/mtab entries" \
-       ) \
-     "\n       -r      Try to remount devices as read-only if mount is busy" \
-     "\n       -l      Lazy umount (detach filesystem)" \
-     "\n       -f      Force umount (i.e., unreachable NFS server)" \
-       IF_FEATURE_MOUNT_LOOP( \
-     "\n       -d      Free loop device if it has been used" \
-       )
-
-#define umount_example_usage \
-       "$ umount /dev/hdc1\n"
-
-#define uname_trivial_usage \
-       "[-amnrspv]"
-#define uname_full_usage "\n\n" \
-       "Print system information\n" \
-     "\nOptions:" \
-     "\n       -a      Print all" \
-     "\n       -m      The machine (hardware) type" \
-     "\n       -n      Hostname" \
-     "\n       -r      OS release" \
-     "\n       -s      OS name (default)" \
-     "\n       -p      Processor type" \
-     "\n       -v      OS version" \
-
-#define uname_example_usage \
-       "$ uname -a\n" \
-       "Linux debian 2.4.23 #2 Tue Dec 23 17:09:10 MST 2003 i686 GNU/Linux\n"
-
-#define uncompress_trivial_usage \
-       "[-cf] [FILE]..."
-#define uncompress_full_usage "\n\n" \
-       "Decompress .Z file[s]\n" \
-     "\nOptions:" \
-     "\n       -c      Write to stdout" \
-     "\n       -f      Overwrite" \
-
-#define unexpand_trivial_usage \
-       "[-fa][-t N] [FILE]..."
-#define unexpand_full_usage "\n\n" \
-       "Convert spaces to tabs, writing to stdout\n" \
-     "\nOptions:" \
-       IF_FEATURE_UNEXPAND_LONG_OPTIONS( \
-     "\n       -a,--all        Convert all blanks" \
-     "\n       -f,--first-only Convert only leading blanks" \
-     "\n       -t,--tabs=N     Tabstops every N chars" \
-       ) \
-       IF_NOT_FEATURE_UNEXPAND_LONG_OPTIONS( \
-     "\n       -a      Convert all blanks" \
-     "\n       -f      Convert only leading blanks" \
-     "\n       -t N    Tabstops every N chars" \
-       )
-
-#define uniq_trivial_usage \
-       "[-cdu][-f,s,w N] [INPUT [OUTPUT]]"
-#define uniq_full_usage "\n\n" \
-       "Discard duplicate lines\n" \
-     "\nOptions:" \
-     "\n       -c      Prefix lines by the number of occurrences" \
-     "\n       -d      Only print duplicate lines" \
-     "\n       -u      Only print unique lines" \
-     "\n       -f N    Skip first N fields" \
-     "\n       -s N    Skip first N chars (after any skipped fields)" \
-     "\n       -w N    Compare N characters in line" \
-
-#define uniq_example_usage \
-       "$ echo -e \"a\\na\\nb\\nc\\nc\\na\" | sort | uniq\n" \
-       "a\n" \
-       "b\n" \
-       "c\n"
-
-#define unzip_trivial_usage \
-       "[-opts[modifiers]] FILE[.zip] [LIST] [-x XLIST] [-d DIR]"
-#define unzip_full_usage "\n\n" \
-       "Extract files from ZIP archives\n" \
-     "\nOptions:" \
-     "\n       -l      List archive contents (with -q for short form)" \
-     "\n       -n      Never overwrite files (default)" \
-     "\n       -o      Overwrite" \
-     "\n       -p      Send output to stdout" \
-     "\n       -q      Quiet" \
-     "\n       -x XLST Exclude these files" \
-     "\n       -d DIR  Extract files into DIR" \
-
-#define uptime_trivial_usage \
-       ""
-#define uptime_full_usage "\n\n" \
-       "Display the time since the last boot"
-
-#define uptime_example_usage \
-       "$ uptime\n" \
-       "  1:55pm  up  2:30, load average: 0.09, 0.04, 0.00\n"
-
-#define usleep_trivial_usage \
-       "N"
-#define usleep_full_usage "\n\n" \
-       "Pause for N microseconds"
-
-#define usleep_example_usage \
-       "$ usleep 1000000\n" \
-       "[pauses for 1 second]\n"
-
-#define uudecode_trivial_usage \
-       "[-o OUTFILE] [INFILE]"
-#define uudecode_full_usage "\n\n" \
-       "Uudecode a file\n" \
-       "Finds outfile name in uuencoded source unless -o is given"
-
-#define uudecode_example_usage \
-       "$ uudecode -o busybox busybox.uu\n" \
-       "$ ls -l busybox\n" \
-       "-rwxr-xr-x   1 ams      ams        245264 Jun  7 21:35 busybox\n"
-
-#define uuencode_trivial_usage \
-       "[-m] [INFILE] STORED_FILENAME"
-#define uuencode_full_usage "\n\n" \
-       "Uuencode a file to stdout\n" \
-     "\nOptions:" \
-     "\n       -m      Use base64 encoding per RFC1521" \
-
-#define uuencode_example_usage \
-       "$ uuencode busybox busybox\n" \
-       "begin 755 busybox\n" \
-       "<encoded file snipped>\n" \
-       "$ uudecode busybox busybox > busybox.uu\n" \
-       "$\n"
-
-#define vconfig_trivial_usage \
-       "COMMAND [OPTIONS]"
-#define vconfig_full_usage "\n\n" \
-       "Create and remove virtual ethernet devices\n" \
-     "\nOptions:" \
-     "\n       add             [interface-name] [vlan_id]" \
-     "\n       rem             [vlan-name]" \
-     "\n       set_flag        [interface-name] [flag-num] [0 | 1]" \
-     "\n       set_egress_map  [vlan-name] [skb_priority] [vlan_qos]" \
-     "\n       set_ingress_map [vlan-name] [skb_priority] [vlan_qos]" \
-     "\n       set_name_type   [name-type]" \
-
-#define vi_trivial_usage \
-       "[OPTIONS] [FILE]..."
-#define vi_full_usage "\n\n" \
-       "Edit FILE\n" \
-     "\nOptions:" \
-       IF_FEATURE_VI_COLON( \
-     "\n       -c      Initial command to run ($EXINIT also available)") \
-       IF_FEATURE_VI_READONLY( \
-     "\n       -R      Read-only") \
-     "\n       -H      Short help regarding available features" \
-
-#define vlock_trivial_usage \
-       "[OPTIONS]"
-#define vlock_full_usage "\n\n" \
-       "Lock a virtual terminal. A password is required to unlock.\n" \
-     "\nOptions:" \
-     "\n       -a      Lock all VTs" \
-
-#define volname_trivial_usage \
-       "[DEVICE]"
-#define volname_full_usage "\n\n" \
-       "Show CD volume name of the DEVICE (default /dev/cdrom)"
-
-#define wall_trivial_usage \
-       "[FILE]"
-#define wall_full_usage "\n\n" \
-       "Write content of FILE or stdin to all logged-in users"
-#define wall_sample_usage \
-       "echo foo | wall\n" \
-       "wall ./mymessage"
-
-#define watch_trivial_usage \
-       "[-n SEC] [-t] PROG ARGS"
-#define watch_full_usage "\n\n" \
-       "Run PROG periodically\n" \
-     "\nOptions:" \
-     "\n       -n      Loop period in seconds (default 2)" \
-     "\n       -t      Don't print header" \
-
-#define watch_example_usage \
-       "$ watch date\n" \
-       "Mon Dec 17 10:31:40 GMT 2000\n" \
-       "Mon Dec 17 10:31:42 GMT 2000\n" \
-       "Mon Dec 17 10:31:44 GMT 2000"
-
-#define watchdog_trivial_usage \
-       "[-t N[ms]] [-T N[ms]] [-F] DEV"
-#define watchdog_full_usage "\n\n" \
-       "Periodically write to watchdog device DEV\n" \
-     "\nOptions:" \
-     "\n       -T N    Reboot after N seconds if not reset (default 60)" \
-     "\n       -t N    Reset every N seconds (default 30)" \
-     "\n       -F      Run in foreground" \
-     "\n" \
-     "\nUse 500ms to specify period in milliseconds" \
-
-#define wc_trivial_usage \
-       "[OPTIONS] [FILE]..."
-#define wc_full_usage "\n\n" \
-       "Print line, word, and byte counts for each FILE (or stdin),\n" \
-       "and a total line if more than one FILE is specified\n" \
-     "\nOptions:" \
-     "\n       -c      Print the byte counts" \
-     "\n       -l      Print the newline counts" \
-     "\n       -L      Print the length of the longest line" \
-     "\n       -w      Print the word counts" \
-
-#define wc_example_usage \
-       "$ wc /etc/passwd\n" \
-       "     31      46    1365 /etc/passwd\n"
-
-#define wget_trivial_usage \
-       IF_FEATURE_WGET_LONG_OPTIONS( \
-       "[-c|--continue] [-s|--spider] [-q|--quiet] [-O|--output-document FILE]\n" \
-       "       [--header 'header: value'] [-Y|--proxy on/off] [-P DIR]\n" \
-       "       [--no-check-certificate] [-U|--user-agent AGENT] URL" \
-       ) \
-       IF_NOT_FEATURE_WGET_LONG_OPTIONS( \
-       "[-csq] [-O FILE] [-Y on/off] [-P DIR] [-U AGENT] URL" \
-       )
-#define wget_full_usage "\n\n" \
-       "Retrieve files via HTTP or FTP\n" \
-     "\nOptions:" \
-     "\n       -s      Spider mode - only check file existence" \
-     "\n       -c      Continue retrieval of aborted transfer" \
-     "\n       -q      Quiet" \
-     "\n       -P      Set directory prefix to DIR" \
-     "\n       -O FILE Save to FILE ('-' for stdout)" \
-     "\n       -U STR  Use STR for User-Agent header" \
-     "\n       -Y      Use proxy ('on' or 'off')" \
-
-#define which_trivial_usage \
-       "[COMMAND]..."
-#define which_full_usage "\n\n" \
-       "Locate a COMMAND"
-#define which_example_usage \
-       "$ which login\n" \
-       "/bin/login\n"
-
-#define who_trivial_usage \
-       "[-a]"
-#define who_full_usage "\n\n" \
-       "Show who is logged on\n" \
-     "\nOptions:" \
-     "\n       -a      Show all" \
-
-#define whoami_trivial_usage \
-       ""
-#define whoami_full_usage "\n\n" \
-       "Print the user name associated with the current effective user id"
-
-#define zcat_trivial_usage \
-       "FILE"
-#define zcat_full_usage "\n\n" \
-       "Decompress to stdout"
-
-#define zcip_trivial_usage \
-       "[OPTIONS] IFACE SCRIPT"
-#define zcip_full_usage "\n\n" \
-       "Manage a ZeroConf IPv4 link-local address\n" \
-     "\nOptions:" \
-     "\n       -f              Run in foreground" \
-     "\n       -q              Quit after obtaining address" \
-     "\n       -r 169.254.x.x  Request this address first" \
-     "\n       -v              Verbose" \
-     "\n" \
-     "\nWith no -q, runs continuously monitoring for ARP conflicts," \
-     "\nexits only on I/O errors (link down etc)" \
-
-
 #endif
index 77e874d..a83da89 100644 (file)
@@ -20,7 +20,7 @@
 
 char *get_devname_from_label(const char *spec);
 char *get_devname_from_uuid(const char *spec);
-void display_uuid_cache(void);
+void display_uuid_cache(int scan_devices);
 
 /* Returns:
  * 0: no UUID= or LABEL= prefix found
@@ -28,3 +28,4 @@ void display_uuid_cache(void);
  *    *fsname is replaced if device with such UUID or LABEL is found
  */
 int resolve_mount_spec(char **fsname);
+int add_to_uuid_cache(const char *device);
index 978c502..45ebbfc 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
@@ -168,6 +168,15 @@ uint32_t bb_strtou32(const char *arg, char **endp, int base)
                return bb_strtoul(arg, endp, base);
        return BUG_bb_strtou32_unimplemented();
 }
+static ALWAYS_INLINE
+int32_t bb_strtoi32(const char *arg, char **endp, int base)
+{
+       if (sizeof(int32_t) == sizeof(int))
+               return bb_strtoi(arg, endp, base);
+       if (sizeof(int32_t) == sizeof(long))
+               return bb_strtol(arg, endp, base);
+       return BUG_bb_strtou32_unimplemented();
+}
 
 /* Floating point */
 
index 61658ed..5e5e6a2 100644 (file)
@@ -4,9 +4,9 @@
  * C library we're linking against may not support regex.h.
  *
  * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
- * Permission has been granted to redistribute this code under the GPL.
+ * Permission has been granted to redistribute this code under GPL.
  *
- * Licensed under GPLv2 or later, see file License in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #ifndef BB_REGEX_H
 #define BB_REGEX_H 1
index 590e298..5767c93 100644 (file)
@@ -7,119 +7,4 @@ menu "Init Utilities"
 
 INSERT
 
-config INIT
-       bool "init"
-       default y
-       select FEATURE_SYSLOG
-       help
-         init is the first program run when the system boots.
-
-config FEATURE_USE_INITTAB
-       bool "Support reading an inittab file"
-       default y
-       depends on INIT
-       help
-         Allow init to read an inittab file when the system boot.
-
-config FEATURE_KILL_REMOVED
-       bool "Support killing processes that have been removed from inittab"
-       default n
-       depends on FEATURE_USE_INITTAB
-       help
-         When respawn entries are removed from inittab and a SIGHUP is
-         sent to init, this option will make init kill the processes
-         that have been removed.
-
-config FEATURE_KILL_DELAY
-       int "How long to wait between TERM and KILL (0 - send TERM only)" if FEATURE_KILL_REMOVED
-       range 0 1024
-       default 0
-       depends on FEATURE_KILL_REMOVED
-       help
-         With nonzero setting, init sends TERM, forks, child waits N
-         seconds, sends KILL and exits. Setting it too high is unwise
-         (child will hang around for too long and could actually kill
-         the wrong process!)
-
-config FEATURE_INIT_SCTTY
-       bool "Run commands with leading dash with controlling tty"
-       default y
-       depends on INIT
-       help
-         If this option is enabled, init will try to give a controlling
-         tty to any command which has leading hyphen (often it's "-/bin/sh").
-         More precisely, init will do "ioctl(STDIN_FILENO, TIOCSCTTY, 0)".
-         If device attached to STDIN_FILENO can be a ctty but is not yet
-         a ctty for other session, it will become this process' ctty.
-         This is not the traditional init behavour, but is often what you want
-         in an embedded system where the console is only accessed during
-         development or for maintenance.
-         NB: using cttyhack applet may work better.
-
-config FEATURE_INIT_SYSLOG
-       bool "Enable init to write to syslog"
-       default y
-       depends on INIT
-
-config FEATURE_EXTRA_QUIET
-       bool "Be _extra_ quiet on boot"
-       default y
-       depends on INIT
-       help
-         Prevent init from logging some messages to the console during boot.
-
-config FEATURE_INIT_COREDUMPS
-       bool "Support dumping core for child processes (debugging only)"
-       default y
-       depends on INIT
-       help
-         If this option is enabled and the file /.init_enable_core
-         exists, then init will call setrlimit() to allow unlimited
-         core file sizes. If this option is disabled, processes
-         will not generate any core files.
-
-config FEATURE_INITRD
-       bool "Support running init from within an initrd (not initramfs)"
-       default y
-       depends on INIT
-       help
-         Legacy support for running init under the old-style initrd. Allows
-         the name linuxrc to act as init, and it doesn't assume init is PID 1.
-
-         This does not apply to initramfs, which runs /init as PID 1 and
-         requires no special support.
-
-config HALT
-       bool "poweroff, halt, and reboot"
-       default y
-       help
-         Stop all processes and either halt, reboot, or power off the system.
-
-config FEATURE_CALL_TELINIT
-       bool "Call telinit on shutdown and reboot"
-       default y
-       depends on HALT && !INIT
-       help
-         Call an external program (normally telinit) to facilitate
-         a switch to a proper runlevel.
-
-         This option is only available if you selected halt and friends,
-         but did not select init.
-
-config TELINIT_PATH
-       string "Path to telinit executable"
-       default "/sbin/telinit"
-       depends on FEATURE_CALL_TELINIT
-       help
-         When busybox halt and friends have to call external telinit
-         to facilitate proper shutdown, this path is to be used when
-         locating telinit executable.
-
-config MESG
-       bool "mesg"
-       default y
-       help
-         Mesg controls access to your terminal by others. It is typically
-         used to allow or disallow other users to write to your terminal
-
 endmenu
index 6095a78..6b4fb74 100644 (file)
@@ -2,12 +2,8 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
 INSERT
-lib-$(CONFIG_HALT)       += halt.o
-lib-$(CONFIG_INIT)       += init.o
-lib-$(CONFIG_MESG)       += mesg.o
-lib-$(CONFIG_BOOTCHARTD) += bootchartd.o
index dae2fe6..c7388c9 100644 (file)
@@ -1,8 +1,12 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//applet:IF_BOOTCHARTD(APPLET(bootchartd, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_BOOTCHARTD) += bootchartd.o
+
 //config:config BOOTCHARTD
 //config:      bool "bootchartd"
 //config:      default y
 #include "libbb.h"
 /* After libbb.h, since it needs sys/types.h on some systems */
 #include <sys/utsname.h>
-#include <sys/mount.h>
-#ifndef MS_SILENT
-# define MS_SILENT      (1 << 15)
+
+#ifdef __linux__
+# include <sys/mount.h>
+# ifndef MS_SILENT
+#  define MS_SILENT      (1 << 15)
+# endif
+# ifndef MNT_DETACH
+#  define MNT_DETACH 0x00000002
+# endif
 #endif
-#ifndef MNT_DETACH
-# define MNT_DETACH 0x00000002
+
+#if !ENABLE_TAR && !ENABLE_WERROR
+# warning Note: bootchartd requires tar command, but you did not select it.
+#elif !ENABLE_FEATURE_SEAMLESS_GZ && !ENABLE_WERROR
+# warning Note: bootchartd requires tar -z support, but you did not select it.
 #endif
 
 #define BC_VERSION_STR "0.8"
@@ -174,6 +187,7 @@ static char *make_tempdir(void)
        char template[] = "/tmp/bootchart.XXXXXX";
        char *tempdir = xstrdup(mkdtemp(template));
        if (!tempdir) {
+#ifdef __linux__
                /* /tmp is not writable (happens when we are used as init).
                 * Try to mount a tmpfs, them cd and lazily unmount it.
                 * Since we unmount it at once, we can mount it anywhere.
@@ -191,20 +205,17 @@ static char *make_tempdir(void)
                if (umount2(try_dir, MNT_DETACH) != 0) {
                        bb_perror_msg_and_die("can't %smount tmpfs", "un");
                }
+#else
+               bb_perror_msg_and_die("can't create temporary directory");
+#endif
        } else {
                xchdir(tempdir);
        }
        return tempdir;
 }
 
-static void do_logging(unsigned sample_period_us)
+static void do_logging(unsigned sample_period_us, int process_accounting)
 {
-       //# Enable process accounting if configured
-       //if [ "$PROCESS_ACCOUNTING" = "yes" ]; then
-       //      [ -e kernel_pacct ] || : > kernel_pacct
-       //      accton kernel_pacct
-       //fi
-
        FILE *proc_stat = xfopen("proc_stat.log", "w");
        FILE *proc_diskstats = xfopen("proc_diskstats.log", "w");
        //FILE *proc_netdev = xfopen("proc_netdev.log", "w");
@@ -212,6 +223,11 @@ static void do_logging(unsigned sample_period_us)
        int look_for_login_process = (getppid() == 1);
        unsigned count = 60*1000*1000 / sample_period_us; /* ~1 minute */
 
+       if (process_accounting) {
+               close(xopen("kernel_pacct", O_WRONLY | O_CREAT | O_TRUNC));
+               acct("kernel_pacct");
+       }
+
        while (--count && !bb_got_signal) {
                char *p;
                int len = open_read_close("/proc/uptime", G.jiffy_line, sizeof(G.jiffy_line)-2);
@@ -242,11 +258,9 @@ static void do_logging(unsigned sample_period_us)
  wait_more:
                usleep(sample_period_us);
        }
-
-       // [ -e kernel_pacct ] && accton off
 }
 
-static void finalize(char *tempdir, const char *prog)
+static void finalize(char *tempdir, const char *prog, int process_accounting)
 {
        //# Stop process accounting if configured
        //local pacct=
@@ -254,6 +268,9 @@ static void finalize(char *tempdir, const char *prog)
 
        FILE *header_fp = xfopen("header", "w");
 
+       if (process_accounting)
+               acct(NULL);
+
        if (prog)
                fprintf(header_fp, "profile.process = %s\n", prog);
 
@@ -296,7 +313,7 @@ static void finalize(char *tempdir, const char *prog)
        fclose(header_fp);
 
        /* Package log files */
-       system("tar -zcf /var/log/bootchart.tgz header *.log"); // + $pacct
+       system(xasprintf("tar -zcf /var/log/bootlog.tgz header %s *.log", process_accounting ? "kernel_pacct" : ""));
        /* Clean up (if we are not in detached tmpfs) */
        if (tempdir) {
                unlink("header");
@@ -304,6 +321,8 @@ static void finalize(char *tempdir, const char *prog)
                unlink("proc_diskstats.log");
                //unlink("proc_netdev.log");
                unlink("proc_ps.log");
+               if (process_accounting)
+                       unlink("kernel_pacct");
                rmdir(tempdir);
        }
 
@@ -316,7 +335,6 @@ static void finalize(char *tempdir, const char *prog)
 //usage:       "start [PROG ARGS]|stop|init"
 //usage:#define bootchartd_full_usage "\n\n"
 //usage:       "Create /var/log/bootchart.tgz with boot chart data\n"
-//usage:     "\nOptions:"
 //usage:     "\nstart: start background logging; with PROG, run PROG, then kill logging with USR1"
 //usage:     "\nstop: send USR1 to all bootchartd processes"
 //usage:     "\ninit: start background logging; stop when getty/xdm is seen (for init scripts)"
@@ -328,6 +346,7 @@ int bootchartd_main(int argc UNUSED_PARAM, char **argv)
        unsigned sample_period_us;
        pid_t parent_pid, logger_pid;
        smallint cmd;
+       int process_accounting;
        enum {
                CMD_STOP = 0,
                CMD_START,
@@ -361,6 +380,7 @@ int bootchartd_main(int argc UNUSED_PARAM, char **argv)
 
        /* Read config file: */
        sample_period_us = 200 * 1000;
+       process_accounting = 0;
        if (ENABLE_FEATURE_BOOTCHARTD_CONFIG_FILE) {
                char* token[2];
                parser_t *parser = config_open2("/etc/bootchartd.conf" + 5, fopen_for_read);
@@ -369,11 +389,16 @@ int bootchartd_main(int argc UNUSED_PARAM, char **argv)
                while (config_read(parser, token, 2, 0, "#=", PARSE_NORMAL & ~PARSE_COLLAPSE)) {
                        if (strcmp(token[0], "SAMPLE_PERIOD") == 0 && token[1])
                                sample_period_us = atof(token[1]) * 1000000;
+                       if (strcmp(token[0], "PROCESS_ACCOUNTING") == 0 && token[1]
+                        && (strcmp(token[1], "on") == 0 || strcmp(token[1], "yes") == 0)
+                       ) {
+                               process_accounting = 1;
+                       }
                }
                config_close(parser);
+               if ((int)sample_period_us <= 0)
+                       sample_period_us = 1; /* prevent division by 0 */
        }
-       if ((int)sample_period_us <= 0)
-               sample_period_us = 1; /* prevent division by 0 */
 
        /* Create logger child: */
        logger_pid = fork_or_rexec(argv);
@@ -401,13 +426,15 @@ int bootchartd_main(int argc UNUSED_PARAM, char **argv)
                        putenv((char*)bb_PATH_root_path);
 
                tempdir = make_tempdir();
-               do_logging(sample_period_us);
-               finalize(tempdir, cmd == CMD_START ? argv[2] : NULL);
+               do_logging(sample_period_us, process_accounting);
+               finalize(tempdir, cmd == CMD_START ? argv[2] : NULL, process_accounting);
                return EXIT_SUCCESS;
        }
 
        /* parent */
 
+       USE_FOR_NOMMU(argv[0][0] &= 0x7f); /* undo fork_or_rexec() damage */
+
        if (DO_SIGNAL_SYNC) {
                /* Wait for logger child to set handlers, then unpause it.
                 * Otherwise with short-lived PROG (e.g. "bootchartd start true")
@@ -430,8 +457,7 @@ int bootchartd_main(int argc UNUSED_PARAM, char **argv)
                pid_t pid = xvfork();
                if (pid == 0) { /* child */
                        argv += 2;
-                       execvp(argv[0], argv);
-                       bb_perror_msg_and_die("can't execute '%s'", argv[0]);
+                       BB_EXECVP_or_die(argv);
                }
                /* parent */
                waitpid(pid, NULL, 0);
index f1bb2c4..7974adb 100644 (file)
@@ -4,15 +4,73 @@
  *
  * Copyright 2006 by Rob Landley <rob@landley.net>
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//applet:IF_HALT(APPLET(halt, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_HALT(APPLET_ODDNAME(poweroff, halt, BB_DIR_SBIN, BB_SUID_DROP, poweroff))
+//applet:IF_HALT(APPLET_ODDNAME(reboot, halt, BB_DIR_SBIN, BB_SUID_DROP, reboot))
+
+//kbuild:lib-$(CONFIG_HALT) += halt.o
+
+//config:config HALT
+//config:      bool "poweroff, halt, and reboot"
+//config:      default y
+//config:      help
+//config:        Stop all processes and either halt, reboot, or power off the system.
+//config:
+//config:config FEATURE_CALL_TELINIT
+//config:      bool "Call telinit on shutdown and reboot"
+//config:      default y
+//config:      depends on HALT && !INIT
+//config:      help
+//config:        Call an external program (normally telinit) to facilitate
+//config:        a switch to a proper runlevel.
+//config:
+//config:        This option is only available if you selected halt and friends,
+//config:        but did not select init.
+//config:
+//config:config TELINIT_PATH
+//config:      string "Path to telinit executable"
+//config:      default "/sbin/telinit"
+//config:      depends on FEATURE_CALL_TELINIT
+//config:      help
+//config:        When busybox halt and friends have to call external telinit
+//config:        to facilitate proper shutdown, this path is to be used when
+//config:        locating telinit executable.
+
+//usage:#define halt_trivial_usage
+//usage:       "[-d DELAY] [-n] [-f]" IF_FEATURE_WTMP(" [-w]")
+//usage:#define halt_full_usage "\n\n"
+//usage:       "Halt the system\n"
+//usage:     "\n       -d SEC  Delay interval"
+//usage:     "\n       -n      Do not sync"
+//usage:     "\n       -f      Force (don't go through init)"
+//usage:       IF_FEATURE_WTMP(
+//usage:     "\n       -w      Only write a wtmp record"
+//usage:       )
+//usage:
+//usage:#define poweroff_trivial_usage
+//usage:       "[-d DELAY] [-n] [-f]"
+//usage:#define poweroff_full_usage "\n\n"
+//usage:       "Halt and shut off power\n"
+//usage:     "\n       -d SEC  Delay interval"
+//usage:     "\n       -n      Do not sync"
+//usage:     "\n       -f      Force (don't go through init)"
+//usage:
+//usage:#define reboot_trivial_usage
+//usage:       "[-d DELAY] [-n] [-f]"
+//usage:#define reboot_full_usage "\n\n"
+//usage:       "Reboot the system\n"
+//usage:     "\n       -d SEC  Delay interval"
+//usage:     "\n       -n      Do not sync"
+//usage:     "\n       -f      Force (don't go through init)"
+
 #include "libbb.h"
-#include <sys/reboot.h>
+#include "reboot.h"
 
 #if ENABLE_FEATURE_WTMP
 #include <sys/utsname.h>
-#include <utmp.h>
 
 static void write_wtmp(void)
 {
@@ -36,18 +94,6 @@ static void write_wtmp(void)
 #define write_wtmp() ((void)0)
 #endif
 
-#ifndef RB_HALT_SYSTEM
-#define RB_HALT_SYSTEM RB_HALT
-#endif
-
-#ifndef RB_POWERDOWN
-/* Stop system and switch power off if possible.  */
-# define RB_POWERDOWN   0x4321fedc
-#endif
-#ifndef RB_POWER_OFF
-# define RB_POWER_OFF RB_POWERDOWN
-#endif
-
 
 int halt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int halt_main(int argc UNUSED_PARAM, char **argv)
@@ -108,11 +154,13 @@ int halt_main(int argc UNUSED_PARAM, char **argv)
                                /* runlevels:
                                 * 0 == shutdown
                                 * 6 == reboot */
-                               rc = execlp(CONFIG_TELINIT_PATH,
+                               execlp(CONFIG_TELINIT_PATH,
                                                CONFIG_TELINIT_PATH,
                                                which == 2 ? "6" : "0",
                                                (char *)NULL
                                );
+                               bb_perror_msg_and_die("can't execute '%s'",
+                                               CONFIG_TELINIT_PATH);
                        }
                }
        } else {
index 2eb8f1a..d29328c 100644 (file)
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  * Adjusted by so many folks, it's impossible to keep track.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//config:config INIT
+//config:      bool "init"
+//config:      default y
+//config:      select FEATURE_SYSLOG
+//config:      help
+//config:        init is the first program run when the system boots.
+//config:
+//config:config FEATURE_USE_INITTAB
+//config:      bool "Support reading an inittab file"
+//config:      default y
+//config:      depends on INIT
+//config:      help
+//config:        Allow init to read an inittab file when the system boot.
+//config:
+//config:config FEATURE_KILL_REMOVED
+//config:      bool "Support killing processes that have been removed from inittab"
+//config:      default n
+//config:      depends on FEATURE_USE_INITTAB
+//config:      help
+//config:        When respawn entries are removed from inittab and a SIGHUP is
+//config:        sent to init, this option will make init kill the processes
+//config:        that have been removed.
+//config:
+//config:config FEATURE_KILL_DELAY
+//config:      int "How long to wait between TERM and KILL (0 - send TERM only)" if FEATURE_KILL_REMOVED
+//config:      range 0 1024
+//config:      default 0
+//config:      depends on FEATURE_KILL_REMOVED
+//config:      help
+//config:        With nonzero setting, init sends TERM, forks, child waits N
+//config:        seconds, sends KILL and exits. Setting it too high is unwise
+//config:        (child will hang around for too long and could actually kill
+//config:        the wrong process!)
+//config:
+//config:config FEATURE_INIT_SCTTY
+//config:      bool "Run commands with leading dash with controlling tty"
+//config:      default y
+//config:      depends on INIT
+//config:      help
+//config:        If this option is enabled, init will try to give a controlling
+//config:        tty to any command which has leading hyphen (often it's "-/bin/sh").
+//config:        More precisely, init will do "ioctl(STDIN_FILENO, TIOCSCTTY, 0)".
+//config:        If device attached to STDIN_FILENO can be a ctty but is not yet
+//config:        a ctty for other session, it will become this process' ctty.
+//config:        This is not the traditional init behavour, but is often what you want
+//config:        in an embedded system where the console is only accessed during
+//config:        development or for maintenance.
+//config:        NB: using cttyhack applet may work better.
+//config:
+//config:config FEATURE_INIT_SYSLOG
+//config:      bool "Enable init to write to syslog"
+//config:      default y
+//config:      depends on INIT
+//config:
+//config:config FEATURE_EXTRA_QUIET
+//config:      bool "Be _extra_ quiet on boot"
+//config:      default y
+//config:      depends on INIT
+//config:      help
+//config:        Prevent init from logging some messages to the console during boot.
+//config:
+//config:config FEATURE_INIT_COREDUMPS
+//config:      bool "Support dumping core for child processes (debugging only)"
+//config:      default y
+//config:      depends on INIT
+//config:      help
+//config:        If this option is enabled and the file /.init_enable_core
+//config:        exists, then init will call setrlimit() to allow unlimited
+//config:        core file sizes. If this option is disabled, processes
+//config:        will not generate any core files.
+//config:
+//config:config FEATURE_INITRD
+//config:      bool "Support running init from within an initrd (not initramfs)"
+//config:      default y
+//config:      depends on INIT
+//config:      help
+//config:        Legacy support for running init under the old-style initrd. Allows
+//config:        the name linuxrc to act as init, and it doesn't assume init is PID 1.
+//config:
+//config:        This does not apply to initramfs, which runs /init as PID 1 and
+//config:        requires no special support.
+//config:
+//config:config INIT_TERMINAL_TYPE
+//config:      string "Initial terminal type"
+//config:      default "linux"
+//config:      depends on INIT
+//config:      help
+//config:        This is the initial value set by init for the TERM environment
+//config:        variable. This variable is used by programs which make use of
+//config:        extended terminal capabilities.
+//config:
+//config:        Note that on Linux, init attempts to detect serial terminal and
+//config:        sets TERM to "vt102" if one is found.
+
+//applet:IF_INIT(APPLET(init, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_FEATURE_INITRD(APPLET_ODDNAME(linuxrc, init, BB_DIR_ROOT, BB_SUID_DROP, linuxrc))
+
+//kbuild:lib-$(CONFIG_INIT) += init.o
+
+#define DEBUG_SEGV_HANDLER 0
+
 #include "libbb.h"
 #include <syslog.h>
 #include <paths.h>
-#include <sys/reboot.h>
 #include <sys/resource.h>
-#include <linux/vt.h>
-#if ENABLE_FEATURE_UTMP
-# include <utmp.h> /* DEAD_PROCESS */
+#ifdef __linux__
+# include <linux/vt.h>
+# include <sys/sysinfo.h>
+#endif
+#include "reboot.h" /* reboot() constants */
+
+#if DEBUG_SEGV_HANDLER
+# undef _GNU_SOURCE
+# define _GNU_SOURCE 1
+# undef __USE_GNU
+# define __USE_GNU 1
+# include <execinfo.h>
+# include <sys/ucontext.h>
 #endif
 
+/* Used only for sanitizing purposes in set_sane_term() below. On systems where
+ * the baud rate is stored in a separate field, we can safely disable them. */
+#ifndef CBAUD
+# define CBAUD 0
+# define CBAUDEX 0
+#endif
 
 /* Was a CONFIG_xxx option. A lot of people were building
  * not fully functional init by switching it on! */
 #define DEBUG_INIT 0
 
-#define COMMAND_SIZE      256
 #define CONSOLE_NAME_SIZE 32
 
 /* Default sysinit script. */
 #ifndef INIT_SCRIPT
-#define INIT_SCRIPT  "/etc/init.d/rcS"
+# define INIT_SCRIPT  "/etc/init.d/rcS"
 #endif
 
 /* Each type of actions can appear many times. They will be
@@ -79,7 +194,7 @@ struct init_action {
        pid_t pid;
        uint8_t action_type;
        char terminal[CONSOLE_NAME_SIZE];
-       char command[COMMAND_SIZE];
+       char command[1];
 };
 
 static struct init_action *init_action_list = NULL;
@@ -89,13 +204,6 @@ static const char *log_console = VC_5;
 enum {
        L_LOG = 0x1,
        L_CONSOLE = 0x2,
-#ifndef RB_HALT_SYSTEM
-       RB_HALT_SYSTEM = 0xcdef0123, /* FIXME: this overflows enum */
-       RB_ENABLE_CAD = 0x89abcdef,
-       RB_DISABLE_CAD = 0,
-       RB_POWER_OFF = 0x4321fedc,
-       RB_AUTOBOOT = 0x01234567,
-#endif
 };
 
 /* Print a message to the specified device.
@@ -114,8 +222,8 @@ static void message(int where, const char *fmt, ...)
        msg[0] = '\r';
        va_start(arguments, fmt);
        l = 1 + vsnprintf(msg + 1, sizeof(msg) - 2, fmt, arguments);
-       if (l > sizeof(msg) - 1)
-               l = sizeof(msg) - 1;
+       if (l > sizeof(msg) - 2)
+               l = sizeof(msg) - 2;
        va_end(arguments);
 
 #if ENABLE_FEATURE_INIT_SYSLOG
@@ -166,7 +274,9 @@ static void message(int where, const char *fmt, ...)
 
 static void console_init(void)
 {
+#ifdef VT_OPENQRY
        int vtno;
+#endif
        char *s;
 
        s = getenv("CONSOLE");
@@ -190,6 +300,7 @@ static void console_init(void)
        }
 
        s = getenv("TERM");
+#ifdef VT_OPENQRY
        if (ioctl(STDIN_FILENO, VT_OPENQRY, &vtno) != 0) {
                /* Not a linux terminal, probably serial console.
                 * Force the TERM setting to vt102
@@ -198,8 +309,10 @@ static void console_init(void)
                        putenv((char*)"TERM=vt102");
                if (!ENABLE_FEATURE_INIT_SYSLOG)
                        log_console = NULL;
-       } else if (!s)
-               putenv((char*)"TERM=linux");
+       } else
+#endif
+       if (!s)
+               putenv((char*)"TERM=" CONFIG_INIT_TERMINAL_TYPE);
 }
 
 /* Set terminal settings to reasonable defaults.
@@ -220,11 +333,17 @@ static void set_sane_term(void)
        tty.c_cc[VSTOP] = 19;   /* C-s */
        tty.c_cc[VSUSP] = 26;   /* C-z */
 
+#ifdef __linux__
        /* use line discipline 0 */
        tty.c_line = 0;
+#endif
 
        /* Make it be sane */
-       tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
+#ifndef CRTSCTS
+# define CRTSCTS 0
+#endif
+       /* added CRTSCTS to fix Debian bug 528560 */
+       tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD | CRTSCTS;
        tty.c_cflag |= CREAD | HUPCL | CLOCAL;
 
        /* input modes */
@@ -234,8 +353,7 @@ static void set_sane_term(void)
        tty.c_oflag = OPOST | ONLCR;
 
        /* local modes */
-       tty.c_lflag =
-               ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
+       tty.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
 
        tcsetattr_stdin_TCSANOW(&tty);
 }
@@ -279,7 +397,7 @@ static void reset_sighandlers_and_unblock_sigs(void)
 }
 
 /* Wrapper around exec:
- * Takes string (max COMMAND_SIZE chars).
+ * Takes string.
  * If chars like '>' detected, execs '[-]/bin/sh -c "exec ......."'.
  * Otherwise splits words on whitespace, deals with leading dash,
  * and uses plain exec().
@@ -287,24 +405,32 @@ static void reset_sighandlers_and_unblock_sigs(void)
  */
 static void init_exec(const char *command)
 {
-       char *cmd[COMMAND_SIZE / 2];
-       char buf[COMMAND_SIZE + 6];  /* COMMAND_SIZE+strlen("exec ")+1 */
-       int dash = (command[0] == '-' /* maybe? && command[1] == '/' */);
+       /* +8 allows to write VLA sizes below more efficiently: */
+       unsigned command_size = strlen(command) + 8;
+       /* strlen(command) + strlen("exec ")+1: */
+       char buf[command_size];
+       /* strlen(command) / 2 + 4: */
+       char *cmd[command_size / 2];
+       int dash;
+
+       dash = (command[0] == '-' /* maybe? && command[1] == '/' */);
+       command += dash;
 
        /* See if any special /bin/sh requiring characters are present */
        if (strpbrk(command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) {
-               strcpy(buf, "exec ");
-               strcpy(buf + 5, command + dash); /* excluding "-" */
+               sprintf(buf, "exec %s", command); /* excluding "-" */
                /* NB: LIBBB_DEFAULT_LOGIN_SHELL define has leading dash */
                cmd[0] = (char*)(LIBBB_DEFAULT_LOGIN_SHELL + !dash);
                cmd[1] = (char*)"-c";
                cmd[2] = buf;
                cmd[3] = NULL;
+               command = LIBBB_DEFAULT_LOGIN_SHELL + 1;
        } else {
                /* Convert command (char*) into cmd (char**, one word per string) */
                char *word, *next;
                int i = 0;
-               next = strcpy(buf, command); /* including "-" */
+               next = strcpy(buf, command - dash); /* command including "-" */
+               command = next + dash;
                while ((word = strsep(&next, " \t")) != NULL) {
                        if (*word != '\0') { /* not two spaces/tabs together? */
                                cmd[i] = word;
@@ -315,14 +441,14 @@ static void init_exec(const char *command)
        }
        /* If we saw leading "-", it is interactive shell.
         * Try harder to give it a controlling tty.
-        * And skip "-" in actual exec call. */
-       if (dash) {
+        */
+       if (ENABLE_FEATURE_INIT_SCTTY && dash) {
                /* _Attempt_ to make stdin a controlling tty. */
-               if (ENABLE_FEATURE_INIT_SCTTY)
-                       ioctl(STDIN_FILENO, TIOCSCTTY, 0 /*only try, don't steal*/);
+               ioctl(STDIN_FILENO, TIOCSCTTY, 0 /*only try, don't steal*/);
        }
-       BB_EXECVP(cmd[0] + dash, cmd);
-       message(L_LOG | L_CONSOLE, "can't run '%s': %s", cmd[0], strerror(errno));
+       /* Here command never contains the dash, cmd[0] might */
+       BB_EXECVP(command, cmd);
+       message(L_LOG | L_CONSOLE, "can't run '%s': %s", command, strerror(errno));
        /* returns if execvp fails */
 }
 
@@ -398,7 +524,7 @@ static pid_t run(const struct init_action *a)
 
        /* Log the process name and args */
        message(L_LOG, "starting pid %d, tty '%s': '%s'",
-                         getpid(), a->terminal, a->command);
+                       getpid(), a->terminal, a->command);
 
        /* Now run it.  The new program will take over this PID,
         * so nothing further in init.c should be run. */
@@ -412,13 +538,17 @@ static struct init_action *mark_terminated(pid_t pid)
        struct init_action *a;
 
        if (pid > 0) {
+               update_utmp(pid, DEAD_PROCESS,
+                               /*tty_name:*/ NULL,
+                               /*username:*/ NULL,
+                               /*hostname:*/ NULL
+               );
                for (a = init_action_list; a; a = a->next) {
                        if (a->pid == pid) {
                                a->pid = 0;
                                return a;
                        }
                }
-               update_utmp(pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL);
        }
        return NULL;
 }
@@ -483,7 +613,7 @@ static void new_init_action(uint8_t action_type, const char *command, const char
         */
        nextp = &init_action_list;
        while ((a = *nextp) != NULL) {
-               /* Don't enter action if it's already in the list,
+               /* Don't enter action if it's already in the list.
                 * This prevents losing running RESPAWNs.
                 */
                if (strcmp(a->command, command) == 0
@@ -495,25 +625,26 @@ static void new_init_action(uint8_t action_type, const char *command, const char
                        while (*nextp != NULL)
                                nextp = &(*nextp)->next;
                        a->next = NULL;
-                       break;
+                       goto append;
                }
                nextp = &a->next;
        }
 
-       if (!a)
-               a = xzalloc(sizeof(*a));
+       a = xzalloc(sizeof(*a) + strlen(command));
+
        /* Append to the end of the list */
+ append:
        *nextp = a;
        a->action_type = action_type;
-       safe_strncpy(a->command, command, sizeof(a->command));
+       strcpy(a->command, command);
        safe_strncpy(a->terminal, cons, sizeof(a->terminal));
-       dbg_message(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n",
+       dbg_message(L_LOG | L_CONSOLE, "command='%s' action=%x tty='%s'\n",
                a->command, a->action_type, a->terminal);
 }
 
 /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
  * then parse_inittab() simply adds in some default
- * actions(i.e., runs INIT_SCRIPT and then starts a pair
+ * actions (i.e., runs INIT_SCRIPT and then starts a pair
  * of "askfirst" shells).  If CONFIG_FEATURE_USE_INITTAB
  * _is_ defined, but /etc/inittab is missing, this
  * results in the same set of default behaviors.
@@ -528,23 +659,22 @@ static void parse_inittab(void)
 #endif
        {
                /* No inittab file - set up some default behavior */
-               /* Reboot on Ctrl-Alt-Del */
-               new_init_action(CTRLALTDEL, "reboot", "");
-               /* Umount all filesystems on halt/reboot */
-               new_init_action(SHUTDOWN, "umount -a -r", "");
-               /* Swapoff on halt/reboot */
-               if (ENABLE_SWAPONOFF)
-                       new_init_action(SHUTDOWN, "swapoff -a", "");
-               /* Prepare to restart init when a QUIT is received */
-               new_init_action(RESTART, "init", "");
+               /* Sysinit */
+               new_init_action(SYSINIT, INIT_SCRIPT, "");
                /* Askfirst shell on tty1-4 */
                new_init_action(ASKFIRST, bb_default_login_shell, "");
 //TODO: VC_1 instead of ""? "" is console -> ctty problems -> angry users
                new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
                new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
                new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
-               /* sysinit */
-               new_init_action(SYSINIT, INIT_SCRIPT, "");
+               /* Reboot on Ctrl-Alt-Del */
+               new_init_action(CTRLALTDEL, "reboot", "");
+               /* Umount all filesystems on halt/reboot */
+               new_init_action(SHUTDOWN, "umount -a -r", "");
+               /* Swapoff on halt/reboot */
+               new_init_action(SHUTDOWN, "swapoff -a", "");
+               /* Restart init when a QUIT is received */
+               new_init_action(RESTART, "init", "");
                return;
        }
 
@@ -659,7 +789,7 @@ static void run_shutdown_and_kill_processes(void)
  * and only one will be remembered and acted upon.
  */
 
-/* The SIGUSR[12]/SIGTERM handler */
+/* The SIGPWR/SIGUSR[12]/SIGTERM handler */
 static void halt_reboot_pwoff(int sig) NORETURN;
 static void halt_reboot_pwoff(int sig)
 {
@@ -709,10 +839,12 @@ static void restart_handler(int sig UNUSED_PARAM)
 
                run_shutdown_and_kill_processes();
 
+#ifdef RB_ENABLE_CAD
                /* Allow Ctrl-Alt-Del to reboot the system.
                 * This is how kernel sets it up for init, we follow suit.
                 */
                reboot(RB_ENABLE_CAD); /* misnomer */
+#endif
 
                if (open_stdio_to_tty(a->terminal)) {
                        dbg_message(L_CONSOLE, "Trying to re-exec %s", a->command);
@@ -802,10 +934,17 @@ static void reload_inittab(void)
 
        /* Remove stale entries and SYSINIT entries.
         * We never rerun SYSINIT entries anyway,
-        * removing them too saves a few bytes */
+        * removing them too saves a few bytes
+        */
        nextp = &init_action_list;
        while ((a = *nextp) != NULL) {
-               if ((a->action_type & ~SYSINIT) == 0) {
+               /*
+                * Why pid == 0 check?
+                * Process can be removed from inittab and added *later*.
+                * If we delete its entry but process still runs,
+                * duplicate is spawned when the entry is re-added.
+                */
+               if ((a->action_type & ~SYSINIT) == 0 && a->pid == 0) {
                        *nextp = a->next;
                        free(a);
                } else {
@@ -839,27 +978,76 @@ static int check_delayed_sigs(void)
        }
 }
 
+#if DEBUG_SEGV_HANDLER
+static
+void handle_sigsegv(int sig, siginfo_t *info, void *ucontext)
+{
+       long ip;
+       ucontext_t *uc;
+
+       uc = ucontext;
+       ip = uc->uc_mcontext.gregs[REG_EIP];
+       fdprintf(2, "signal:%d address:0x%lx ip:0x%lx\n",
+                       sig,
+                       /* this is void*, but using %p would print "(null)"
+                        * even for ptrs which are not exactly 0, but, say, 0x123:
+                        */
+                       (long)info->si_addr,
+                       ip);
+       {
+               /* glibc extension */
+               void *array[50];
+               int size;
+               size = backtrace(array, 50);
+               backtrace_symbols_fd(array, size, 2);
+       }
+       for (;;) sleep(9999);
+}
+#endif
+
 int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int init_main(int argc UNUSED_PARAM, char **argv)
 {
-       die_sleep = 30 * 24*60*60; /* if xmalloc would ever die... */
-
        if (argv[1] && strcmp(argv[1], "-q") == 0) {
                return kill(1, SIGHUP);
        }
 
+#if DEBUG_SEGV_HANDLER
+       {
+               struct sigaction sa;
+               memset(&sa, 0, sizeof(sa));
+               sa.sa_sigaction = handle_sigsegv;
+               sa.sa_flags = SA_SIGINFO;
+               sigaction(SIGSEGV, &sa, NULL);
+               sigaction(SIGILL, &sa, NULL);
+               sigaction(SIGFPE, &sa, NULL);
+               sigaction(SIGBUS, &sa, NULL);
+       }
+#endif
+
        if (!DEBUG_INIT) {
                /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */
                if (getpid() != 1
-                && (!ENABLE_FEATURE_INITRD || !strstr(applet_name, "linuxrc"))
+                && (!ENABLE_FEATURE_INITRD || applet_name[0] != 'l') /* not linuxrc? */
                ) {
-                       bb_show_usage();
+                       bb_error_msg_and_die("must be run as PID 1");
                }
+#ifdef RB_DISABLE_CAD
                /* Turn off rebooting via CTL-ALT-DEL - we get a
                 * SIGINT on CAD so we can shut things down gracefully... */
                reboot(RB_DISABLE_CAD); /* misnomer */
+#endif
        }
 
+       /* If, say, xmalloc would ever die, we don't want to oops kernel
+        * by exiting.
+        * NB: we set die_sleep *after* PID 1 check and bb_show_usage.
+        * Otherwise, for example, "init u" ("please rexec yourself"
+        * command for sysvinit) will show help text (which isn't too bad),
+        * *and sleep forever* (which is bad!)
+        */
+       die_sleep = 30 * 24*60*60;
+
        /* Figure out where the default console should be */
        console_init();
        set_sane_term();
@@ -880,8 +1068,13 @@ int init_main(int argc UNUSED_PARAM, char **argv)
        message(L_CONSOLE | L_LOG, "init started: %s", bb_banner);
 #endif
 
+#if 0
+/* It's 2013, does anyone really still depend on this? */
+/* If you do, consider adding swapon to sysinot actions then! */
+/* struct sysinfo is linux-specific */
+# ifdef __linux__
        /* Make sure there is enough memory to do something useful. */
-       if (ENABLE_SWAPONOFF) {
+       /*if (ENABLE_SWAPONOFF) - WRONG: we may have non-bbox swapon*/ {
                struct sysinfo info;
 
                if (sysinfo(&info) == 0
@@ -895,6 +1088,8 @@ int init_main(int argc UNUSED_PARAM, char **argv)
                        run_actions(SYSINIT);   /* wait and removing */
                }
        }
+# endif
+#endif
 
        /* Check if we are supposed to be in single user mode */
        if (argv[1]
@@ -908,8 +1103,8 @@ int init_main(int argc UNUSED_PARAM, char **argv)
 
                /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
                 * then parse_inittab() simply adds in some default
-                * actions(i.e., INIT_SCRIPT and a pair
-                * of "askfirst" shells */
+                * actions (i.e., INIT_SCRIPT and a pair
+                * of "askfirst" shells) */
                parse_inittab();
        }
 
@@ -924,7 +1119,7 @@ int init_main(int argc UNUSED_PARAM, char **argv)
                        /* SELinux in enforcing mode but load_policy failed */
                        message(L_CONSOLE, "can't load SELinux Policy. "
                                "Machine is in enforcing mode. Halting now.");
-                       exit(EXIT_FAILURE);
+                       return EXIT_FAILURE;
                }
        }
 #endif
@@ -933,13 +1128,14 @@ int init_main(int argc UNUSED_PARAM, char **argv)
        strncpy(argv[0], "init", strlen(argv[0]));
        /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
        while (*++argv)
-               memset(*argv, 0, strlen(*argv));
+               nuke_str(*argv);
 
        /* Set up signal handlers */
        if (!DEBUG_INIT) {
                struct sigaction sa;
 
                bb_signals(0
+                       + (1 << SIGPWR)  /* halt */
                        + (1 << SIGUSR1) /* halt */
                        + (1 << SIGTERM) /* reboot */
                        + (1 << SIGUSR2) /* poweroff */
@@ -1028,3 +1224,138 @@ int init_main(int argc UNUSED_PARAM, char **argv)
                }
        } /* while (1) */
 }
+
+//usage:#define linuxrc_trivial_usage NOUSAGE_STR
+//usage:#define linuxrc_full_usage ""
+
+//usage:#define init_trivial_usage
+//usage:       ""
+//usage:#define init_full_usage "\n\n"
+//usage:       "Init is the first process started during boot. It never exits."
+//usage:       IF_FEATURE_USE_INITTAB(
+//usage:   "\n""It (re)spawns children according to /etc/inittab."
+//usage:       )
+//usage:       IF_NOT_FEATURE_USE_INITTAB(
+//usage:   "\n""This version of init doesn't use /etc/inittab,"
+//usage:   "\n""has fixed set of processed to run."
+//usage:       )
+//usage:
+//usage:#define init_notes_usage
+//usage:       "This version of init is designed to be run only by the kernel.\n"
+//usage:       "\n"
+//usage:       "BusyBox init doesn't support multiple runlevels. The runlevels field of\n"
+//usage:       "the /etc/inittab file is completely ignored by BusyBox init. If you want\n"
+//usage:       "runlevels, use sysvinit.\n"
+//usage:       "\n"
+//usage:       "BusyBox init works just fine without an inittab. If no inittab is found,\n"
+//usage:       "it has the following default behavior:\n"
+//usage:       "\n"
+//usage:       "       ::sysinit:/etc/init.d/rcS\n"
+//usage:       "       ::askfirst:/bin/sh\n"
+//usage:       "       ::ctrlaltdel:/sbin/reboot\n"
+//usage:       "       ::shutdown:/sbin/swapoff -a\n"
+//usage:       "       ::shutdown:/bin/umount -a -r\n"
+//usage:       "       ::restart:/sbin/init\n"
+//usage:       "       tty2::askfirst:/bin/sh\n"
+//usage:       "       tty3::askfirst:/bin/sh\n"
+//usage:       "       tty4::askfirst:/bin/sh\n"
+//usage:       "\n"
+//usage:       "If you choose to use an /etc/inittab file, the inittab entry format is as follows:\n"
+//usage:       "\n"
+//usage:       "       <id>:<runlevels>:<action>:<process>\n"
+//usage:       "\n"
+//usage:       "       <id>:\n"
+//usage:       "\n"
+//usage:       "               WARNING: This field has a non-traditional meaning for BusyBox init!\n"
+//usage:       "               The id field is used by BusyBox init to specify the controlling tty for\n"
+//usage:       "               the specified process to run on. The contents of this field are\n"
+//usage:       "               appended to \"/dev/\" and used as-is. There is no need for this field to\n"
+//usage:       "               be unique, although if it isn't you may have strange results. If this\n"
+//usage:       "               field is left blank, then the init's stdin/out will be used.\n"
+//usage:       "\n"
+//usage:       "       <runlevels>:\n"
+//usage:       "\n"
+//usage:       "               The runlevels field is completely ignored.\n"
+//usage:       "\n"
+//usage:       "       <action>:\n"
+//usage:       "\n"
+//usage:       "               Valid actions include: sysinit, respawn, askfirst, wait,\n"
+//usage:       "               once, restart, ctrlaltdel, and shutdown.\n"
+//usage:       "\n"
+//usage:       "               The available actions can be classified into two groups: actions\n"
+//usage:       "               that are run only once, and actions that are re-run when the specified\n"
+//usage:       "               process exits.\n"
+//usage:       "\n"
+//usage:       "               Run only-once actions:\n"
+//usage:       "\n"
+//usage:       "                       'sysinit' is the first item run on boot. init waits until all\n"
+//usage:       "                       sysinit actions are completed before continuing. Following the\n"
+//usage:       "                       completion of all sysinit actions, all 'wait' actions are run.\n"
+//usage:       "                       'wait' actions, like 'sysinit' actions, cause init to wait until\n"
+//usage:       "                       the specified task completes. 'once' actions are asynchronous,\n"
+//usage:       "                       therefore, init does not wait for them to complete. 'restart' is\n"
+//usage:       "                       the action taken to restart the init process. By default this should\n"
+//usage:       "                       simply run /sbin/init, but can be a script which runs pivot_root or it\n"
+//usage:       "                       can do all sorts of other interesting things. The 'ctrlaltdel' init\n"
+//usage:       "                       actions are run when the system detects that someone on the system\n"
+//usage:       "                       console has pressed the CTRL-ALT-DEL key combination. Typically one\n"
+//usage:       "                       wants to run 'reboot' at this point to cause the system to reboot.\n"
+//usage:       "                       Finally the 'shutdown' action specifies the actions to taken when\n"
+//usage:       "                       init is told to reboot. Unmounting filesystems and disabling swap\n"
+//usage:       "                       is a very good here.\n"
+//usage:       "\n"
+//usage:       "               Run repeatedly actions:\n"
+//usage:       "\n"
+//usage:       "                       'respawn' actions are run after the 'once' actions. When a process\n"
+//usage:       "                       started with a 'respawn' action exits, init automatically restarts\n"
+//usage:       "                       it. Unlike sysvinit, BusyBox init does not stop processes from\n"
+//usage:       "                       respawning out of control. The 'askfirst' actions acts just like\n"
+//usage:       "                       respawn, except that before running the specified process it\n"
+//usage:       "                       displays the line \"Please press Enter to activate this console.\"\n"
+//usage:       "                       and then waits for the user to press enter before starting the\n"
+//usage:       "                       specified process.\n"
+//usage:       "\n"
+//usage:       "               Unrecognized actions (like initdefault) will cause init to emit an\n"
+//usage:       "               error message, and then go along with its business. All actions are\n"
+//usage:       "               run in the order they appear in /etc/inittab.\n"
+//usage:       "\n"
+//usage:       "       <process>:\n"
+//usage:       "\n"
+//usage:       "               Specifies the process to be executed and its command line.\n"
+//usage:       "\n"
+//usage:       "Example /etc/inittab file:\n"
+//usage:       "\n"
+//usage:       "       # This is run first except when booting in single-user mode\n"
+//usage:       "       #\n"
+//usage:       "       ::sysinit:/etc/init.d/rcS\n"
+//usage:       "       \n"
+//usage:       "       # /bin/sh invocations on selected ttys\n"
+//usage:       "       #\n"
+//usage:       "       # Start an \"askfirst\" shell on the console (whatever that may be)\n"
+//usage:       "       ::askfirst:-/bin/sh\n"
+//usage:       "       # Start an \"askfirst\" shell on /dev/tty2-4\n"
+//usage:       "       tty2::askfirst:-/bin/sh\n"
+//usage:       "       tty3::askfirst:-/bin/sh\n"
+//usage:       "       tty4::askfirst:-/bin/sh\n"
+//usage:       "       \n"
+//usage:       "       # /sbin/getty invocations for selected ttys\n"
+//usage:       "       #\n"
+//usage:       "       tty4::respawn:/sbin/getty 38400 tty4\n"
+//usage:       "       tty5::respawn:/sbin/getty 38400 tty5\n"
+//usage:       "       \n"
+//usage:       "       \n"
+//usage:       "       # Example of how to put a getty on a serial line (for a terminal)\n"
+//usage:       "       #\n"
+//usage:       "       #::respawn:/sbin/getty -L ttyS0 9600 vt100\n"
+//usage:       "       #::respawn:/sbin/getty -L ttyS1 9600 vt100\n"
+//usage:       "       #\n"
+//usage:       "       # Example how to put a getty on a modem line\n"
+//usage:       "       #::respawn:/sbin/getty 57600 ttyS2\n"
+//usage:       "       \n"
+//usage:       "       # Stuff to do when restarting the init process\n"
+//usage:       "       ::restart:/sbin/init\n"
+//usage:       "       \n"
+//usage:       "       # Stuff to do before rebooting\n"
+//usage:       "       ::ctrlaltdel:/sbin/reboot\n"
+//usage:       "       ::shutdown:/bin/umount -a -r\n"
+//usage:       "       ::shutdown:/sbin/swapoff -a\n"
index 2e8b16e..45c13b8 100644 (file)
@@ -4,45 +4,73 @@
  *
  * Copyright (c) 2002 Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//config:config MESG
+//config:      bool "mesg"
+//config:      default y
+//config:      help
+//config:        Mesg controls access to your terminal by others. It is typically
+//config:        used to allow or disallow other users to write to your terminal
+//config:
+//config:config FEATURE_MESG_ENABLE_ONLY_GROUP
+//config:      bool "Enable writing to tty only by group, not by everybody"
+//config:      default y
+//config:      depends on MESG
+//config:      help
+//config:        Usually, ttys are owned by group "tty", and "write" tool is
+//config:        setgid to this group. This way, "mesg y" only needs to enable
+//config:        "write by owning group" bit in tty mode.
+//config:
+//config:        If you set this option to N, "mesg y" will enable writing
+//config:        by anybody at all. This is not recommended.
+
+//applet:IF_MESG(APPLET(mesg, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MESG) += mesg.o
+
+//usage:#define mesg_trivial_usage
+//usage:       "[y|n]"
+//usage:#define mesg_full_usage "\n\n"
+//usage:       "Control write access to your terminal\n"
+//usage:       "       y       Allow write access to your terminal\n"
+//usage:       "       n       Disallow write access to your terminal"
+
 #include "libbb.h"
 
-#ifdef USE_TTY_GROUP
-#define S_IWGRP_OR_S_IWOTH     S_IWGRP
+#if ENABLE_FEATURE_MESG_ENABLE_ONLY_GROUP
+#define S_IWGRP_OR_S_IWOTH  S_IWGRP
 #else
-#define S_IWGRP_OR_S_IWOTH     (S_IWGRP | S_IWOTH)
+#define S_IWGRP_OR_S_IWOTH  (S_IWGRP | S_IWOTH)
 #endif
 
 int mesg_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int mesg_main(int argc UNUSED_PARAM, char **argv)
 {
        struct stat sb;
-       const char *tty;
+       mode_t m;
        char c = 0;
 
        argv++;
 
-       if (!argv[0]
-        || (!argv[1] && ((c = argv[0][0]) == 'y' || c == 'n'))
+       if (argv[0]
+        && (argv[1] || ((c = argv[0][0]) != 'y' && c != 'n'))
        ) {
-               tty = xmalloc_ttyname(STDERR_FILENO);
-               if (tty == NULL) {
-                       tty = "ttyname";
-               } else if (stat(tty, &sb) == 0) {
-                       mode_t m;
-                       if (c == 0) {
-                               puts((sb.st_mode & (S_IWGRP|S_IWOTH)) ? "is y" : "is n");
-                               return EXIT_SUCCESS;
-                       }
-                       m = (c == 'y') ? sb.st_mode | S_IWGRP_OR_S_IWOTH
-                                      : sb.st_mode & ~(S_IWGRP|S_IWOTH);
-                       if (chmod(tty, m) == 0) {
-                               return EXIT_SUCCESS;
-                       }
-               }
-               bb_simple_perror_msg_and_die(tty);
+               bb_show_usage();
+       }
+
+       if (!isatty(STDIN_FILENO))
+               bb_error_msg_and_die("not a tty");
+
+       xfstat(STDIN_FILENO, &sb, "stderr");
+       if (c == 0) {
+               puts((sb.st_mode & (S_IWGRP|S_IWOTH)) ? "is y" : "is n");
+               return EXIT_SUCCESS;
        }
-       bb_show_usage();
+       m = (c == 'y') ? sb.st_mode | S_IWGRP_OR_S_IWOTH
+                      : sb.st_mode & ~(S_IWGRP|S_IWOTH);
+       if (fchmod(STDIN_FILENO, m) != 0)
+               bb_perror_nomsg_and_die();
+       return EXIT_SUCCESS;
 }
diff --git a/init/reboot.h b/init/reboot.h
new file mode 100644 (file)
index 0000000..9497639
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Definitions related to the reboot() system call,
+ * shared between init.c and halt.c.
+ */
+
+#include <sys/reboot.h>
+
+#ifndef RB_HALT_SYSTEM
+# if defined(__linux__)
+#  define RB_HALT_SYSTEM  0xcdef0123
+#  define RB_ENABLE_CAD   0x89abcdef
+#  define RB_DISABLE_CAD  0
+#  define RB_POWER_OFF    0x4321fedc
+#  define RB_AUTOBOOT     0x01234567
+# elif defined(RB_HALT)
+#  define RB_HALT_SYSTEM  RB_HALT
+# endif
+#endif
+
+/* Stop system and switch power off if possible.  */
+#ifndef RB_POWER_OFF
+# if defined(RB_POWERDOWN)
+#  define RB_POWER_OFF  RB_POWERDOWN
+# elif defined(__linux__)
+#  define RB_POWER_OFF  0x4321fedc
+# else
+#  warning "poweroff unsupported, using halt as fallback"
+#  define RB_POWER_OFF  RB_HALT_SYSTEM
+# endif
+#endif
index 09bf892..19021fe 100644 (file)
@@ -14,9 +14,9 @@ config PASSWORD_MINLEN
        help
          Minimum allowable password length.
 
-config MD5_SIZE_VS_SPEED
+config MD5_SMALL
        int "MD5: Trade bytes for speed (0:fast, 3:slow)"
-       default 2
+       default 1
        range 0 3
        help
          Trade binary size versus speed for the md5sum algorithm.
@@ -28,6 +28,16 @@ config MD5_SIZE_VS_SPEED
          2                   3.0                5088
          3 (smallest)        5.1                4912
 
+config SHA3_SMALL
+       int "SHA3: Trade bytes for speed (0:fast, 1:slow)"
+       default 1
+       range 0 1
+       help
+         Trade binary size versus speed for the sha3sum algorithm.
+         SHA3_SMALL=0 compared to SHA3_SMALL=1 (approximate):
+         64-bit x86: +270 bytes of code, 45% faster
+         32-bit x86: +450 bytes of code, 75% faster
+
 config FEATURE_FAST_TOP
        bool "Faster /proc scanning code (+100 bytes)"
        default y
@@ -43,6 +53,17 @@ config FEATURE_ETC_NETWORKS
          a rarely used feature which allows you to use names
          instead of IP/mask pairs in route command.
 
+config FEATURE_USE_TERMIOS
+       bool "Use termios to manipulate the screen"
+       default y
+       depends on MORE || TOP || POWERTOP
+       help
+         This option allows utilities such as 'more' and 'top' to determine
+         the size of the screen. If you leave this disabled, your utilities
+         that display things on the screen will be especially primitive and
+         will be unable to determine the current screen size, and will be
+         unable to move the cursor.
+
 config FEATURE_EDITING
        bool "Command line editing"
        default y
@@ -69,18 +90,34 @@ config FEATURE_EDITING_VI
 
 config FEATURE_EDITING_HISTORY
        int "History size"
-       range 0 99999
+       # Don't allow way too big values here, code uses fixed "char *history[N]" struct member
+       range 0 9999
        default 255
        depends on FEATURE_EDITING
        help
-         Specify command history size.
+         Specify command history size (0 - disable).
 
 config FEATURE_EDITING_SAVEHISTORY
        bool "History saving"
        default y
-       depends on ASH && FEATURE_EDITING
+       depends on FEATURE_EDITING
+       help
+         Enable history saving in shells.
+
+config FEATURE_EDITING_SAVE_ON_EXIT
+       bool "Save history on shell exit, not after every command"
+       default n
+       depends on FEATURE_EDITING_SAVEHISTORY
+       help
+         Save history on shell exit, not after every command.
+
+config FEATURE_REVERSE_SEARCH
+       bool "Reverse history search"
+       default y
+       depends on FEATURE_EDITING_SAVEHISTORY
        help
-         Enable history saving in ash shell.
+         Enable readline-like Ctrl-R combination for reverse history search.
+         Increases code by about 0.5k.
 
 config FEATURE_TAB_COMPLETION
        bool "Tab completion"
@@ -124,7 +161,7 @@ config FEATURE_NON_POSIX_CP
          and create a regular file. This does not conform to POSIX,
          but prevents a symlink attack.
          Similarly, "cp file device" will not send file's data
-         to the device.
+         to the device. (To do that, use "cat file >device")
 
 config FEATURE_VERBOSE_CP_MESSAGE
        bool "Give more precise messages when copy fails (cp, mv etc)"
@@ -145,14 +182,34 @@ config FEATURE_COPYBUF_KB
        range 1 1024
        default 4
        help
-         Size of buffer used by cp, mv, install etc.
+         Size of buffer used by cp, mv, install, wget etc.
          Buffers which are 4 kb or less will be allocated on stack.
          Bigger buffers will be allocated with mmap, with fallback to 4 kb
          stack buffer if mmap fails.
 
+config FEATURE_SKIP_ROOTFS
+       bool "Skip rootfs in mount table"
+       default y
+       help
+         Ignore rootfs entry in mount table.
+
+         In Linux, kernel has a special filesystem, rootfs, which is initially
+         mounted on /. It contains initramfs data, if kernel is configured
+         to have one. Usually, another file system is mounted over / early
+         in boot process, and therefore most tools which manipulate
+         mount table, such as df, will skip rootfs entry.
+
+         However, some systems do not mount anything on /.
+         If you need to configure busybox for one of these systems,
+         you may find it useful to turn this option off to make df show
+         initramfs statistics.
+
+         Otherwise, choose Y.
+
 config MONOTONIC_SYSCALL
        bool "Use clock_gettime(CLOCK_MONOTONIC) syscall"
        default n
+       select PLATFORM_LINUX
        help
          Use clock_gettime(CLOCK_MONOTONIC) syscall for measuring
          time intervals (time, ping, traceroute etc need this).
index 5c56700..a6468f1 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 libbb/appletlib.o: include/usage_compressed.h
 
@@ -13,7 +13,7 @@ INSERT
 lib-y += appletlib.o
 lib-y += ask_confirmation.o
 lib-y += bb_askpass.o
-lib-y += bb_basename.o
+lib-y += bb_bswap_64.o
 lib-y += bb_do_delay.o
 lib-y += bb_pwd.o
 lib-y += bb_qsort.o
@@ -27,8 +27,6 @@ lib-y += concat_subpath_file.o
 lib-y += copy_file.o
 lib-y += copyfd.o
 lib-y += crc32.o
-lib-y += create_icmp6_socket.o
-lib-y += create_icmp_socket.o
 lib-y += default_error_retval.o
 lib-y += device_open.o
 lib-y += dump.o
@@ -58,16 +56,12 @@ lib-y += llist.o
 lib-y += login.o
 lib-y += make_directory.o
 lib-y += makedev.o
-lib-y += match_fstype.o
-lib-y += md5.o
-# Alternative (disabled) implementation
-#lib-y += md5prime.o
+lib-y += hash_md5_sha.o
+# Alternative (disabled) MD5 implementation
+#lib-y += hash_md5prime.o
 lib-y += messages.o
 lib-y += mode_string.o
-lib-y += mtab_file.o
-lib-y += obscure.o
 lib-y += parse_mode.o
-lib-y += parse_config.o
 lib-y += perror_msg.o
 lib-y += perror_nomsg.o
 lib-y += perror_nomsg_and_die.o
@@ -91,7 +85,6 @@ lib-y += safe_poll.o
 lib-y += safe_strncpy.o
 lib-y += safe_write.o
 lib-y += setup_environment.o
-lib-y += sha1.o
 lib-y += signals.o
 lib-y += simplify_path.o
 lib-y += single_argv.o
@@ -102,7 +95,6 @@ lib-y += strrstr.o
 lib-y += time.o
 lib-y += trim.o
 lib-y += u_signal_names.o
-lib-y += udp_io.o
 lib-y += uuencode.o
 lib-y += vdprintf.o
 lib-y += verror_msg.o
@@ -121,6 +113,8 @@ lib-y += xgethostbyname.o
 lib-y += xreadlink.o
 lib-y += xrealloc_vector.o
 
+lib-$(CONFIG_PLATFORM_LINUX) += match_fstype.o
+
 lib-$(CONFIG_FEATURE_UTMP) += utmp.o
 
 # A mix of optimizations (why build stuff we know won't be used)
@@ -130,6 +124,15 @@ lib-$(CONFIG_FEATURE_MTAB_SUPPORT) += mtab.o
 lib-$(CONFIG_UNICODE_SUPPORT) += unicode.o
 lib-$(CONFIG_FEATURE_CHECK_NAMES) += die_if_bad_username.o
 
+lib-$(CONFIG_NC) += udp_io.o
+lib-$(CONFIG_DNSD) += udp_io.o
+lib-$(CONFIG_NTPD) += udp_io.o
+lib-$(CONFIG_TFTP) += udp_io.o
+lib-$(CONFIG_TFTPD) += udp_io.o
+lib-$(CONFIG_TCPSVD) += udp_io.o
+lib-$(CONFIG_UDPSVD) += udp_io.o
+lib-$(CONFIG_TRACEROUTE) += udp_io.o
+
 lib-$(CONFIG_LOSETUP) += loop.o
 lib-$(CONFIG_FEATURE_MOUNT_LOOP) += loop.o
 
@@ -138,10 +141,10 @@ lib-$(CONFIG_ADDUSER) += update_passwd.o
 lib-$(CONFIG_DELGROUP) += update_passwd.o
 lib-$(CONFIG_DELUSER) += update_passwd.o
 
-lib-$(CONFIG_PASSWD) += pw_encrypt.o update_passwd.o
+lib-$(CONFIG_PASSWD) += pw_encrypt.o update_passwd.o obscure.o
 lib-$(CONFIG_CHPASSWD) += pw_encrypt.o update_passwd.o
 lib-$(CONFIG_CRYPTPW) += pw_encrypt.o
-lib-$(CONFIG_SULOGIN) += pw_encrypt.o
+lib-$(CONFIG_SULOGIN) += pw_encrypt.o correct_password.o
 lib-$(CONFIG_VLOCK) += pw_encrypt.o correct_password.o
 lib-$(CONFIG_SU) += pw_encrypt.o correct_password.o
 lib-$(CONFIG_LOGIN) += pw_encrypt.o correct_password.o
@@ -157,6 +160,17 @@ lib-$(CONFIG_MOUNT) += find_mount_point.o
 lib-$(CONFIG_HWCLOCK) += rtc.o
 lib-$(CONFIG_RTCWAKE) += rtc.o
 
+lib-$(CONFIG_IOSTAT) += get_cpu_count.o
+lib-$(CONFIG_MPSTAT) += get_cpu_count.o
+lib-$(CONFIG_POWERTOP) += get_cpu_count.o
+
+lib-$(CONFIG_PING) += inet_cksum.o
+lib-$(CONFIG_TRACEROUTE) += inet_cksum.o
+lib-$(CONFIG_TRACEROUTE6) += inet_cksum.o
+lib-$(CONFIG_UDHCPC) += inet_cksum.o
+lib-$(CONFIG_UDHCPC6) += inet_cksum.o
+lib-$(CONFIG_UDHCPD) += inet_cksum.o
+
 # We shouldn't build xregcomp.c if we don't need it - this ensures we don't
 # require regex.h to be in the include dir even if we don't need it thereby
 # allowing us to build busybox even if uclibc regex support is disabled.
index 4f28f7e..6e63dc5 100644 (file)
@@ -8,4 +8,3 @@ that you wrote that is mis-attributed, do let me know so we can fix that up.
 
        Erik Andersen
        <andersen@codepoet.org>
-
index 4924a97..8f3a8a1 100644 (file)
@@ -7,9 +7,9 @@
  * here, please feel free to acknowledge your work.
  *
  * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
- * Permission has been granted to redistribute this code under the GPL.
+ * Permission has been granted to redistribute this code under GPL.
  *
- * Licensed under GPLv2 or later, see file License in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* We are trying to not use printf, this benefits the case when selected
  * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :(
  */
 #include "busybox.h"
-#include <assert.h>
-#include <malloc.h>
-/* Try to pull in PAGE_SIZE */
-#ifdef __linux__
-# include <sys/user.h>
-#endif
-#ifdef __GNU__ /* Hurd */
-# include <mach/vm_param.h>
+
+#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
+    || defined(__APPLE__) \
+    )
+# include <malloc.h> /* for mallopt */
 #endif
 
 
@@ -43,7 +40,6 @@
 #include "applets.h"
 #undef PROTOTYPES
 
-
 /* Include generated applet names, pointers to <applet>_main, etc */
 #include "applet_tables.h"
 /* ...and if applet_tables generator says we have only one applet... */
@@ -54,9 +50,9 @@
 # define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__
 #endif
 
-
 #include "usage_compressed.h"
 
+
 #if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
 static const char usage_messages[] ALIGN1 = UNPACKED_USAGE;
 #else
@@ -66,7 +62,7 @@ static const char usage_messages[] ALIGN1 = UNPACKED_USAGE;
 #if ENABLE_FEATURE_COMPRESS_USAGE
 
 static const char packed_usage[] ALIGN1 = { PACKED_USAGE };
-# include "unarchive.h"
+# include "bb_archive.h"
 static const char *unpack_usage_messages(void)
 {
        char *outbuf = NULL;
@@ -75,7 +71,7 @@ static const char *unpack_usage_messages(void)
 
        i = start_bunzip(&bd,
                        /* src_fd: */ -1,
-                       /* inbuf:  */ (void *)packed_usage,
+                       /* inbuf:  */ packed_usage,
                        /* len:    */ sizeof(packed_usage));
        /* read_bunzip can longjmp to start_bunzip, and ultimately
         * end up here with i != 0 on read data errors! Not trivial */
@@ -103,14 +99,13 @@ void FAST_FUNC bb_show_usage(void)
        if (ENABLE_SHOW_USAGE) {
 #ifdef SINGLE_APPLET_STR
                /* Imagine that this applet is "true". Dont suck in printf! */
-               const char *p;
-               const char *usage_string = p = unpack_usage_messages();
+               const char *usage_string = unpack_usage_messages();
 
-               if (*p == '\b') {
+               if (*usage_string == '\b') {
                        full_write2_str("No help available.\n\n");
                } else {
                        full_write2_str("Usage: "SINGLE_APPLET_STR" ");
-                       full_write2_str(p);
+                       full_write2_str(usage_string);
                        full_write2_str("\n\n");
                }
                if (ENABLE_FEATURE_CLEAN_UP)
@@ -145,10 +140,9 @@ void FAST_FUNC bb_show_usage(void)
 }
 
 #if NUM_APPLETS > 8
-/* NB: any char pointer will work as well, not necessarily applet_names */
-static int applet_name_compare(const void *name, const void *v)
+static int applet_name_compare(const void *name, const void *idx)
 {
-       int i = (const char *)v - applet_names;
+       int i = (int)(ptrdiff_t)idx - 1;
        return strcmp(name, APPLET_NAME(i));
 }
 #endif
@@ -157,10 +151,12 @@ int FAST_FUNC find_applet_by_name(const char *name)
 #if NUM_APPLETS > 8
        /* Do a binary search to find the applet entry given the name. */
        const char *p;
-       p = bsearch(name, applet_names, ARRAY_SIZE(applet_main), 1, applet_name_compare);
-       if (!p)
-               return -1;
-       return p - applet_names;
+       p = bsearch(name, (void*)(ptrdiff_t)1, ARRAY_SIZE(applet_main), 1, applet_name_compare);
+       /*
+        * if (!p) return -1;
+        * ^^^^^^^^^^^^^^^^^^ the code below will do this if p == NULL :)
+        */
+       return (int)(ptrdiff_t)p - 1;
 #else
        /* A version which does not pull in bsearch */
        int i = 0;
@@ -228,15 +224,14 @@ bool re_execed;
 
 IF_FEATURE_SUID(static uid_t ruid;)  /* real uid */
 
-#if ENABLE_FEATURE_SUID_CONFIG
+# if ENABLE_FEATURE_SUID_CONFIG
 
-/* applets[] is const, so we have to define this "override" structure */
-static struct BB_suid_config {
+static struct suid_config_t {
+       /* next ptr must be first: this struct needs to be llist-compatible */
+       struct suid_config_t *m_next;
+       struct bb_uidgid_t m_ugid;
        int m_applet;
-       uid_t m_uid;
-       gid_t m_gid;
        mode_t m_mode;
-       struct BB_suid_config *m_next;
 } *suid_config;
 
 static bool suid_cfg_readable;
@@ -245,13 +240,10 @@ static bool suid_cfg_readable;
 static int ingroup(uid_t u, gid_t g)
 {
        struct group *grp = getgrgid(g);
-
        if (grp) {
                char **mem;
-
                for (mem = grp->gr_mem; *mem; mem++) {
                        struct passwd *pwd = getpwnam(*mem);
-
                        if (pwd && (pwd->pw_uid == u))
                                return 1;
                }
@@ -259,9 +251,7 @@ static int ingroup(uid_t u, gid_t g)
        return 0;
 }
 
-/* This should probably be a libbb routine.  In that case,
- * I'd probably rename it to something like bb_trimmed_slice.
- */
+/* libbb candidate */
 static char *get_trimmed_slice(char *s, char *e)
 {
        /* First, consider the value at e to be nul and back up until we
@@ -279,38 +269,19 @@ static char *get_trimmed_slice(char *s, char *e)
        return skip_whitespace(s);
 }
 
-/* Don't depend on the tools to combine strings. */
-static const char config_file[] ALIGN1 = "/etc/busybox.conf";
-
-/* We don't supply a value for the nul, so an index adjustment is
- * necessary below.  Also, we use unsigned short here to save some
- * space even though these are really mode_t values. */
-static const unsigned short mode_mask[] ALIGN2 = {
-       /*  SST     sst                 xxx         --- */
-       S_ISUID,    S_ISUID|S_IXUSR,    S_IXUSR,    0,  /* user */
-       S_ISGID,    S_ISGID|S_IXGRP,    S_IXGRP,    0,  /* group */
-       0,          S_IXOTH,            S_IXOTH,    0   /* other */
-};
-
-#define parse_error(x)  do { errmsg = x; goto pe_label; } while (0)
-
 static void parse_config_file(void)
 {
-       struct BB_suid_config *sct_head;
-       struct BB_suid_config *sct;
+       /* Don't depend on the tools to combine strings. */
+       static const char config_file[] ALIGN1 = "/etc/busybox.conf";
+
+       struct suid_config_t *sct_head;
        int applet_no;
        FILE *f;
        const char *errmsg;
-       char *s;
-       char *e;
-       int i;
        unsigned lc;
        smallint section;
-       char buffer[256];
        struct stat st;
 
-       assert(!suid_config); /* Should be set to NULL by bss init. */
-
        ruid = getuid();
        if (ruid == 0) /* run by root - don't need to even read config file */
                return;
@@ -319,7 +290,7 @@ static void parse_config_file(void)
         || !S_ISREG(st.st_mode)                /* Not a regular file? */
         || (st.st_uid != 0)                    /* Not owned by root? */
         || (st.st_mode & (S_IWGRP | S_IWOTH))  /* Writable by non-root? */
-        || !(f = fopen_for_read(config_file))      /* Cannot open? */
+        || !(f = fopen_for_read(config_file))  /* Cannot open? */
        ) {
                return;
        }
@@ -329,18 +300,21 @@ static void parse_config_file(void)
        section = lc = 0;
 
        while (1) {
-               s = buffer;
-
-               if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
-// why?
-                       if (ferror(f)) {   /* Make sure it wasn't a read error. */
-                               parse_error("reading");
-                       }
+               char buffer[256];
+               char *s;
+
+               if (!fgets(buffer, sizeof(buffer), f)) { /* Are we done? */
+                       // Looks like bloat
+                       //if (ferror(f)) {   /* Make sure it wasn't a read error. */
+                       //      errmsg = "reading";
+                       //      goto pe_label;
+                       //}
                        fclose(f);
                        suid_config = sct_head; /* Success, so set the pointer. */
                        return;
                }
 
+               s = buffer;
                lc++;                                   /* Got a (partial) line. */
 
                /* If a line is too long for our buffer, we consider it an error.
@@ -352,7 +326,8 @@ static void parse_config_file(void)
                 * we do err on the side of caution.  Besides, the line would be
                 * too long if it did end with a newline. */
                if (!strchr(s, '\n') && !feof(f)) {
-                       parse_error("line too long");
+                       errmsg = "line too long";
+                       goto pe_label;
                }
 
                /* Trim leading and trailing whitespace, ignoring comments, and
@@ -368,12 +343,13 @@ static void parse_config_file(void)
                        /* Unlike the old code, we ignore leading and trailing
                         * whitespace for the section name.  We also require that
                         * there are no stray characters after the closing bracket. */
-                       e = strchr(s, ']');
+                       char *e = strchr(s, ']');
                        if (!e   /* Missing right bracket? */
                         || e[1] /* Trailing characters? */
                         || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
                        ) {
-                               parse_error("section header");
+                               errmsg = "section header";
+                               goto pe_label;
                        }
                        /* Right now we only have one section so just check it.
                         * If more sections are added in the future, please don't
@@ -398,12 +374,13 @@ static void parse_config_file(void)
                         * where both key and value could contain inner whitespace. */
 
                        /* First get the key (an applet name in our case). */
-                       e = strchr(s, '=');
+                       char *e = strchr(s, '=');
                        if (e) {
                                s = get_trimmed_slice(s, e);
                        }
                        if (!e || !*s) {        /* Missing '=' or empty key. */
-                               parse_error("keyword");
+                               errmsg = "keyword";
+                               goto pe_label;
                        }
 
                        /* Ok, we have an applet name.  Process the rhs if this
@@ -412,13 +389,16 @@ static void parse_config_file(void)
                         * up when the busybox configuration is changed. */
                        applet_no = find_applet_by_name(s);
                        if (applet_no >= 0) {
+                               unsigned i;
+                               struct suid_config_t *sct;
+
                                /* Note: We currently don't check for duplicates!
                                 * The last config line for each applet will be the
                                 * one used since we insert at the head of the list.
                                 * I suppose this could be considered a feature. */
-                               sct = xmalloc(sizeof(struct BB_suid_config));
+                               sct = xzalloc(sizeof(*sct));
                                sct->m_applet = applet_no;
-                               sct->m_mode = 0;
+                               /*sct->m_mode = 0;*/
                                sct->m_next = sct_head;
                                sct_head = sct;
 
@@ -427,48 +407,41 @@ static void parse_config_file(void)
                                e = skip_whitespace(e+1);
 
                                for (i = 0; i < 3; i++) {
-                                       /* There are 4 chars + 1 nul for each of user/group/other. */
-                                       static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-";
-
-                                       const char *q;
-                                       q = strchrnul(mode_chars + 5*i, *e++);
-                                       if (!*q) {
-                                               parse_error("mode");
+                                       /* There are 4 chars for each of user/group/other.
+                                        * "x-xx" instead of "x-" are to make
+                                        * "idx > 3" check catch invalid chars.
+                                        */
+                                       static const char mode_chars[] ALIGN1 = "Ssx-" "Ssx-" "x-xx";
+                                       static const unsigned short mode_mask[] ALIGN2 = {
+                                               S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* Ssx- */
+                                               S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* Ssx- */
+                                                                         S_IXOTH, 0  /*   x- */
+                                       };
+                                       const char *q = strchrnul(mode_chars + 4*i, *e);
+                                       unsigned idx = q - (mode_chars + 4*i);
+                                       if (idx > 3) {
+                                               errmsg = "mode";
+                                               goto pe_label;
                                        }
-                                       /* Adjust by -i to account for nul. */
-                                       sct->m_mode |= mode_mask[(q - mode_chars) - i];
+                                       sct->m_mode |= mode_mask[q - mode_chars];
+                                       e++;
                                }
 
-                               /* Now get the the user/group info. */
+                               /* Now get the user/group info. */
 
                                s = skip_whitespace(e);
-
-                               /* Note: we require whitespace between the mode and the
-                                * user/group info. */
-                               if ((s == e) || !(e = strchr(s, '.'))) {
-                                       parse_error("<uid>.<gid>");
-                               }
-                               *e++ = '\0';
-
-                               /* We can't use get_ug_id here since it would exit()
-                                * if a uid or gid was not found.  Oh well... */
-                               sct->m_uid = bb_strtoul(s, NULL, 10);
-                               if (errno) {
-                                       struct passwd *pwd = getpwnam(s);
-                                       if (!pwd) {
-                                               parse_error("user");
+                               /* Default is 0.0, else parse USER.GROUP: */
+                               if (*s) {
+                                       /* We require whitespace between mode and USER.GROUP */
+                                       if ((s == e) || !(e = strchr(s, '.'))) {
+                                               errmsg = "uid.gid";
+                                               goto pe_label;
                                        }
-                                       sct->m_uid = pwd->pw_uid;
-                               }
-
-                               sct->m_gid = bb_strtoul(e, NULL, 10);
-                               if (errno) {
-                                       struct group *grp;
-                                       grp = getgrnam(e);
-                                       if (!grp) {
-                                               parse_error("group");
+                                       *e = ':'; /* get_uidgid needs USER:GROUP syntax */
+                                       if (get_uidgid(&sct->m_ugid, s, /*allow_numeric:*/ 1) == 0) {
+                                               errmsg = "unknown user/group";
+                                               goto pe_label;
                                        }
-                                       sct->m_gid = grp->gr_gid;
                                }
                        }
                        continue;
@@ -482,32 +455,28 @@ static void parse_config_file(void)
                 * We may want to simply ignore such lines in case they
                 * are used in some future version of busybox. */
                if (!section) {
-                       parse_error("keyword outside section");
+                       errmsg = "keyword outside section";
+                       goto pe_label;
                }
 
        } /* while (1) */
 
  pe_label:
-       fprintf(stderr, "Parse error in %s, line %d: %s\n",
-                       config_file, lc, errmsg);
-
        fclose(f);
+       bb_error_msg("parse error in %s, line %u: %s", config_file, lc, errmsg);
+
        /* Release any allocated memory before returning. */
-       while (sct_head) {
-               sct = sct_head->m_next;
-               free(sct_head);
-               sct_head = sct;
-       }
+       llist_free((llist_t*)sct_head, NULL);
 }
-#else
+# else
 static inline void parse_config_file(void)
 {
        IF_FEATURE_SUID(ruid = getuid();)
 }
-#endif /* FEATURE_SUID_CONFIG */
+# endif /* FEATURE_SUID_CONFIG */
 
 
-#if ENABLE_FEATURE_SUID
+# if ENABLE_FEATURE_SUID
 static void check_suid(int applet_no)
 {
        gid_t rgid;  /* real gid */
@@ -516,10 +485,10 @@ static void check_suid(int applet_no)
                return; /* run by root - no need to check more */
        rgid = getgid();
 
-#if ENABLE_FEATURE_SUID_CONFIG
+#  if ENABLE_FEATURE_SUID_CONFIG
        if (suid_cfg_readable) {
                uid_t uid;
-               struct BB_suid_config *sct;
+               struct suid_config_t *sct;
                mode_t m;
 
                for (sct = suid_config; sct; sct = sct->m_next) {
@@ -528,76 +497,85 @@ static void check_suid(int applet_no)
                }
                goto check_need_suid;
  found:
+               /* Is this user allowed to run this applet? */
                m = sct->m_mode;
-               if (sct->m_uid == ruid)
+               if (sct->m_ugid.uid == ruid)
                        /* same uid */
                        m >>= 6;
-               else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
+               else if ((sct->m_ugid.gid == rgid) || ingroup(ruid, sct->m_ugid.gid))
                        /* same group / in group */
                        m >>= 3;
-
-               if (!(m & S_IXOTH))           /* is x bit not set ? */
-                       bb_error_msg_and_die("you have no permission to run this applet!");
-
-               /* _both_ sgid and group_exec have to be set for setegid */
-               if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
-                       rgid = sct->m_gid;
-               /* else (no setegid) we will set egid = rgid */
+               if (!(m & S_IXOTH)) /* is x bit not set? */
+                       bb_error_msg_and_die("you have no permission to run this applet");
 
                /* We set effective AND saved ids. If saved-id is not set
-                * like we do below, seteiud(0) can still later succeed! */
+                * like we do below, seteuid(0) can still later succeed! */
+
+               /* Are we directed to change gid
+                * (APPLET = *s* USER.GROUP or APPLET = *S* USER.GROUP)?
+                */
+               if (sct->m_mode & S_ISGID)
+                       rgid = sct->m_ugid.gid;
+               /* else: we will set egid = rgid, thus dropping sgid effect */
                if (setresgid(-1, rgid, rgid))
                        bb_perror_msg_and_die("setresgid");
 
-               /* do we have to set effective uid? */
+               /* Are we directed to change uid
+                * (APPLET = s** USER.GROUP or APPLET = S** USER.GROUP)?
+                */
                uid = ruid;
                if (sct->m_mode & S_ISUID)
-                       uid = sct->m_uid;
-               /* else (no seteuid) we will set euid = ruid */
-
+                       uid = sct->m_ugid.uid;
+               /* else: we will set euid = ruid, thus dropping suid effect */
                if (setresuid(-1, uid, uid))
                        bb_perror_msg_and_die("setresuid");
-               return;
+
+               goto ret;
        }
-#if !ENABLE_FEATURE_SUID_CONFIG_QUIET
+#   if !ENABLE_FEATURE_SUID_CONFIG_QUIET
        {
                static bool onetime = 0;
 
                if (!onetime) {
                        onetime = 1;
-                       fprintf(stderr, "Using fallback suid method\n");
+                       bb_error_msg("using fallback suid method");
                }
        }
-#endif
+#   endif
  check_need_suid:
-#endif
-       if (APPLET_SUID(applet_no) == _BB_SUID_REQUIRE) {
+#  endif
+       if (APPLET_SUID(applet_no) == BB_SUID_REQUIRE) {
                /* Real uid is not 0. If euid isn't 0 too, suid bit
                 * is most probably not set on our executable */
                if (geteuid())
                        bb_error_msg_and_die("must be suid to work properly");
-       } else if (APPLET_SUID(applet_no) == _BB_SUID_DROP) {
+       } else if (APPLET_SUID(applet_no) == BB_SUID_DROP) {
                xsetgid(rgid);  /* drop all privileges */
                xsetuid(ruid);
        }
+#  if ENABLE_FEATURE_SUID_CONFIG
+ ret: ;
+       llist_free((llist_t*)suid_config, NULL);
+#  endif
 }
-#else
-#define check_suid(x) ((void)0)
-#endif /* FEATURE_SUID */
+# else
+#  define check_suid(x) ((void)0)
+# endif /* FEATURE_SUID */
 
 
-#if ENABLE_FEATURE_INSTALLER
+# if ENABLE_FEATURE_INSTALLER
 static const char usr_bin [] ALIGN1 = "/usr/bin/";
 static const char usr_sbin[] ALIGN1 = "/usr/sbin/";
 static const char *const install_dir[] = {
        &usr_bin [8], /* "/" */
        &usr_bin [4], /* "/bin/" */
-       &usr_sbin[4], /* "/sbin/" */
-       usr_bin,
-       usr_sbin
+       &usr_sbin[4]  /* "/sbin/" */
+#  if !ENABLE_INSTALL_NO_USR
+       ,usr_bin
+       ,usr_sbin
+#  endif
 };
 
-
 /* create (sym)links for each applet */
 static void install_links(const char *busybox, int use_symbolic_links,
                char *custom_install_dir)
@@ -627,9 +605,9 @@ static void install_links(const char *busybox, int use_symbolic_links,
                free(fpc);
        }
 }
-#else
-# define install_links(x,y,z) ((void)0)
-#endif
+# else
+#  define install_links(x,y,z) ((void)0)
+# endif
 
 /* If we were called as "busybox..." */
 static int busybox_main(char **argv)
@@ -650,11 +628,15 @@ static int busybox_main(char **argv)
                full_write2_str(bb_banner); /* reuse const string */
                full_write2_str(" multi-call binary.\n"); /* reuse */
                full_write2_str(
-                       "Copyright (C) 1998-2009 Erik Andersen, Rob Landley, Denys Vlasenko\n"
-                       "and others. Licensed under GPLv2.\n"
-                       "See source distribution for full notice.\n"
+                       "BusyBox is copyrighted by many authors between 1998-2012.\n"
+                       "Licensed under GPLv2. See source distribution for detailed\n"
+                       "copyright notices.\n"
                        "\n"
-                       "Usage: busybox [function] [arguments]...\n"
+                       "Usage: busybox [function [arguments]...]\n"
+                       "   or: busybox --list"IF_FEATURE_INSTALLER("[-full]")"\n"
+                       IF_FEATURE_INSTALLER(
+                       "   or: busybox --install [-s] [DIR]\n"
+                       )
                        "   or: function [arguments]...\n"
                        "\n"
                        "\tBusyBox is a multi-call binary that combines many common Unix\n"
@@ -693,10 +675,10 @@ static int busybox_main(char **argv)
                const char *a = applet_names;
                dup2(1, 2);
                while (*a) {
-#if ENABLE_FEATURE_INSTALLER
-                       if (argv[1][6]) /* --list-path? */
+# if ENABLE_FEATURE_INSTALLER
+                       if (argv[1][6]) /* --list-full? */
                                full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1);
-#endif
+# endif
                        full_write2_str(a);
                        full_write2_str("\n");
                        i++;
@@ -708,13 +690,23 @@ static int busybox_main(char **argv)
        if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
                int use_symbolic_links;
                const char *busybox;
+
                busybox = xmalloc_readlink(bb_busybox_exec_path);
-               if (!busybox)
-                       busybox = bb_busybox_exec_path;
-               /* busybox --install [-s] [DIR]: */
-               /* -s: make symlinks */
-               /* DIR: directory to install links to */
-               use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && argv++);
+               if (!busybox) {
+                       /* bb_busybox_exec_path is usually "/proc/self/exe".
+                        * In chroot, readlink("/proc/self/exe") usually fails.
+                        * In such case, better use argv[0] as symlink target
+                        * if it is a full path name.
+                        */
+                       if (argv[0][0] != '/')
+                               bb_error_msg_and_die("'%s' is not an absolute path", argv[0]);
+                       busybox = argv[0];
+               }
+               /* busybox --install [-s] [DIR]:
+                * -s: make symlinks
+                * DIR: directory to install links to
+                */
+               use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && ++argv);
                install_links(busybox, use_symbolic_links, argv[2]);
                return 0;
        }
@@ -756,8 +748,11 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv)
                /* Special case. POSIX says "test --help"
                 * should be no different from e.g. "test --foo".  */
 //TODO: just compare applet_no with APPLET_NO_test
-               if (!ENABLE_TEST || strcmp(applet_name, "test") != 0)
+               if (!ENABLE_TEST || strcmp(applet_name, "test") != 0) {
+                       /* If you want "foo --help" to return 0: */
+                       xfunc_error_retval = 0;
                        bb_show_usage();
+               }
        }
        if (ENABLE_FEATURE_SUID)
                check_suid(applet_no);
@@ -769,7 +764,7 @@ void FAST_FUNC run_applet_and_exit(const char *name, char **argv)
        int applet = find_applet_by_name(name);
        if (applet >= 0)
                run_applet_no_and_exit(applet, argv);
-       if (!strncmp(name, "busybox", 7))
+       if (strncmp(name, "busybox", 7) == 0)
                exit(busybox_main(argv));
 }
 
@@ -784,31 +779,20 @@ int main(int argc UNUSED_PARAM, char **argv)
 #endif
 {
        /* Tweak malloc for reduced memory consumption */
-#ifndef PAGE_SIZE
-# define PAGE_SIZE (4*1024) /* guess */
-#endif
 #ifdef M_TRIM_THRESHOLD
        /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory
         * to keep before releasing to the OS
         * Default is way too big: 256k
         */
-       mallopt(M_TRIM_THRESHOLD, 2 * PAGE_SIZE);
+       mallopt(M_TRIM_THRESHOLD, 8 * 1024);
 #endif
 #ifdef M_MMAP_THRESHOLD
        /* M_MMAP_THRESHOLD is the request size threshold for using mmap()
         * Default is too big: 256k
         */
-       mallopt(M_MMAP_THRESHOLD, 8 * PAGE_SIZE - 256);
+       mallopt(M_MMAP_THRESHOLD, 32 * 1024 - 256);
 #endif
 
-#if defined(SINGLE_APPLET_MAIN)
-       /* Only one applet is selected by the user! */
-       /* applet_names in this case is just "applet\0\0" */
-       lbb_prepare(applet_names IF_FEATURE_INDIVIDUAL(, argv));
-       return SINGLE_APPLET_MAIN(argc, argv);
-#else
-       lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv));
-
 #if !BB_MMU
        /* NOMMU re-exec trick sets high-order bit in first byte of name */
        if (argv[0][0] & 0x80) {
@@ -816,6 +800,19 @@ int main(int argc UNUSED_PARAM, char **argv)
                argv[0][0] &= 0x7f;
        }
 #endif
+
+#if defined(SINGLE_APPLET_MAIN)
+       /* Only one applet is selected in .config */
+       if (argv[1] && strncmp(argv[0], "busybox", 7) == 0) {
+               /* "busybox <applet> <params>" should still work as expected */
+               argv++;
+       }
+       /* applet_names in this case is just "applet\0\0" */
+       lbb_prepare(applet_names IF_FEATURE_INDIVIDUAL(, argv));
+       return SINGLE_APPLET_MAIN(argc, argv);
+#else
+       lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv));
+
        applet_name = argv[0];
        if (applet_name[0] == '-')
                applet_name++;
index e0e9419..d95729c 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* Read a line from stdin.  If the first non-whitespace char is 'y' or 'Y',
index 12ebe17..77c1bcd 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -30,14 +30,23 @@ char* FAST_FUNC bb_ask(const int fd, int timeout, const char *prompt)
        struct sigaction sa, oldsa;
        struct termios tio, oldtio;
 
-       tcgetattr(fd, &oldtio);
+       fputs(prompt, stdout);
+       fflush_all();
        tcflush(fd, TCIFLUSH);
+
+       tcgetattr(fd, &oldtio);
        tio = oldtio;
-#ifndef IUCLC
-# define IUCLC 0
-#endif
+#if 0
+       /* Switch off UPPERCASE->lowercase conversion (never used since 198x)
+        * and XON/XOFF (why we want to mess with this??)
+        */
+# ifndef IUCLC
+#  define IUCLC 0
+# endif
        tio.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
-       tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP);
+#endif
+       /* Switch off echo */
+       tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL);
        tcsetattr(fd, TCSANOW, &tio);
 
        memset(&sa, 0, sizeof(sa));
@@ -50,16 +59,15 @@ char* FAST_FUNC bb_ask(const int fd, int timeout, const char *prompt)
                alarm(timeout);
        }
 
-       fputs(prompt, stdout);
-       fflush_all();
-
        if (!passwd)
                passwd = xmalloc(sizeof_passwd);
        ret = passwd;
        i = 0;
        while (1) {
                int r = read(fd, &ret[i], 1);
-               if (r < 0) {
+               if ((i == 0 && r == 0) /* EOF (^D) with no password */
+                || r < 0
+               ) {
                        /* read is interrupted by timeout or ^C */
                        ret = NULL;
                        break;
diff --git a/libbb/bb_basename.c b/libbb/bb_basename.c
deleted file mode 100644 (file)
index bab4166..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * Utility routines.
- *
- * Copyright (C) 2007 Denys Vlasenko
- *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
- */
-
-#include "libbb.h"
-
-const char* FAST_FUNC bb_basename(const char *name)
-{
-       const char *cp = strrchr(name, '/');
-       if (cp)
-               return cp + 1;
-       return name;
-}
diff --git a/libbb/bb_bswap_64.c b/libbb/bb_bswap_64.c
new file mode 100644 (file)
index 0000000..ce9d53b
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2010 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+#if !(ULONG_MAX > 0xffffffff)
+uint64_t FAST_FUNC bb_bswap_64(uint64_t x)
+{
+       return bswap_64(x);
+}
+#endif
index 139dc18..05c879f 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2005 by Tito Ragusa <tito-wolit@tiscali.it>
  *
- * Licensed under the GPL v2, see the file LICENSE in this tarball.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index d728577..8250cd4 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  * Copyright (C) 2008 by Tito Ragusa <farmatito@tiscali.it>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -72,13 +72,13 @@ char* FAST_FUNC gid2group(gid_t gid)
        return (gr) ? gr->gr_name : NULL;
 }
 
-char* FAST_FUNC uid2uname_utoa(long uid)
+char* FAST_FUNC uid2uname_utoa(uid_t uid)
 {
        char *name = uid2uname(uid);
        return (name) ? name : utoa(uid);
 }
 
-char* FAST_FUNC gid2group_utoa(long gid)
+char* FAST_FUNC gid2group_utoa(gid_t gid)
 {
        char *name = gid2group(gid);
        return (name) ? name : utoa(gid);
@@ -110,3 +110,51 @@ unsigned long FAST_FUNC get_ug_id(const char *s,
                return xname2id(s);
        return r;
 }
+
+/* Experimental "mallocing" API.
+ * The goal is nice: "we want to support a case when "guests" group is very large"
+ * but the code is butt-ugly.
+ */
+#if 0
+static char *find_latest(char last, char *cp)
+{
+       if (!cp)
+               return last;
+       cp += strlen(cp) + 1;
+       if (last < cp)
+               last = cp;
+       return last;
+}
+
+struct group* FAST_FUNC xmalloc_getgrnam(const char *name)
+{
+       struct {
+               struct group gr;
+               // May still be not enough!
+               char buf[64*1024 - sizeof(struct group) - 16];
+       } *s;
+       struct group *grp;
+       int r;
+       char *last;
+       char **gr_mem;
+
+       s = xmalloc(sizeof(*s));
+       r = getgrnam_r(name, &s->gr, s->buf, sizeof(s->buf), &grp);
+       if (!grp) {
+               free(s);
+               return grp;
+       }
+       last = find_latest(s->buf, grp->gr_name);
+       last = find_latest(last, grp->gr_passwd);
+       gr_mem = grp->gr_mem;
+       while (*gr_mem)
+               last = find_latest(last, *gr_mem++);
+       gr_mem++; /* points past NULL */
+       if (last < (char*)gr_mem)
+               last = (char*)gr_mem;
+//FIXME: what if we get not only truncated, but also moved here?
+// grp->gr_name pointer and friends are invalid now!!!
+       s = xrealloc(s, last - (char*)s);
+       return grp;
+}
+#endif
index 9773afa..a54e723 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (c) 2008 Denys Vlasenko
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index 1e96710..5dde784 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index 87cd744..949f26b 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -36,14 +36,14 @@ static unsigned long long ret_ERANGE(void)
        return ULLONG_MAX;
 }
 
-static unsigned long long handle_errors(unsigned long long v, char **endp, char *endptr)
+static unsigned long long handle_errors(unsigned long long v, char **endp)
 {
-       if (endp) *endp = endptr;
+       char next_ch = **endp;
 
        /* errno is already set to ERANGE by strtoXXX if value overflowed */
-       if (endptr[0]) {
+       if (next_ch) {
                /* "1234abcg" or out-of-range? */
-               if (isalnum(endptr[0]) || errno)
+               if (isalnum(next_ch) || errno)
                        return ret_ERANGE();
                /* good number, just suspicious terminator */
                errno = EINVAL;
@@ -57,30 +57,37 @@ unsigned long long FAST_FUNC bb_strtoull(const char *arg, char **endp, int base)
        unsigned long long v;
        char *endptr;
 
+       if (!endp) endp = &endptr;
+       *endp = (char*) arg;
+
        /* strtoul("  -4200000000") returns 94967296, errno 0 (!) */
        /* I don't think that this is right. Preventing this... */
        if (!isalnum(arg[0])) return ret_ERANGE();
 
        /* not 100% correct for lib func, but convenient for the caller */
        errno = 0;
-       v = strtoull(arg, &endptr, base);
-       return handle_errors(v, endp, endptr);
+       v = strtoull(arg, endp, base);
+       return handle_errors(v, endp);
 }
 
 long long FAST_FUNC bb_strtoll(const char *arg, char **endp, int base)
 {
        unsigned long long v;
        char *endptr;
+       char first;
+
+       if (!endp) endp = &endptr;
+       *endp = (char*) arg;
 
        /* Check for the weird "feature":
         * a "-" string is apparently a valid "number" for strto[u]l[l]!
         * It returns zero and errno is 0! :( */
-       char first = (arg[0] != '-' ? arg[0] : arg[1]);
+       first = (arg[0] != '-' ? arg[0] : arg[1]);
        if (!isalnum(first)) return ret_ERANGE();
 
        errno = 0;
-       v = strtoll(arg, &endptr, base);
-       return handle_errors(v, endp, endptr);
+       v = strtoll(arg, endp, base);
+       return handle_errors(v, endp);
 }
 
 #if ULONG_MAX != ULLONG_MAX
@@ -89,23 +96,30 @@ unsigned long FAST_FUNC bb_strtoul(const char *arg, char **endp, int base)
        unsigned long v;
        char *endptr;
 
+       if (!endp) endp = &endptr;
+       *endp = (char*) arg;
+
        if (!isalnum(arg[0])) return ret_ERANGE();
        errno = 0;
-       v = strtoul(arg, &endptr, base);
-       return handle_errors(v, endp, endptr);
+       v = strtoul(arg, endp, base);
+       return handle_errors(v, endp);
 }
 
 long FAST_FUNC bb_strtol(const char *arg, char **endp, int base)
 {
        long v;
        char *endptr;
+       char first;
 
-       char first = (arg[0] != '-' ? arg[0] : arg[1]);
+       if (!endp) endp = &endptr;
+       *endp = (char*) arg;
+
+       first = (arg[0] != '-' ? arg[0] : arg[1]);
        if (!isalnum(first)) return ret_ERANGE();
 
        errno = 0;
-       v = strtol(arg, &endptr, base);
-       return handle_errors(v, endp, endptr);
+       v = strtol(arg, endp, base);
+       return handle_errors(v, endp);
 }
 #endif
 
@@ -115,25 +129,32 @@ unsigned FAST_FUNC bb_strtou(const char *arg, char **endp, int base)
        unsigned long v;
        char *endptr;
 
+       if (!endp) endp = &endptr;
+       *endp = (char*) arg;
+
        if (!isalnum(arg[0])) return ret_ERANGE();
        errno = 0;
-       v = strtoul(arg, &endptr, base);
+       v = strtoul(arg, endp, base);
        if (v > UINT_MAX) return ret_ERANGE();
-       return handle_errors(v, endp, endptr);
+       return handle_errors(v, endp);
 }
 
 int FAST_FUNC bb_strtoi(const char *arg, char **endp, int base)
 {
        long v;
        char *endptr;
+       char first;
+
+       if (!endp) endp = &endptr;
+       *endp = (char*) arg;
 
-       char first = (arg[0] != '-' ? arg[0] : arg[1]);
+       first = (arg[0] != '-' ? arg[0] : arg[1]);
        if (!isalnum(first)) return ret_ERANGE();
 
        errno = 0;
-       v = strtol(arg, &endptr, base);
+       v = strtol(arg, endp, base);
        if (v > INT_MAX) return ret_ERANGE();
        if (v < INT_MIN) return ret_ERANGE();
-       return handle_errors(v, endp, endptr);
+       return handle_errors(v, endp);
 }
 #endif
index ed4bf6b..cb92bef 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) many different people.
  * If you wrote this, please acknowledge your work.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index 599449f..4b10cc1 100644 (file)
@@ -1,6 +1,6 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index fb53354..9ed2959 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) many different people.
  * If you wrote this, please acknowledge your work.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* Concatenate path and filename to new allocated buffer.
index 313fa63..c9167d4 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) (C) 2003  Vladimir Oleynik  <dzo@simtreas.ru>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*
index ed765d8..9333a8d 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
  * SELinux support by Yuichi Nakamura <ynakam@hitachisoft.jp>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
@@ -78,9 +78,9 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
        /* NB: each struct stat is ~100 bytes */
        struct stat source_stat;
        struct stat dest_stat;
-       signed char retval = 0;
-       signed char dest_exists = 0;
-       signed char ovr;
+       smallint retval = 0;
+       smallint dest_exists = 0;
+       smallint ovr;
 
 /* Inverse of cp -d ("cp without -d") */
 #define FLAGS_DEREF (flags & (FILEUTILS_DEREFERENCE + FILEUTILS_DEREFERENCE_L0))
@@ -147,7 +147,6 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
                        return -1;
                }
 
-               /* Create DEST */
                if (dest_exists) {
                        if (!S_ISDIR(dest_stat.st_mode)) {
                                bb_error_msg("target '%s' is not a directory", dest);
@@ -156,6 +155,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
                        /* race here: user can substitute a symlink between
                         * this check and actual creation of files inside dest */
                } else {
+                       /* Create DEST */
                        mode_t mode;
                        saved_umask = umask(0);
 
index 82622c0..eda2747 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index 6301589..acadf39 100644 (file)
 #include "libbb.h"
 
 /* Ask the user for a password.
+ * Return 1 without asking if PW has an empty password.
+ * Return -1 on EOF, error while reading input, or timeout.
  * Return 1 if the user gives the correct password for entry PW,
- * 0 if not.  Return 1 without asking if PW has an empty password.
+ * 0 if not.
  *
- * NULL pw means "just fake it for login with bad username" */
-
-int FAST_FUNC correct_password(const struct passwd *pw)
+ * NULL pw means "just fake it for login with bad username"
+ */
+int FAST_FUNC ask_and_check_password_extended(const struct passwd *pw,
+               int timeout, const char *prompt)
 {
        char *unencrypted, *encrypted;
        const char *correct;
        int r;
-#if ENABLE_FEATURE_SHADOWPASSWDS
-       /* Using _r function to avoid pulling in static buffers */
-       struct spwd spw;
-       char buffer[256];
-#endif
-
        /* fake salt. crypt() can choke otherwise. */
        correct = "aa";
        if (!pw) {
@@ -55,7 +52,10 @@ int FAST_FUNC correct_password(const struct passwd *pw)
        }
        correct = pw->pw_passwd;
 #if ENABLE_FEATURE_SHADOWPASSWDS
+       /* Using _r function to avoid pulling in static buffers */
        if ((correct[0] == 'x' || correct[0] == '*') && !correct[1]) {
+               struct spwd spw;
+               char buffer[256];
                /* getspnam_r may return 0 yet set result to NULL.
                 * At least glibc 2.4 does this. Be extra paranoid here. */
                struct spwd *result = NULL;
@@ -68,13 +68,19 @@ int FAST_FUNC correct_password(const struct passwd *pw)
                return 1;
 
  fake_it:
-       unencrypted = bb_ask_stdin("Password: ");
+       unencrypted = bb_ask(STDIN_FILENO, timeout, prompt);
        if (!unencrypted) {
-               return 0;
+               /* EOF (such as ^D) or error (such as ^C) or timeout */
+               return -1;
        }
        encrypted = pw_encrypt(unencrypted, correct, 1);
        r = (strcmp(encrypted, correct) == 0);
        free(encrypted);
-       memset(unencrypted, 0, strlen(unencrypted));
+       nuke_str(unencrypted);
        return r;
 }
+
+int FAST_FUNC ask_and_check_password(const struct passwd *pw)
+{
+       return ask_and_check_password_extended(pw, 0, "Password: ");
+}
index 36ac860..ac9836c 100644 (file)
  * endian = 1: big-endian
  * endian = 0: little-endian
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 
+uint32_t *global_crc32_table;
+
 uint32_t* FAST_FUNC crc32_filltable(uint32_t *crc_table, int endian)
 {
        uint32_t polynomial = endian ? 0x04c11db7 : 0xedb88320;
@@ -40,3 +42,25 @@ uint32_t* FAST_FUNC crc32_filltable(uint32_t *crc_table, int endian)
 
        return crc_table - 256;
 }
+
+uint32_t FAST_FUNC crc32_block_endian1(uint32_t val, const void *buf, unsigned len, uint32_t *crc_table)
+{
+       const void *end = (uint8_t*)buf + len;
+
+       while (buf != end) {
+               val = (val << 8) ^ crc_table[(val >> 24) ^ *(uint8_t*)buf];
+               buf = (uint8_t*)buf + 1;
+       }
+       return val;
+}
+
+uint32_t FAST_FUNC crc32_block_endian0(uint32_t val, const void *buf, unsigned len, uint32_t *crc_table)
+{
+       const void *end = (uint8_t*)buf + len;
+
+       while (buf != end) {
+               val = crc_table[(uint8_t)val ^ *(uint8_t*)buf] ^ (val >> 8);
+               buf = (uint8_t*)buf + 1;
+       }
+       return val;
+}
diff --git a/libbb/create_icmp6_socket.c b/libbb/create_icmp6_socket.c
deleted file mode 100644 (file)
index 91e478e..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * Utility routines.
- *
- * create raw socket for icmp (IPv6 version) protocol
- * and drop root privileges if running setuid
- *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
- */
-
-#include "libbb.h"
-
-#if ENABLE_FEATURE_IPV6
-int FAST_FUNC create_icmp6_socket(void)
-{
-       int sock;
-#if 0
-       struct protoent *proto;
-       proto = getprotobyname("ipv6-icmp");
-       /* if getprotobyname failed, just silently force
-        * proto->p_proto to have the correct value for "ipv6-icmp" */
-       sock = socket(AF_INET6, SOCK_RAW,
-                       (proto ? proto->p_proto : IPPROTO_ICMPV6));
-#else
-       sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
-#endif
-       if (sock < 0) {
-               if (errno == EPERM)
-                       bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
-               bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
-       }
-
-       /* drop root privs if running setuid */
-       xsetuid(getuid());
-
-       return sock;
-}
-#endif
diff --git a/libbb/create_icmp_socket.c b/libbb/create_icmp_socket.c
deleted file mode 100644 (file)
index d75f845..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * Utility routines.
- *
- * create raw socket for icmp protocol
- * and drop root privileges if running setuid
- *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
- */
-
-#include "libbb.h"
-
-int FAST_FUNC create_icmp_socket(void)
-{
-       int sock;
-#if 0
-       struct protoent *proto;
-       proto = getprotobyname("icmp");
-       /* if getprotobyname failed, just silently force
-        * proto->p_proto to have the correct value for "icmp" */
-       sock = socket(AF_INET, SOCK_RAW,
-                       (proto ? proto->p_proto : 1)); /* 1 == ICMP */
-#else
-       sock = socket(AF_INET, SOCK_RAW, 1); /* 1 == ICMP */
-#endif
-       if (sock < 0) {
-               if (errno == EPERM)
-                       bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
-               bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
-       }
-
-       /* drop root privs if running setuid */
-       xsetuid(getuid());
-
-       return sock;
-}
index 0b19f21..4f6395f 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* Seems silly to copyright a global variable.  ;-)  Oh well.
@@ -15,4 +15,4 @@
 
 #include "libbb.h"
 
-int xfunc_error_retval = EXIT_FAILURE;
+uint8_t xfunc_error_retval = EXIT_FAILURE;
index cf8bcf6..a8fe2fc 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index 8b4deec..cf1297b 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2008 Tito Ragusa <farmatito@tiscali.it>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 
 void FAST_FUNC die_if_bad_username(const char *name)
 {
-       /* 1st char being dash or dot isn't valid: */
-       goto skip;
-       /* For example, name like ".." can make adduser
-        * chown "/home/.." recursively - NOT GOOD
+       const char *start = name;
+
+       /* 1st char being dash or dot isn't valid:
+        * for example, name like ".." can make adduser
+        * chown "/home/.." recursively - NOT GOOD.
+        * Name of just a single "$" is also rejected.
         */
+       goto skip;
 
        do {
-               if (*name == '-' || *name == '.')
+               unsigned char ch;
+
+               /* These chars are valid unless they are at the 1st pos: */
+               if (*name == '-'
+                || *name == '.'
+               /* $ is allowed if it's the last char: */
+                || (*name == '$' && !name[1])
+               ) {
                        continue;
+               }
  skip:
-               if (isalnum(*name)
-                || *name == '_'
-                || *name == '@'
-                || (*name == '$' && !name[1])
+               ch = *name;
+               if (ch == '_'
+               /* || ch == '@' -- we disallow this too. Think about "user@host" */
+               /* open-coded isalnum: */
+                || (ch >= '0' && ch <= '9')
+                || ((ch|0x20) >= 'a' && (ch|0x20) <= 'z')
                ) {
                        continue;
                }
-               bb_error_msg_and_die("illegal character '%c'", *name);
+               bb_error_msg_and_die("illegal character with code %u at position %u",
+                               (unsigned)ch, (unsigned)(name - start));
        } while (*++name);
+
+       /* The minimum size of the login name is one char or two if
+        * last char is the '$'. Violations of this are caught above.
+        * The maximum size of the login name is LOGIN_NAME_MAX
+        * including the terminating null byte.
+        */
+       if (name - start >= LOGIN_NAME_MAX)
+               bb_error_msg_and_die("name is too long");
 }
index 7a87219..566881a 100644 (file)
@@ -4,9 +4,9 @@
  * based on code from util-linux v 2.11l
  *
  * Copyright (c) 1989
- *     The Regents of the University of California.  All rights reserved.
+ * The Regents of the University of California.  All rights reserved.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Original copyright notice is retained at the end of this file.
  */
@@ -71,7 +71,8 @@ static NOINLINE int bb_dump_size(FS *fs)
                         * skip any special chars -- save precision in
                         * case it's a %s format.
                         */
-                       while (strchr(index_str + 1, *++fmt));
+                       while (strchr(index_str + 1, *++fmt))
+                               continue;
                        if (*fmt == '.' && isdigit(*++fmt)) {
                                prec = atoi(fmt);
                                while (isdigit(*++fmt))
@@ -99,8 +100,8 @@ static NOINLINE int bb_dump_size(FS *fs)
 static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
 {
        enum { NOTOKAY, USEBCNT, USEPREC } sokay;
-       PR *pr, **nextpr = NULL;
        FU *fu;
+       PR *pr;
        char *p1, *p2, *p3;
        char savech, *fmtp;
        const char *byte_count_str;
@@ -111,15 +112,12 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
                 * break each format unit into print units; each
                 * conversion character gets its own.
                 */
-               for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
+               for (nconv = 0, fmtp = fu->fmt; *fmtp; ) {
                        /* NOSTRICT */
                        /* DBU:[dvae@cray.com] zalloc so that forward ptrs start out NULL*/
                        pr = xzalloc(sizeof(PR));
                        if (!fu->nextpr)
                                fu->nextpr = pr;
-                       /* ignore nextpr -- its unused inside the loop and is
-                        * uninitialized 1st time through.
-                        */
 
                        /* skip preceding text and up to the next % sign */
                        for (p1 = fmtp; *p1 && *p1 != '%'; ++p1)
@@ -208,7 +206,7 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
                                        pr->bcnt = fu->bcnt;
                                } else if (sokay == USEPREC) {
                                        pr->bcnt = prec;
-                               } else {        /* NOTOKAY */
+                               } else {   /* NOTOKAY */
                                        bb_error_msg_and_die("%%s requires a precision or a byte count");
                                }
                        } else if (*p1 == '_') {
@@ -295,16 +293,18 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
         * interprets any data at all, and has no iteration count,
         * repeat it as necessary.
         *
-        * if, rep count is greater than 1, no trailing whitespace
+        * if rep count is greater than 1, no trailing whitespace
         * gets output from the last iteration of the format unit.
         */
        for (fu = fs->nextfu; fu; fu = fu->nextfu) {
-               if (!fu->nextfu && fs->bcnt < dumper->blocksize
-                && !(fu->flags & F_SETREP) && fu->bcnt
+               if (!fu->nextfu
+                && fs->bcnt < dumper->blocksize
+                && !(fu->flags & F_SETREP)
+                && fu->bcnt
                ) {
                        fu->reps += (dumper->blocksize - fs->bcnt) / fu->bcnt;
                }
-               if (fu->reps > 1) {
+               if (fu->reps > 1 && fu->nextpr) {
                        for (pr = fu->nextpr;; pr = pr->nextpr)
                                if (!pr->nextpr)
                                        break;
@@ -323,9 +323,7 @@ static void do_skip(priv_dumper_t *dumper, const char *fname, int statok)
        struct stat sbuf;
 
        if (statok) {
-               if (fstat(STDIN_FILENO, &sbuf)) {
-                       bb_simple_perror_msg_and_die(fname);
-               }
+               xfstat(STDIN_FILENO, &sbuf, fname);
                if (!(S_ISCHR(sbuf.st_mode) || S_ISBLK(sbuf.st_mode) || S_ISFIFO(sbuf.st_mode))
                 && dumper->pub.dump_skip >= sbuf.st_size
                ) {
@@ -335,7 +333,7 @@ static void do_skip(priv_dumper_t *dumper, const char *fname, int statok)
                        return;
                }
        }
-       if (fseek(stdin, dumper->pub.dump_skip, SEEK_SET)) {
+       if (fseeko(stdin, dumper->pub.dump_skip, SEEK_SET)) {
                bb_simple_perror_msg_and_die(fname);
        }
        dumper->address += dumper->pub.dump_skip;
@@ -469,7 +467,7 @@ static void bpad(PR *pr)
 
 static const char conv_str[] ALIGN1 =
        "\0\\0\0"
-       "\007\\a\0"                             /* \a */
+       "\007\\a\0"  /* \a */
        "\b\\b\0"
        "\f\\b\0"
        "\n\\n\0"
@@ -726,7 +724,7 @@ void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
        p = fmt;
        for (;;) {
                p = skip_whitespace(p);
-               if (!*p) {
+               if (*p == '\0') {
                        break;
                }
 
@@ -754,7 +752,7 @@ void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
 
                /* skip slash and trailing white space */
                if (*p == '/') {
-                       p = skip_whitespace(++p);
+                       p = skip_whitespace(p + 1);
                }
 
                /* byte count */
@@ -768,7 +766,7 @@ void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
                        }
                        tfu->bcnt = atoi(savep);
                        /* skip trailing white space */
-                       p = skip_whitespace(++p);
+                       p = skip_whitespace(p + 1);
                }
 
                /* format */
@@ -776,7 +774,7 @@ void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
                        bb_error_msg_and_die("bad format {%s}", fmt);
                }
                for (savep = ++p; *p != '"';) {
-                       if (*p++ == 0) {
+                       if (*p++ == '\0') {
                                bb_error_msg_and_die("bad format {%s}", fmt);
                        }
                }
@@ -787,7 +785,7 @@ void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
 
                /* alphabetic escape sequences have to be done in place */
                for (p2 = p1;; ++p1, ++p2) {
-                       if (!*p1) {
+                       if (*p1 == '\0') {
                                *p2 = *p1;
                                break;
                        }
diff --git a/libbb/endofname.c b/libbb/endofname.c
new file mode 100644 (file)
index 0000000..305f093
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2013 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//kbuild:lib-y += endofname.o
+
+#include "libbb.h"
+
+#define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
+#define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
+
+const char* FAST_FUNC
+endofname(const char *name)
+{
+       if (!is_name(*name))
+               return name;
+       while (*++name) {
+               if (!is_in_name(*name))
+                       break;
+       }
+       return name;
+}
index 82241cd..178a00a 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2006 Gabriel Somlo <somlo at cmu.edu>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -68,12 +68,12 @@ int FAST_FUNC exists_execable(const char *filename)
 }
 
 #if ENABLE_FEATURE_PREFER_APPLETS
-/* just like the real execvp, but try to launch an applet named 'file' first
- */
-int FAST_FUNC bb_execvp(const char *file, char *const argv[])
+/* just like the real execvp, but try to launch an applet named 'file' first */
+int FAST_FUNC BB_EXECVP(const char *file, char *const argv[])
 {
-       return execvp(find_applet_by_name(file) >= 0 ? bb_busybox_exec_path : file,
-                                       argv);
+       if (find_applet_by_name(file) >= 0)
+               execvp(bb_busybox_exec_path, argv);
+       return execvp(file, argv);
 }
 #endif
 
index 6f3f373..1b14413 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* A number of standard utilities can accept multiple command line args
@@ -18,7 +18,8 @@ int FAST_FUNC fclose_if_not_stdin(FILE *f)
 {
        /* Some more paranoid applets want ferror() check too */
        int r = ferror(f); /* NB: does NOT set errno! */
-       if (r) errno = EIO; /* so we'll help it */
+       if (r)
+               errno = EIO; /* so we'll help it */
        if (f != stdin)
                return (r | fclose(f)); /* fclose does set errno on error */
        return r;
index 742fb9f..9ad5dbf 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* Attempt to fflush(stdout), and exit with an error code if stdout is
index 3fe61cd..89210a3 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) many different people.
  * If you wrote this, please acknowledge your work.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index bcb18ef..9676b5f 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -30,7 +30,8 @@ struct mntent* FAST_FUNC find_mount_point(const char *name, int subdir_too)
 
        devno_of_name = s.st_dev;
        block_dev = 0;
-       if (S_ISBLK(s.st_mode)) {
+       /* Why S_ISCHR? - UBI volumes use char devices, not block */
+       if (S_ISBLK(s.st_mode) || S_ISCHR(s.st_mode)) {
                devno_of_name = s.st_rdev;
                block_dev = 1;
        }
@@ -43,7 +44,7 @@ struct mntent* FAST_FUNC find_mount_point(const char *name, int subdir_too)
                /* rootfs mount in Linux 2.6 exists always,
                 * and it makes sense to always ignore it.
                 * Otherwise people can't reference their "real" root! */
-               if (strcmp(mountEntry->mnt_fsname, "rootfs") == 0)
+               if (ENABLE_FEATURE_SKIP_ROOTFS && strcmp(mountEntry->mnt_fsname, "rootfs") == 0)
                        continue;
 
                if (strcmp(name, mountEntry->mnt_dir) == 0
index 52a0c6d..db823d0 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index ca46bf5..8436cd6 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -29,14 +29,15 @@ static char *find_block_device_in_dir(struct arena *ap)
        char *retpath = NULL;
        int len, rem;
 
-       dir = opendir(ap->devpath);
-       if (!dir)
-               return NULL;
-
        len = strlen(ap->devpath);
        rem = DEVNAME_MAX-2 - len;
        if (rem <= 0)
                return NULL;
+
+       dir = opendir(ap->devpath);
+       if (!dir)
+               return NULL;
+
        ap->devpath[len++] = '/';
 
        while ((entry = readdir(dir)) != NULL) {
index f353b7d..777fbd9 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -30,7 +30,7 @@ ssize_t FAST_FUNC full_write(int fd, const void *buf, size_t len)
                                /* user can do another write to know the error code */
                                return total;
                        }
-                       return cc;      /* write() returns -1 on failure. */
+                       return cc;  /* write() returns -1 on failure. */
                }
 
                total += cc;
index 74022b5..9b6407b 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) many different people.  If you wrote this, please
  * acknowledge your work.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
diff --git a/libbb/get_cpu_count.c b/libbb/get_cpu_count.c
new file mode 100644 (file)
index 0000000..ab468af
--- /dev/null
@@ -0,0 +1,47 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Factored out of mpstat/iostat.
+ *
+ * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+
+/* Does str start with "cpu"? */
+int FAST_FUNC starts_with_cpu(const char *str)
+{
+       return ((str[0] - 'c') | (str[1] - 'p') | (str[2] - 'u')) == 0;
+}
+
+/*
+ * Get number of processors. Uses /proc/stat.
+ * Return value 0 means one CPU and non SMP kernel.
+ * Otherwise N means N processor(s) and SMP kernel.
+ */
+unsigned FAST_FUNC get_cpu_count(void)
+{
+       FILE *fp;
+       char line[256];
+       int proc_nr = -1;
+
+       fp = xfopen_for_read("/proc/stat");
+       while (fgets(line, sizeof(line), fp)) {
+               if (!starts_with_cpu(line)) {
+                       if (proc_nr >= 0)
+                               break; /* we are past "cpuN..." lines */
+                       continue;
+               }
+               if (line[3] != ' ') { /* "cpuN" */
+                       int num_proc;
+                       if (sscanf(line + 3, "%u", &num_proc) == 1
+                        && num_proc > proc_nr
+                       ) {
+                               proc_nr = num_proc;
+                       }
+               }
+       }
+
+       fclose(fp);
+       return proc_nr + 1;
+}
index 7c99116..04fdf2a 100644 (file)
@@ -4,10 +4,18 @@
  *
  * Copyright (C) 2001  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
-
 #include "libbb.h"
+
+const char* FAST_FUNC bb_basename(const char *name)
+{
+       const char *cp = strrchr(name, '/');
+       if (cp)
+               return cp + 1;
+       return name;
+}
+
 /*
  * "/"        -> "/"
  * "abc"      -> "abc"
index cbfb45b..a98dd35 100644 (file)
@@ -6,43 +6,26 @@
  * Copyright (C) 2004 Erik Andersen <andersen@codepoet.org>
  * Copyright (C) 2001 Matt Krai
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 
-/* This function reads an entire line from a text file, up to a newline
- * or NUL byte, inclusive.  It returns a malloc'ed char * which
- * must be free'ed by the caller.  If end is NULL '\n' isn't considered
- * end of line.  If end isn't NULL, length of the chunk is stored in it.
- * If lineno is not NULL, *lineno is incremented for each line,
- * and also trailing '\' is recognized as line continuation.
- *
- * Returns NULL if EOF/error. */
-char* FAST_FUNC bb_get_chunk_with_continuation(FILE *file, int *end, int *lineno)
+char* FAST_FUNC bb_get_chunk_from_file(FILE *file, int *end)
 {
        int ch;
-       int idx = 0;
+       unsigned idx = 0;
        char *linebuf = NULL;
-       int linebufsz = 0;
 
        while ((ch = getc(file)) != EOF) {
                /* grow the line buffer as necessary */
-               if (idx >= linebufsz) {
-                       linebufsz += 256;
-                       linebuf = xrealloc(linebuf, linebufsz);
-               }
+               if (!(idx & 0xff))
+                       linebuf = xrealloc(linebuf, idx + 0x100);
                linebuf[idx++] = (char) ch;
-               if (!ch)
+               if (ch == '\0')
+                       break;
+               if (end && ch == '\n')
                        break;
-               if (end && ch == '\n') {
-                       if (lineno == NULL)
-                               break;
-                       (*lineno)++;
-                       if (idx < 2 || linebuf[idx-2] != '\\')
-                               break;
-                       idx -= 2;
-               }
        }
        if (end)
                *end = idx;
@@ -59,11 +42,6 @@ char* FAST_FUNC bb_get_chunk_with_continuation(FILE *file, int *end, int *lineno
        return linebuf;
 }
 
-char* FAST_FUNC bb_get_chunk_from_file(FILE *file, int *end)
-{
-       return bb_get_chunk_with_continuation(file, end, NULL);
-}
-
 /* Get line, including trailing \n if any */
 char* FAST_FUNC xmalloc_fgets(FILE *file)
 {
diff --git a/libbb/get_shell_name.c b/libbb/get_shell_name.c
new file mode 100644 (file)
index 0000000..5aebe9c
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011, Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//kbuild:lib-y += get_shell_name.o
+
+#include "libbb.h"
+
+const char* FAST_FUNC get_shell_name(void)
+{
+       struct passwd *pw;
+       char *shell;
+
+       shell = getenv("SHELL");
+       if (shell && shell[0])
+               return shell;
+
+       pw = getpwuid(getuid());
+       if (pw && pw->pw_shell && pw->pw_shell[0])
+               return pw->pw_shell;
+
+       return DEFAULT_SHELL;
+}
index 5b02709..241ceda 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2010 Denys Vlasenko
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
index b5f83c1..d0e83d8 100644 (file)
@@ -4,10 +4,12 @@
  *
  * Copyright (C) 2003-2005  Vladimir Oleynik  <dzo@simtreas.ru>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
-#include <getopt.h>
+#if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG
+# include <getopt.h>
+#endif
 #include "libbb.h"
 
 /*      Documentation
@@ -80,9 +82,9 @@ const char *applet_long_options
         This struct allows you to define long options:
 
         static const char applet_longopts[] ALIGN1 =
-               //"name\0" has_arg val
-               "verbose\0" No_argument "v"
-               ;
+                //"name\0" has_arg val
+                "verbose\0" No_argument "v"
+                ;
         applet_long_options = applet_longopts;
 
         The last member of struct option (val) typically is set to
@@ -115,7 +117,7 @@ const char *opt_complementary
         found.
 
  "ww"   Adjacent double options have a counter associated which indicates
-        the number of occurences of the option.
+        the number of occurrences of the option.
         For example the ps applet needs:
         if w is given once, GNU ps sets the width to 132,
         if w is given more than once, it is "unlimited"
@@ -226,14 +228,14 @@ Special characters:
         if specified together.  In this case you must set
         opt_complementary = "b--cf:c--bf:f--bc".  If two of the
         mutually exclusive options are found, getopt32 will call
-       bb_show_usage() and die.
+        bb_show_usage() and die.
 
  "x--x" Variation of the above, it means that -x option should occur
         at most once.
 
  "a+"   A plus after a char in opt_complementary means that the parameter
         for this option is a nonnegative integer. It will be processed
-        with xatoi_u() - allowed range is 0..INT_MAX.
+        with xatoi_positive() - allowed range is 0..INT_MAX.
 
         int param;  // "unsigned param;" will also work
         opt_complementary = "p+";
@@ -465,13 +467,17 @@ getopt32(char **argv, const char *applet_opts, ...)
                }
                for (on_off = complementary; on_off->opt_char; on_off++)
                        if (on_off->opt_char == *s)
-                               break;
+                               goto found_opt;
+               /* Without this, diagnostic of such bugs is not easy */
+               bb_error_msg_and_die("NO OPT %c!", *s);
+ found_opt:
                if (c == ':' && s[2] == ':') {
                        on_off->param_type = PARAM_LIST;
                        continue;
                }
                if (c == '+' && (s[2] == ':' || s[2] == '\0')) {
                        on_off->param_type = PARAM_INT;
+                       s++;
                        continue;
                }
                if (c == ':' || c == '\0') {
@@ -531,7 +537,7 @@ getopt32(char **argv, const char *applet_opts, ...)
 
        /* In case getopt32 was already called:
         * reset the libc getopt() function, which keeps internal state.
-        * run_nofork_applet_prime() does this, but we might end up here
+        * run_nofork_applet() does this, but we might end up here
         * also via gunzip_main() -> gzip_main(). Play safe.
         */
 #ifdef __GLIBC__
@@ -542,8 +548,6 @@ getopt32(char **argv, const char *applet_opts, ...)
 #endif
        /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */
 
-       pargv = NULL;
-
        /* Note: just "getopt() <= 0" will not work well for
         * "fake" short options, like this one:
         * wget $'-\203' "Test: test" http://kernel.org/
@@ -574,19 +578,16 @@ getopt32(char **argv, const char *applet_opts, ...)
                flags ^= trigger;
                if (on_off->counter)
                        (*(on_off->counter))++;
-               if (on_off->param_type == PARAM_LIST) {
-                       if (optarg)
+               if (optarg) {
+                       if (on_off->param_type == PARAM_LIST) {
                                llist_add_to_end((llist_t **)(on_off->optarg), optarg);
-               } else if (on_off->param_type == PARAM_INT) {
-                       if (optarg)
-//TODO: xatoi_u indirectly pulls in printf machinery
-                               *(unsigned*)(on_off->optarg) = xatoi_u(optarg);
-               } else if (on_off->optarg) {
-                       if (optarg)
+                       } else if (on_off->param_type == PARAM_INT) {
+//TODO: xatoi_positive indirectly pulls in printf machinery
+                               *(unsigned*)(on_off->optarg) = xatoi_positive(optarg);
+                       } else if (on_off->optarg) {
                                *(char **)(on_off->optarg) = optarg;
+                       }
                }
-               if (pargv != NULL)
-                       break;
        }
 
        /* check depending requires for given options */
index 4bffd9a..435e4d0 100644 (file)
@@ -3,7 +3,7 @@
  * Mini getpty implementation for busybox
  * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -19,20 +19,22 @@ int FAST_FUNC xgetpty(char *line)
        if (p > 0) {
                grantpt(p); /* chmod+chown corresponding slave pty */
                unlockpt(p); /* (what does this do?) */
-#if 0 /* if ptsname_r is not available... */
-               const char *name;
-               name = ptsname(p); /* find out the name of slave pty */
-               if (!name) {
-                       bb_perror_msg_and_die("ptsname error (is /dev/pts mounted?)");
+# ifndef HAVE_PTSNAME_R
+               {
+                       const char *name;
+                       name = ptsname(p); /* find out the name of slave pty */
+                       if (!name) {
+                               bb_perror_msg_and_die("ptsname error (is /dev/pts mounted?)");
+                       }
+                       safe_strncpy(line, name, GETPTY_BUFSIZE);
                }
-               safe_strncpy(line, name, GETPTY_BUFSIZE);
-#else
+# else
                /* find out the name of slave pty */
                if (ptsname_r(p, line, GETPTY_BUFSIZE-1) != 0) {
                        bb_perror_msg_and_die("ptsname error (is /dev/pts mounted?)");
                }
                line[GETPTY_BUFSIZE-1] = '\0';
-#endif
+# endif
                return p;
        }
 #else
diff --git a/libbb/hash_md5_sha.c b/libbb/hash_md5_sha.c
new file mode 100644 (file)
index 0000000..3f743ac
--- /dev/null
@@ -0,0 +1,1168 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2010 Denys Vlasenko
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* gcc 4.2.1 optimizes rotr64 better with inline than with macro
+ * (for rotX32, there is no difference). Why? My guess is that
+ * macro requires clever common subexpression elimination heuristics
+ * in gcc, while inline basically forces it to happen.
+ */
+//#define rotl32(x,n) (((x) << (n)) | ((x) >> (32 - (n))))
+static ALWAYS_INLINE uint32_t rotl32(uint32_t x, unsigned n)
+{
+       return (x << n) | (x >> (32 - n));
+}
+//#define rotr32(x,n) (((x) >> (n)) | ((x) << (32 - (n))))
+static ALWAYS_INLINE uint32_t rotr32(uint32_t x, unsigned n)
+{
+       return (x >> n) | (x << (32 - n));
+}
+/* rotr64 in needed for sha512 only: */
+//#define rotr64(x,n) (((x) >> (n)) | ((x) << (64 - (n))))
+static ALWAYS_INLINE uint64_t rotr64(uint64_t x, unsigned n)
+{
+       return (x >> n) | (x << (64 - n));
+}
+
+/* rotl64 only used for sha3 currently */
+static ALWAYS_INLINE uint64_t rotl64(uint64_t x, unsigned n)
+{
+       return (x << n) | (x >> (64 - n));
+}
+
+/* Feed data through a temporary buffer.
+ * The internal buffer remembers previous data until it has 64
+ * bytes worth to pass on.
+ */
+static void FAST_FUNC common64_hash(md5_ctx_t *ctx, const void *buffer, size_t len)
+{
+       unsigned bufpos = ctx->total64 & 63;
+
+       ctx->total64 += len;
+
+       while (1) {
+               unsigned remaining = 64 - bufpos;
+               if (remaining > len)
+                       remaining = len;
+               /* Copy data into aligned buffer */
+               memcpy(ctx->wbuffer + bufpos, buffer, remaining);
+               len -= remaining;
+               buffer = (const char *)buffer + remaining;
+               bufpos += remaining;
+               /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */
+               bufpos -= 64;
+               if (bufpos != 0)
+                       break;
+               /* Buffer is filled up, process it */
+               ctx->process_block(ctx);
+               /*bufpos = 0; - already is */
+       }
+}
+
+/* Process the remaining bytes in the buffer */
+static void FAST_FUNC common64_end(md5_ctx_t *ctx, int swap_needed)
+{
+       unsigned bufpos = ctx->total64 & 63;
+       /* Pad the buffer to the next 64-byte boundary with 0x80,0,0,0... */
+       ctx->wbuffer[bufpos++] = 0x80;
+
+       /* This loop iterates either once or twice, no more, no less */
+       while (1) {
+               unsigned remaining = 64 - bufpos;
+               memset(ctx->wbuffer + bufpos, 0, remaining);
+               /* Do we have enough space for the length count? */
+               if (remaining >= 8) {
+                       /* Store the 64-bit counter of bits in the buffer */
+                       uint64_t t = ctx->total64 << 3;
+                       if (swap_needed)
+                               t = bb_bswap_64(t);
+                       /* wbuffer is suitably aligned for this */
+                       *(bb__aliased_uint64_t *) (&ctx->wbuffer[64 - 8]) = t;
+               }
+               ctx->process_block(ctx);
+               if (remaining >= 8)
+                       break;
+               bufpos = 0;
+       }
+}
+
+
+/*
+ * Compute MD5 checksum of strings according to the
+ * definition of MD5 in RFC 1321 from April 1992.
+ *
+ * Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+ *
+ * Copyright (C) 1995-1999 Free Software Foundation, Inc.
+ * Copyright (C) 2001 Manuel Novoa III
+ * Copyright (C) 2003 Glenn L. McGrath
+ * Copyright (C) 2003 Erik Andersen
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* 0: fastest, 3: smallest */
+#if CONFIG_MD5_SMALL < 0
+# define MD5_SMALL 0
+#elif CONFIG_MD5_SMALL > 3
+# define MD5_SMALL 3
+#else
+# define MD5_SMALL CONFIG_MD5_SMALL
+#endif
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+ * and defined in the RFC 1321.  The first function is a little bit optimized
+ * (as found in Colin Plumbs public domain implementation).
+ * #define FF(b, c, d) ((b & c) | (~b & d))
+ */
+#undef FF
+#undef FG
+#undef FH
+#undef FI
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF(d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+
+/* Hash a single block, 64 bytes long and 4-byte aligned */
+static void FAST_FUNC md5_process_block64(md5_ctx_t *ctx)
+{
+#if MD5_SMALL > 0
+       /* Before we start, one word to the strange constants.
+          They are defined in RFC 1321 as
+          T[i] = (int)(4294967296.0 * fabs(sin(i))), i=1..64
+        */
+       static const uint32_t C_array[] = {
+               /* round 1 */
+               0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+               0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+               0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+               0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+               /* round 2 */
+               0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+               0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+               0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+               0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+               /* round 3 */
+               0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+               0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+               0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
+               0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+               /* round 4 */
+               0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+               0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+               0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+               0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
+       };
+       static const char P_array[] ALIGN1 = {
+# if MD5_SMALL > 1
+               0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */
+# endif
+               1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */
+               5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */
+               0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9  /* 4 */
+       };
+#endif
+       uint32_t *words = (void*) ctx->wbuffer;
+       uint32_t A = ctx->hash[0];
+       uint32_t B = ctx->hash[1];
+       uint32_t C = ctx->hash[2];
+       uint32_t D = ctx->hash[3];
+
+#if MD5_SMALL >= 2  /* 2 or 3 */
+
+       static const char S_array[] ALIGN1 = {
+               7, 12, 17, 22,
+               5, 9, 14, 20,
+               4, 11, 16, 23,
+               6, 10, 15, 21
+       };
+       const uint32_t *pc;
+       const char *pp;
+       const char *ps;
+       int i;
+       uint32_t temp;
+
+       if (BB_BIG_ENDIAN)
+               for (i = 0; i < 16; i++)
+                       words[i] = SWAP_LE32(words[i]);
+
+# if MD5_SMALL == 3
+       pc = C_array;
+       pp = P_array;
+       ps = S_array - 4;
+
+       for (i = 0; i < 64; i++) {
+               if ((i & 0x0f) == 0)
+                       ps += 4;
+               temp = A;
+               switch (i >> 4) {
+               case 0:
+                       temp += FF(B, C, D);
+                       break;
+               case 1:
+                       temp += FG(B, C, D);
+                       break;
+               case 2:
+                       temp += FH(B, C, D);
+                       break;
+               case 3:
+                       temp += FI(B, C, D);
+               }
+               temp += words[(int) (*pp++)] + *pc++;
+               temp = rotl32(temp, ps[i & 3]);
+               temp += B;
+               A = D;
+               D = C;
+               C = B;
+               B = temp;
+       }
+# else  /* MD5_SMALL == 2 */
+       pc = C_array;
+       pp = P_array;
+       ps = S_array;
+
+       for (i = 0; i < 16; i++) {
+               temp = A + FF(B, C, D) + words[(int) (*pp++)] + *pc++;
+               temp = rotl32(temp, ps[i & 3]);
+               temp += B;
+               A = D;
+               D = C;
+               C = B;
+               B = temp;
+       }
+       ps += 4;
+       for (i = 0; i < 16; i++) {
+               temp = A + FG(B, C, D) + words[(int) (*pp++)] + *pc++;
+               temp = rotl32(temp, ps[i & 3]);
+               temp += B;
+               A = D;
+               D = C;
+               C = B;
+               B = temp;
+       }
+       ps += 4;
+       for (i = 0; i < 16; i++) {
+               temp = A + FH(B, C, D) + words[(int) (*pp++)] + *pc++;
+               temp = rotl32(temp, ps[i & 3]);
+               temp += B;
+               A = D;
+               D = C;
+               C = B;
+               B = temp;
+       }
+       ps += 4;
+       for (i = 0; i < 16; i++) {
+               temp = A + FI(B, C, D) + words[(int) (*pp++)] + *pc++;
+               temp = rotl32(temp, ps[i & 3]);
+               temp += B;
+               A = D;
+               D = C;
+               C = B;
+               B = temp;
+       }
+# endif
+       /* Add checksum to the starting values */
+       ctx->hash[0] += A;
+       ctx->hash[1] += B;
+       ctx->hash[2] += C;
+       ctx->hash[3] += D;
+
+#else  /* MD5_SMALL == 0 or 1 */
+
+       uint32_t A_save = A;
+       uint32_t B_save = B;
+       uint32_t C_save = C;
+       uint32_t D_save = D;
+# if MD5_SMALL == 1
+       const uint32_t *pc;
+       const char *pp;
+       int i;
+# endif
+
+       /* First round: using the given function, the context and a constant
+          the next context is computed.  Because the algorithm's processing
+          unit is a 32-bit word and it is determined to work on words in
+          little endian byte order we perhaps have to change the byte order
+          before the computation.  To reduce the work for the next steps
+          we save swapped words in WORDS array.  */
+# undef OP
+# define OP(a, b, c, d, s, T) \
+       do { \
+               a += FF(b, c, d) + (*words IF_BIG_ENDIAN(= SWAP_LE32(*words))) + T; \
+               words++; \
+               a = rotl32(a, s); \
+               a += b; \
+       } while (0)
+
+       /* Round 1 */
+# if MD5_SMALL == 1
+       pc = C_array;
+       for (i = 0; i < 4; i++) {
+               OP(A, B, C, D, 7, *pc++);
+               OP(D, A, B, C, 12, *pc++);
+               OP(C, D, A, B, 17, *pc++);
+               OP(B, C, D, A, 22, *pc++);
+       }
+# else
+       OP(A, B, C, D, 7, 0xd76aa478);
+       OP(D, A, B, C, 12, 0xe8c7b756);
+       OP(C, D, A, B, 17, 0x242070db);
+       OP(B, C, D, A, 22, 0xc1bdceee);
+       OP(A, B, C, D, 7, 0xf57c0faf);
+       OP(D, A, B, C, 12, 0x4787c62a);
+       OP(C, D, A, B, 17, 0xa8304613);
+       OP(B, C, D, A, 22, 0xfd469501);
+       OP(A, B, C, D, 7, 0x698098d8);
+       OP(D, A, B, C, 12, 0x8b44f7af);
+       OP(C, D, A, B, 17, 0xffff5bb1);
+       OP(B, C, D, A, 22, 0x895cd7be);
+       OP(A, B, C, D, 7, 0x6b901122);
+       OP(D, A, B, C, 12, 0xfd987193);
+       OP(C, D, A, B, 17, 0xa679438e);
+       OP(B, C, D, A, 22, 0x49b40821);
+# endif
+       words -= 16;
+
+       /* For the second to fourth round we have the possibly swapped words
+          in WORDS.  Redefine the macro to take an additional first
+          argument specifying the function to use.  */
+# undef OP
+# define OP(f, a, b, c, d, k, s, T) \
+       do { \
+               a += f(b, c, d) + words[k] + T; \
+               a = rotl32(a, s); \
+               a += b; \
+       } while (0)
+
+       /* Round 2 */
+# if MD5_SMALL == 1
+       pp = P_array;
+       for (i = 0; i < 4; i++) {
+               OP(FG, A, B, C, D, (int) (*pp++), 5, *pc++);
+               OP(FG, D, A, B, C, (int) (*pp++), 9, *pc++);
+               OP(FG, C, D, A, B, (int) (*pp++), 14, *pc++);
+               OP(FG, B, C, D, A, (int) (*pp++), 20, *pc++);
+       }
+# else
+       OP(FG, A, B, C, D, 1, 5, 0xf61e2562);
+       OP(FG, D, A, B, C, 6, 9, 0xc040b340);
+       OP(FG, C, D, A, B, 11, 14, 0x265e5a51);
+       OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
+       OP(FG, A, B, C, D, 5, 5, 0xd62f105d);
+       OP(FG, D, A, B, C, 10, 9, 0x02441453);
+       OP(FG, C, D, A, B, 15, 14, 0xd8a1e681);
+       OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
+       OP(FG, A, B, C, D, 9, 5, 0x21e1cde6);
+       OP(FG, D, A, B, C, 14, 9, 0xc33707d6);
+       OP(FG, C, D, A, B, 3, 14, 0xf4d50d87);
+       OP(FG, B, C, D, A, 8, 20, 0x455a14ed);
+       OP(FG, A, B, C, D, 13, 5, 0xa9e3e905);
+       OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8);
+       OP(FG, C, D, A, B, 7, 14, 0x676f02d9);
+       OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+# endif
+
+       /* Round 3 */
+# if MD5_SMALL == 1
+       for (i = 0; i < 4; i++) {
+               OP(FH, A, B, C, D, (int) (*pp++), 4, *pc++);
+               OP(FH, D, A, B, C, (int) (*pp++), 11, *pc++);
+               OP(FH, C, D, A, B, (int) (*pp++), 16, *pc++);
+               OP(FH, B, C, D, A, (int) (*pp++), 23, *pc++);
+       }
+# else
+       OP(FH, A, B, C, D, 5, 4, 0xfffa3942);
+       OP(FH, D, A, B, C, 8, 11, 0x8771f681);
+       OP(FH, C, D, A, B, 11, 16, 0x6d9d6122);
+       OP(FH, B, C, D, A, 14, 23, 0xfde5380c);
+       OP(FH, A, B, C, D, 1, 4, 0xa4beea44);
+       OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9);
+       OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60);
+       OP(FH, B, C, D, A, 10, 23, 0xbebfbc70);
+       OP(FH, A, B, C, D, 13, 4, 0x289b7ec6);
+       OP(FH, D, A, B, C, 0, 11, 0xeaa127fa);
+       OP(FH, C, D, A, B, 3, 16, 0xd4ef3085);
+       OP(FH, B, C, D, A, 6, 23, 0x04881d05);
+       OP(FH, A, B, C, D, 9, 4, 0xd9d4d039);
+       OP(FH, D, A, B, C, 12, 11, 0xe6db99e5);
+       OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+       OP(FH, B, C, D, A, 2, 23, 0xc4ac5665);
+# endif
+
+       /* Round 4 */
+# if MD5_SMALL == 1
+       for (i = 0; i < 4; i++) {
+               OP(FI, A, B, C, D, (int) (*pp++), 6, *pc++);
+               OP(FI, D, A, B, C, (int) (*pp++), 10, *pc++);
+               OP(FI, C, D, A, B, (int) (*pp++), 15, *pc++);
+               OP(FI, B, C, D, A, (int) (*pp++), 21, *pc++);
+       }
+# else
+       OP(FI, A, B, C, D, 0, 6, 0xf4292244);
+       OP(FI, D, A, B, C, 7, 10, 0x432aff97);
+       OP(FI, C, D, A, B, 14, 15, 0xab9423a7);
+       OP(FI, B, C, D, A, 5, 21, 0xfc93a039);
+       OP(FI, A, B, C, D, 12, 6, 0x655b59c3);
+       OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92);
+       OP(FI, C, D, A, B, 10, 15, 0xffeff47d);
+       OP(FI, B, C, D, A, 1, 21, 0x85845dd1);
+       OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f);
+       OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+       OP(FI, C, D, A, B, 6, 15, 0xa3014314);
+       OP(FI, B, C, D, A, 13, 21, 0x4e0811a1);
+       OP(FI, A, B, C, D, 4, 6, 0xf7537e82);
+       OP(FI, D, A, B, C, 11, 10, 0xbd3af235);
+       OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
+       OP(FI, B, C, D, A, 9, 21, 0xeb86d391);
+# undef OP
+# endif
+       /* Add checksum to the starting values */
+       ctx->hash[0] = A_save + A;
+       ctx->hash[1] = B_save + B;
+       ctx->hash[2] = C_save + C;
+       ctx->hash[3] = D_save + D;
+#endif
+}
+#undef FF
+#undef FG
+#undef FH
+#undef FI
+
+/* Initialize structure containing state of computation.
+ * (RFC 1321, 3.3: Step 3)
+ */
+void FAST_FUNC md5_begin(md5_ctx_t *ctx)
+{
+       ctx->hash[0] = 0x67452301;
+       ctx->hash[1] = 0xefcdab89;
+       ctx->hash[2] = 0x98badcfe;
+       ctx->hash[3] = 0x10325476;
+       ctx->total64 = 0;
+       ctx->process_block = md5_process_block64;
+}
+
+/* Used also for sha1 and sha256 */
+void FAST_FUNC md5_hash(md5_ctx_t *ctx, const void *buffer, size_t len)
+{
+       common64_hash(ctx, buffer, len);
+}
+
+/* Process the remaining bytes in the buffer and put result from CTX
+ * in first 16 bytes following RESBUF.  The result is always in little
+ * endian byte order, so that a byte-wise output yields to the wanted
+ * ASCII representation of the message digest.
+ */
+void FAST_FUNC md5_end(md5_ctx_t *ctx, void *resbuf)
+{
+       /* MD5 stores total in LE, need to swap on BE arches: */
+       common64_end(ctx, /*swap_needed:*/ BB_BIG_ENDIAN);
+
+       /* The MD5 result is in little endian byte order */
+       if (BB_BIG_ENDIAN) {
+               ctx->hash[0] = SWAP_LE32(ctx->hash[0]);
+               ctx->hash[1] = SWAP_LE32(ctx->hash[1]);
+               ctx->hash[2] = SWAP_LE32(ctx->hash[2]);
+               ctx->hash[3] = SWAP_LE32(ctx->hash[3]);
+       }
+
+       memcpy(resbuf, ctx->hash, sizeof(ctx->hash[0]) * 4);
+}
+
+
+/*
+ * SHA1 part is:
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * Based on the public domain SHA-1 in C by Steve Reid <steve@edmweb.com>
+ * from http://www.mirrors.wiretapped.net/security/cryptography/hashes/sha1/
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * SHA256 and SHA512 parts are:
+ * Released into the Public Domain by Ulrich Drepper <drepper@redhat.com>.
+ * Shrank by Denys Vlasenko.
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * The best way to test random blocksizes is to go to coreutils/md5_sha1_sum.c
+ * and replace "4096" with something like "2000 + time(NULL) % 2097",
+ * then rebuild and compare "shaNNNsum bigfile" results.
+ */
+
+static void FAST_FUNC sha1_process_block64(sha1_ctx_t *ctx)
+{
+       static const uint32_t rconsts[] = {
+               0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6
+       };
+       int i, j;
+       int cnt;
+       uint32_t W[16+16];
+       uint32_t a, b, c, d, e;
+
+       /* On-stack work buffer frees up one register in the main loop
+        * which otherwise will be needed to hold ctx pointer */
+       for (i = 0; i < 16; i++)
+               W[i] = W[i+16] = SWAP_BE32(((uint32_t*)ctx->wbuffer)[i]);
+
+       a = ctx->hash[0];
+       b = ctx->hash[1];
+       c = ctx->hash[2];
+       d = ctx->hash[3];
+       e = ctx->hash[4];
+
+       /* 4 rounds of 20 operations each */
+       cnt = 0;
+       for (i = 0; i < 4; i++) {
+               j = 19;
+               do {
+                       uint32_t work;
+
+                       work = c ^ d;
+                       if (i == 0) {
+                               work = (work & b) ^ d;
+                               if (j <= 3)
+                                       goto ge16;
+                               /* Used to do SWAP_BE32 here, but this
+                                * requires ctx (see comment above) */
+                               work += W[cnt];
+                       } else {
+                               if (i == 2)
+                                       work = ((b | c) & d) | (b & c);
+                               else /* i = 1 or 3 */
+                                       work ^= b;
+ ge16:
+                               W[cnt] = W[cnt+16] = rotl32(W[cnt+13] ^ W[cnt+8] ^ W[cnt+2] ^ W[cnt], 1);
+                               work += W[cnt];
+                       }
+                       work += e + rotl32(a, 5) + rconsts[i];
+
+                       /* Rotate by one for next time */
+                       e = d;
+                       d = c;
+                       c = /* b = */ rotl32(b, 30);
+                       b = a;
+                       a = work;
+                       cnt = (cnt + 1) & 15;
+               } while (--j >= 0);
+       }
+
+       ctx->hash[0] += a;
+       ctx->hash[1] += b;
+       ctx->hash[2] += c;
+       ctx->hash[3] += d;
+       ctx->hash[4] += e;
+}
+
+/* Constants for SHA512 from FIPS 180-2:4.2.3.
+ * SHA256 constants from FIPS 180-2:4.2.2
+ * are the most significant half of first 64 elements
+ * of the same array.
+ */
+static const uint64_t sha_K[80] = {
+       0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
+       0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
+       0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
+       0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
+       0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
+       0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
+       0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
+       0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
+       0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
+       0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
+       0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
+       0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
+       0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
+       0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
+       0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
+       0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
+       0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
+       0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
+       0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
+       0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
+       0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
+       0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
+       0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
+       0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
+       0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
+       0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
+       0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
+       0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
+       0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
+       0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
+       0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
+       0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
+       0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, /* [64]+ are used for sha512 only */
+       0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
+       0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
+       0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
+       0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
+       0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
+       0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
+       0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
+};
+
+#undef Ch
+#undef Maj
+#undef S0
+#undef S1
+#undef R0
+#undef R1
+
+static void FAST_FUNC sha256_process_block64(sha256_ctx_t *ctx)
+{
+       unsigned t;
+       uint32_t W[64], a, b, c, d, e, f, g, h;
+       const uint32_t *words = (uint32_t*) ctx->wbuffer;
+
+       /* Operators defined in FIPS 180-2:4.1.2.  */
+#define Ch(x, y, z) ((x & y) ^ (~x & z))
+#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+#define S0(x) (rotr32(x, 2) ^ rotr32(x, 13) ^ rotr32(x, 22))
+#define S1(x) (rotr32(x, 6) ^ rotr32(x, 11) ^ rotr32(x, 25))
+#define R0(x) (rotr32(x, 7) ^ rotr32(x, 18) ^ (x >> 3))
+#define R1(x) (rotr32(x, 17) ^ rotr32(x, 19) ^ (x >> 10))
+
+       /* Compute the message schedule according to FIPS 180-2:6.2.2 step 2.  */
+       for (t = 0; t < 16; ++t)
+               W[t] = SWAP_BE32(words[t]);
+       for (/*t = 16*/; t < 64; ++t)
+               W[t] = R1(W[t - 2]) + W[t - 7] + R0(W[t - 15]) + W[t - 16];
+
+       a = ctx->hash[0];
+       b = ctx->hash[1];
+       c = ctx->hash[2];
+       d = ctx->hash[3];
+       e = ctx->hash[4];
+       f = ctx->hash[5];
+       g = ctx->hash[6];
+       h = ctx->hash[7];
+
+       /* The actual computation according to FIPS 180-2:6.2.2 step 3.  */
+       for (t = 0; t < 64; ++t) {
+               /* Need to fetch upper half of sha_K[t]
+                * (I hope compiler is clever enough to just fetch
+                * upper half)
+                */
+               uint32_t K_t = sha_K[t] >> 32;
+               uint32_t T1 = h + S1(e) + Ch(e, f, g) + K_t + W[t];
+               uint32_t T2 = S0(a) + Maj(a, b, c);
+               h = g;
+               g = f;
+               f = e;
+               e = d + T1;
+               d = c;
+               c = b;
+               b = a;
+               a = T1 + T2;
+       }
+#undef Ch
+#undef Maj
+#undef S0
+#undef S1
+#undef R0
+#undef R1
+       /* Add the starting values of the context according to FIPS 180-2:6.2.2
+          step 4.  */
+       ctx->hash[0] += a;
+       ctx->hash[1] += b;
+       ctx->hash[2] += c;
+       ctx->hash[3] += d;
+       ctx->hash[4] += e;
+       ctx->hash[5] += f;
+       ctx->hash[6] += g;
+       ctx->hash[7] += h;
+}
+
+static void FAST_FUNC sha512_process_block128(sha512_ctx_t *ctx)
+{
+       unsigned t;
+       uint64_t W[80];
+       /* On i386, having assignments here (not later as sha256 does)
+        * produces 99 bytes smaller code with gcc 4.3.1
+        */
+       uint64_t a = ctx->hash[0];
+       uint64_t b = ctx->hash[1];
+       uint64_t c = ctx->hash[2];
+       uint64_t d = ctx->hash[3];
+       uint64_t e = ctx->hash[4];
+       uint64_t f = ctx->hash[5];
+       uint64_t g = ctx->hash[6];
+       uint64_t h = ctx->hash[7];
+       const uint64_t *words = (uint64_t*) ctx->wbuffer;
+
+       /* Operators defined in FIPS 180-2:4.1.2.  */
+#define Ch(x, y, z) ((x & y) ^ (~x & z))
+#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+#define S0(x) (rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39))
+#define S1(x) (rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41))
+#define R0(x) (rotr64(x, 1) ^ rotr64(x, 8) ^ (x >> 7))
+#define R1(x) (rotr64(x, 19) ^ rotr64(x, 61) ^ (x >> 6))
+
+       /* Compute the message schedule according to FIPS 180-2:6.3.2 step 2.  */
+       for (t = 0; t < 16; ++t)
+               W[t] = SWAP_BE64(words[t]);
+       for (/*t = 16*/; t < 80; ++t)
+               W[t] = R1(W[t - 2]) + W[t - 7] + R0(W[t - 15]) + W[t - 16];
+
+       /* The actual computation according to FIPS 180-2:6.3.2 step 3.  */
+       for (t = 0; t < 80; ++t) {
+               uint64_t T1 = h + S1(e) + Ch(e, f, g) + sha_K[t] + W[t];
+               uint64_t T2 = S0(a) + Maj(a, b, c);
+               h = g;
+               g = f;
+               f = e;
+               e = d + T1;
+               d = c;
+               c = b;
+               b = a;
+               a = T1 + T2;
+       }
+#undef Ch
+#undef Maj
+#undef S0
+#undef S1
+#undef R0
+#undef R1
+       /* Add the starting values of the context according to FIPS 180-2:6.3.2
+          step 4.  */
+       ctx->hash[0] += a;
+       ctx->hash[1] += b;
+       ctx->hash[2] += c;
+       ctx->hash[3] += d;
+       ctx->hash[4] += e;
+       ctx->hash[5] += f;
+       ctx->hash[6] += g;
+       ctx->hash[7] += h;
+}
+
+
+void FAST_FUNC sha1_begin(sha1_ctx_t *ctx)
+{
+       ctx->hash[0] = 0x67452301;
+       ctx->hash[1] = 0xefcdab89;
+       ctx->hash[2] = 0x98badcfe;
+       ctx->hash[3] = 0x10325476;
+       ctx->hash[4] = 0xc3d2e1f0;
+       ctx->total64 = 0;
+       ctx->process_block = sha1_process_block64;
+}
+
+static const uint32_t init256[] = {
+       0,
+       0,
+       0x6a09e667,
+       0xbb67ae85,
+       0x3c6ef372,
+       0xa54ff53a,
+       0x510e527f,
+       0x9b05688c,
+       0x1f83d9ab,
+       0x5be0cd19,
+};
+static const uint32_t init512_lo[] = {
+       0,
+       0,
+       0xf3bcc908,
+       0x84caa73b,
+       0xfe94f82b,
+       0x5f1d36f1,
+       0xade682d1,
+       0x2b3e6c1f,
+       0xfb41bd6b,
+       0x137e2179,
+};
+
+/* Initialize structure containing state of computation.
+   (FIPS 180-2:5.3.2)  */
+void FAST_FUNC sha256_begin(sha256_ctx_t *ctx)
+{
+       memcpy(&ctx->total64, init256, sizeof(init256));
+       /*ctx->total64 = 0; - done by prepending two 32-bit zeros to init256 */
+       ctx->process_block = sha256_process_block64;
+}
+
+/* Initialize structure containing state of computation.
+   (FIPS 180-2:5.3.3)  */
+void FAST_FUNC sha512_begin(sha512_ctx_t *ctx)
+{
+       int i;
+       /* Two extra iterations zero out ctx->total64[2] */
+       uint64_t *tp = ctx->total64;
+       for (i = 0; i < 2+8; i++)
+               tp[i] = ((uint64_t)(init256[i]) << 32) + init512_lo[i];
+       /*ctx->total64[0] = ctx->total64[1] = 0; - already done */
+}
+
+void FAST_FUNC sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len)
+{
+       unsigned bufpos = ctx->total64[0] & 127;
+       unsigned remaining;
+
+       /* First increment the byte count.  FIPS 180-2 specifies the possible
+          length of the file up to 2^128 _bits_.
+          We compute the number of _bytes_ and convert to bits later.  */
+       ctx->total64[0] += len;
+       if (ctx->total64[0] < len)
+               ctx->total64[1]++;
+#if 0
+       remaining = 128 - bufpos;
+
+       /* Hash whole blocks */
+       while (len >= remaining) {
+               memcpy(ctx->wbuffer + bufpos, buffer, remaining);
+               buffer = (const char *)buffer + remaining;
+               len -= remaining;
+               remaining = 128;
+               bufpos = 0;
+               sha512_process_block128(ctx);
+       }
+
+       /* Save last, partial blosk */
+       memcpy(ctx->wbuffer + bufpos, buffer, len);
+#else
+       while (1) {
+               remaining = 128 - bufpos;
+               if (remaining > len)
+                       remaining = len;
+               /* Copy data into aligned buffer */
+               memcpy(ctx->wbuffer + bufpos, buffer, remaining);
+               len -= remaining;
+               buffer = (const char *)buffer + remaining;
+               bufpos += remaining;
+               /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */
+               bufpos -= 128;
+               if (bufpos != 0)
+                       break;
+               /* Buffer is filled up, process it */
+               sha512_process_block128(ctx);
+               /*bufpos = 0; - already is */
+       }
+#endif
+}
+
+/* Used also for sha256 */
+void FAST_FUNC sha1_end(sha1_ctx_t *ctx, void *resbuf)
+{
+       unsigned hash_size;
+
+       /* SHA stores total in BE, need to swap on LE arches: */
+       common64_end(ctx, /*swap_needed:*/ BB_LITTLE_ENDIAN);
+
+       hash_size = (ctx->process_block == sha1_process_block64) ? 5 : 8;
+       /* This way we do not impose alignment constraints on resbuf: */
+       if (BB_LITTLE_ENDIAN) {
+               unsigned i;
+               for (i = 0; i < hash_size; ++i)
+                       ctx->hash[i] = SWAP_BE32(ctx->hash[i]);
+       }
+       memcpy(resbuf, ctx->hash, sizeof(ctx->hash[0]) * hash_size);
+}
+
+void FAST_FUNC sha512_end(sha512_ctx_t *ctx, void *resbuf)
+{
+       unsigned bufpos = ctx->total64[0] & 127;
+
+       /* Pad the buffer to the next 128-byte boundary with 0x80,0,0,0... */
+       ctx->wbuffer[bufpos++] = 0x80;
+
+       while (1) {
+               unsigned remaining = 128 - bufpos;
+               memset(ctx->wbuffer + bufpos, 0, remaining);
+               if (remaining >= 16) {
+                       /* Store the 128-bit counter of bits in the buffer in BE format */
+                       uint64_t t;
+                       t = ctx->total64[0] << 3;
+                       t = SWAP_BE64(t);
+                       *(bb__aliased_uint64_t *) (&ctx->wbuffer[128 - 8]) = t;
+                       t = (ctx->total64[1] << 3) | (ctx->total64[0] >> 61);
+                       t = SWAP_BE64(t);
+                       *(bb__aliased_uint64_t *) (&ctx->wbuffer[128 - 16]) = t;
+               }
+               sha512_process_block128(ctx);
+               if (remaining >= 16)
+                       break;
+               bufpos = 0;
+       }
+
+       if (BB_LITTLE_ENDIAN) {
+               unsigned i;
+               for (i = 0; i < ARRAY_SIZE(ctx->hash); ++i)
+                       ctx->hash[i] = SWAP_BE64(ctx->hash[i]);
+       }
+       memcpy(resbuf, ctx->hash, sizeof(ctx->hash));
+}
+
+
+/*
+ * The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
+ * Michael Peeters and Gilles Van Assche. For more information, feedback or
+ * questions, please refer to our website: http://keccak.noekeon.org/
+ *
+ * Implementation by Ronny Van Keer,
+ * hereby denoted as "the implementer".
+ *
+ * To the extent possible under law, the implementer has waived all copyright
+ * and related or neighboring rights to the source code in this file.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * Busybox modifications (C) Lauri Kasanen, under the GPLv2.
+ */
+
+#if CONFIG_SHA3_SMALL < 0
+# define SHA3_SMALL 0
+#elif CONFIG_SHA3_SMALL > 1
+# define SHA3_SMALL 1
+#else
+# define SHA3_SMALL CONFIG_SHA3_SMALL
+#endif
+
+enum {
+       SHA3_IBLK_BYTES = 72, /* 576 bits / 8 */
+};
+
+/*
+ * In the crypto literature this function is usually called Keccak-f().
+ */
+static void sha3_process_block72(uint64_t *state)
+{
+       enum { NROUNDS = 24 };
+
+       /* Elements should be 64-bit, but top half is always zero or 0x80000000.
+        * We encode 63rd bits in a separate word below.
+        * Same is true for 31th bits, which lets us use 16-bit table instead of 64-bit.
+        * The speed penalty is lost in the noise.
+        */
+       static const uint16_t IOTA_CONST[NROUNDS] = {
+               0x0001,
+               0x8082,
+               0x808a,
+               0x8000,
+               0x808b,
+               0x0001,
+               0x8081,
+               0x8009,
+               0x008a,
+               0x0088,
+               0x8009,
+               0x000a,
+               0x808b,
+               0x008b,
+               0x8089,
+               0x8003,
+               0x8002,
+               0x0080,
+               0x800a,
+               0x000a,
+               0x8081,
+               0x8080,
+               0x0001,
+               0x8008,
+       };
+       /* bit for CONST[0] is in msb: 0011 0011 0000 0111 1101 1101 */
+       const uint32_t IOTA_CONST_bit63 = (uint32_t)(0x3307dd00);
+       /* bit for CONST[0] is in msb: 0001 0110 0011 1000 0001 1011 */
+       const uint32_t IOTA_CONST_bit31 = (uint32_t)(0x16381b00);
+
+       static const uint8_t ROT_CONST[24] = {
+               1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
+               27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
+       };
+       static const uint8_t PI_LANE[24] = {
+               10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
+               15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
+       };
+       /*static const uint8_t MOD5[10] = { 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, };*/
+
+       unsigned x, y;
+       unsigned round;
+
+       if (BB_BIG_ENDIAN) {
+               for (x = 0; x < 25; x++) {
+                       state[x] = SWAP_LE64(state[x]);
+               }
+       }
+
+       for (round = 0; round < NROUNDS; ++round) {
+               /* Theta */
+               {
+                       uint64_t BC[10];
+                       for (x = 0; x < 5; ++x) {
+                               BC[x + 5] = BC[x] = state[x]
+                                       ^ state[x + 5] ^ state[x + 10]
+                                       ^ state[x + 15] ^ state[x + 20];
+                       }
+                       /* Using 2x5 vector above eliminates the need to use
+                        * BC[MOD5[x+N]] trick below to fetch BC[(x+N) % 5],
+                        * and the code is a bit _smaller_.
+                        */
+                       for (x = 0; x < 5; ++x) {
+                               uint64_t temp = BC[x + 4] ^ rotl64(BC[x + 1], 1);
+                               state[x] ^= temp;
+                               state[x + 5] ^= temp;
+                               state[x + 10] ^= temp;
+                               state[x + 15] ^= temp;
+                               state[x + 20] ^= temp;
+                       }
+               }
+
+               /* Rho Pi */
+               if (SHA3_SMALL) {
+                       uint64_t t1 = state[1];
+                       for (x = 0; x < 24; ++x) {
+                               uint64_t t0 = state[PI_LANE[x]];
+                               state[PI_LANE[x]] = rotl64(t1, ROT_CONST[x]);
+                               t1 = t0;
+                       }
+               } else {
+                       /* Especially large benefit for 32-bit arch (75% faster):
+                        * 64-bit rotations by non-constant usually are SLOW on those.
+                        * We resort to unrolling here.
+                        * This optimizes out PI_LANE[] and ROT_CONST[],
+                        * but generates 300-500 more bytes of code.
+                        */
+                       uint64_t t0;
+                       uint64_t t1 = state[1];
+#define RhoPi_twice(x) \
+       t0 = state[PI_LANE[x  ]]; \
+       state[PI_LANE[x  ]] = rotl64(t1, ROT_CONST[x  ]); \
+       t1 = state[PI_LANE[x+1]]; \
+       state[PI_LANE[x+1]] = rotl64(t0, ROT_CONST[x+1]);
+                       RhoPi_twice(0); RhoPi_twice(2);
+                       RhoPi_twice(4); RhoPi_twice(6);
+                       RhoPi_twice(8); RhoPi_twice(10);
+                       RhoPi_twice(12); RhoPi_twice(14);
+                       RhoPi_twice(16); RhoPi_twice(18);
+                       RhoPi_twice(20); RhoPi_twice(22);
+#undef RhoPi_twice
+               }
+
+               /* Chi */
+               for (y = 0; y <= 20; y += 5) {
+                       uint64_t BC0, BC1, BC2, BC3, BC4;
+                       BC0 = state[y + 0];
+                       BC1 = state[y + 1];
+                       BC2 = state[y + 2];
+                       state[y + 0] = BC0 ^ ((~BC1) & BC2);
+                       BC3 = state[y + 3];
+                       state[y + 1] = BC1 ^ ((~BC2) & BC3);
+                       BC4 = state[y + 4];
+                       state[y + 2] = BC2 ^ ((~BC3) & BC4);
+                       state[y + 3] = BC3 ^ ((~BC4) & BC0);
+                       state[y + 4] = BC4 ^ ((~BC0) & BC1);
+               }
+
+               /* Iota */
+               state[0] ^= IOTA_CONST[round]
+                       | (uint32_t)((IOTA_CONST_bit31 << round) & 0x80000000)
+                       | (uint64_t)((IOTA_CONST_bit63 << round) & 0x80000000) << 32;
+       }
+
+       if (BB_BIG_ENDIAN) {
+               for (x = 0; x < 25; x++) {
+                       state[x] = SWAP_LE64(state[x]);
+               }
+       }
+}
+
+void FAST_FUNC sha3_begin(sha3_ctx_t *ctx)
+{
+       memset(ctx, 0, sizeof(*ctx));
+}
+
+void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len)
+{
+#if SHA3_SMALL
+       const uint8_t *data = buffer;
+       unsigned bufpos = ctx->bytes_queued;
+
+       while (1) {
+               unsigned remaining = SHA3_IBLK_BYTES - bufpos;
+               if (remaining > len)
+                       remaining = len;
+               len -= remaining;
+               /* XOR data into buffer */
+               while (remaining != 0) {
+                       uint8_t *buf = (uint8_t*)ctx->state;
+                       buf[bufpos] ^= *data++;
+                       bufpos++;
+                       remaining--;
+               }
+               /* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */
+               bufpos -= SHA3_IBLK_BYTES;
+               if (bufpos != 0)
+                       break;
+               /* Buffer is filled up, process it */
+               sha3_process_block72(ctx->state);
+               /*bufpos = 0; - already is */
+       }
+       ctx->bytes_queued = bufpos + SHA3_IBLK_BYTES;
+#else
+       /* +50 bytes code size, but a bit faster because of long-sized XORs */
+       const uint8_t *data = buffer;
+       unsigned bufpos = ctx->bytes_queued;
+
+       /* If already data in queue, continue queuing first */
+       while (len != 0 && bufpos != 0) {
+               uint8_t *buf = (uint8_t*)ctx->state;
+               buf[bufpos] ^= *data++;
+               len--;
+               bufpos++;
+               if (bufpos == SHA3_IBLK_BYTES) {
+                       bufpos = 0;
+                       goto do_block;
+               }
+       }
+
+       /* Absorb complete blocks */
+       while (len >= SHA3_IBLK_BYTES) {
+               /* XOR data onto beginning of state[].
+                * We try to be efficient - operate one word at a time, not byte.
+                * Careful wrt unaligned access: can't just use "*(long*)data"!
+                */
+               unsigned count = SHA3_IBLK_BYTES / sizeof(long);
+               long *buf = (long*)ctx->state;
+               do {
+                       long v;
+                       move_from_unaligned_long(v, (long*)data);
+                       *buf++ ^= v;
+                       data += sizeof(long);
+               } while (--count);
+               len -= SHA3_IBLK_BYTES;
+ do_block:
+               sha3_process_block72(ctx->state);
+       }
+
+       /* Queue remaining data bytes */
+       while (len != 0) {
+               uint8_t *buf = (uint8_t*)ctx->state;
+               buf[bufpos] ^= *data++;
+               bufpos++;
+               len--;
+       }
+
+       ctx->bytes_queued = bufpos;
+#endif
+}
+
+void FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf)
+{
+       /* Padding */
+       uint8_t *buf = (uint8_t*)ctx->state;
+       buf[ctx->bytes_queued]   ^= 1;
+       buf[SHA3_IBLK_BYTES - 1] ^= 0x80;
+
+       sha3_process_block72(ctx->state);
+
+       /* Output */
+       memcpy(resbuf, ctx->state, 64);
+}
similarity index 96%
rename from libbb/md5prime.c
rename to libbb/hash_md5prime.c
index 7986f4d..e089a15 100644 (file)
@@ -59,7 +59,7 @@
  * Completely removed static PADDING array.
  *
  * Reintroduced the loop unrolling in md5_transform and added the
- * MD5_SIZE_VS_SPEED option for configurability.  Define below as:
+ * MD5_SMALL option for configurability.  Define below as:
  *       0    fully unrolled loops
  *       1    partially unrolled (4 ops per loop)
  *       2    no unrolling -- introduces the need to swap 4 variables (slow)
 #include "libbb.h"
 
 /* 1: fastest, 3: smallest */
-#if CONFIG_MD5_SIZE_VS_SPEED < 1
-# define MD5_SIZE_VS_SPEED 1
-#elif CONFIG_MD5_SIZE_VS_SPEED > 3
-# define MD5_SIZE_VS_SPEED 3
+#if CONFIG_MD5_SMALL < 1
+# define MD5_SMALL 1
+#elif CONFIG_MD5_SMALL > 3
+# define MD5_SMALL 3
 #else
-# define MD5_SIZE_VS_SPEED CONFIG_MD5_SIZE_VS_SPEED
+# define MD5_SMALL CONFIG_MD5_SMALL
 #endif
 
 #if BB_LITTLE_ENDIAN
@@ -152,7 +152,7 @@ memcpy32_le2cpu(uint32_t *output, const unsigned char *input, unsigned len)
 static void md5_transform(uint32_t state[4], const unsigned char block[64])
 {
        uint32_t a, b, c, d, x[16];
-#if MD5_SIZE_VS_SPEED > 1
+#if MD5_SMALL > 1
        uint32_t temp;
        const unsigned char *ps;
 
@@ -162,9 +162,9 @@ static void md5_transform(uint32_t state[4], const unsigned char block[64])
                4, 11, 16, 23,
                6, 10, 15, 21
        };
-#endif /* MD5_SIZE_VS_SPEED > 1 */
+#endif /* MD5_SMALL > 1 */
 
-#if MD5_SIZE_VS_SPEED > 0
+#if MD5_SMALL > 0
        const uint32_t *pc;
        const unsigned char *pp;
        int i;
@@ -198,7 +198,7 @@ static void md5_transform(uint32_t state[4], const unsigned char block[64])
                0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9  /* 4 */
        };
 
-#endif /* MD5_SIZE_VS_SPEED > 0 */
+#endif /* MD5_SMALL > 0 */
 
        memcpy32_le2cpu(x, block, 64);
 
@@ -207,7 +207,7 @@ static void md5_transform(uint32_t state[4], const unsigned char block[64])
        c = state[2];
        d = state[3];
 
-#if MD5_SIZE_VS_SPEED > 2
+#if MD5_SMALL > 2
        pc = C;
        pp = P;
        ps = S - 4;
@@ -233,7 +233,7 @@ static void md5_transform(uint32_t state[4], const unsigned char block[64])
                temp += b;
                a = d; d = c; c = b; b = temp;
        }
-#elif MD5_SIZE_VS_SPEED > 1
+#elif MD5_SMALL > 1
        pc = C;
        pp = P;
        ps = S;
@@ -260,7 +260,7 @@ static void md5_transform(uint32_t state[4], const unsigned char block[64])
                II(a, b, c, d, x[*pp], ps[i & 0x3], *pc); pp++; pc++;
                temp = d; d = c; c = b; b = a; a = temp;
        }
-#elif MD5_SIZE_VS_SPEED > 0
+#elif MD5_SMALL > 0
        pc = C;
        pp = P;
        /* Round 1 */
index ca9274c..d041076 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
index 4228aaf..0b2eb77 100644 (file)
@@ -25,7 +25,7 @@
  *      Some code to omit the decimal point and tenths digit is sketched out
  *      and "#if 0"'d below.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -53,8 +53,8 @@ const char* FAST_FUNC make_human_readable_str(unsigned long long val,
        u = unit_chars;
 
        if (display_unit) {
-               val += display_unit/2;  /* Deal with rounding */
-               val /= display_unit;    /* Don't combine with the line above! */
+               val += display_unit/2;  /* Deal with rounding */
+               val /= display_unit;    /* Don't combine with the line above! */
                /* will just print it as ulonglong (below) */
        } else {
                while ((val >= 1024)
@@ -94,7 +94,7 @@ const char* FAST_FUNC make_human_readable_str(unsigned long long val,
 
 /* Convert unsigned long long value into compact 5-char representation.
  * String is not terminated (buf[5] is untouched) */
-void FAST_FUNC smart_ulltoa5(unsigned long long ul, char buf[6], const char *scale)
+char* FAST_FUNC smart_ulltoa5(unsigned long long ul, char buf[5], const char *scale)
 {
        const char *fmt;
        char c;
@@ -145,12 +145,13 @@ void FAST_FUNC smart_ulltoa5(unsigned long long ul, char buf[6], const char *sca
                buf[3] = "0123456789"[v];
                buf[4] = scale[idx]; /* typically scale = " kmgt..." */
        }
+       return buf + 5;
 }
 
 /* Convert unsigned long long value into compact 4-char
  * representation. Examples: "1234", "1.2k", " 27M", "123T"
  * String is not terminated (buf[4] is untouched) */
-void FAST_FUNC smart_ulltoa4(unsigned long long ul, char buf[5], const char *scale)
+char* FAST_FUNC smart_ulltoa4(unsigned long long ul, char buf[4], const char *scale)
 {
        const char *fmt;
        char c;
@@ -194,4 +195,5 @@ void FAST_FUNC smart_ulltoa4(unsigned long long ul, char buf[5], const char *sca
                buf[2] = "0123456789"[v];
                buf[3] = scale[idx]; /* typically scale = " kmgt..." */
        }
+       return buf + 4;
 }
diff --git a/libbb/in_ether.c b/libbb/in_ether.c
new file mode 100644 (file)
index 0000000..1de383b
--- /dev/null
@@ -0,0 +1,59 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ */
+
+//kbuild:lib-$(CONFIG_ARP) += in_ether.o
+//kbuild:lib-$(CONFIG_IFCONFIG) += in_ether.o
+//kbuild:lib-$(CONFIG_IFENSLAVE) += in_ether.o
+
+#include "libbb.h"
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+
+/* Convert Ethernet address from "XX[:]XX[:]XX[:]XX[:]XX[:]XX" to sockaddr.
+ * Return nonzero on error.
+ */
+int FAST_FUNC in_ether(const char *bufp, struct sockaddr *sap)
+{
+       char *ptr;
+       int i, j;
+       unsigned char val;
+       unsigned char c;
+
+       sap->sa_family = ARPHRD_ETHER;
+       ptr = (char *) sap->sa_data;
+
+       i = ETH_ALEN;
+       goto first;
+       do {
+               /* We might get a semicolon here */
+               if (*bufp == ':')
+                       bufp++;
+ first:
+               j = val = 0;
+               do {
+                       c = *bufp;
+                       if (((unsigned char)(c - '0')) <= 9) {
+                               c -= '0';
+                       } else if ((unsigned char)((c|0x20) - 'a') <= 5) {
+                               c = (unsigned char)((c|0x20) - 'a') + 10;
+                       } else {
+                               if (j && (c == ':' || c == '\0'))
+                                       /* One-digit byte: __:X:__ */
+                                       break;
+                               return -1;
+                       }
+                       ++bufp;
+                       val <<= 4;
+                       val += c;
+                       j ^= 1;
+               } while (j);
+
+               *ptr++ = val;
+
+       } while (--i);
+
+       /* Error if we aren't at end of string */
+       return *bufp;
+}
diff --git a/libbb/inet_cksum.c b/libbb/inet_cksum.c
new file mode 100644 (file)
index 0000000..3d5dc3a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Checksum routine for Internet Protocol family headers (C Version)
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+uint16_t FAST_FUNC inet_cksum(uint16_t *addr, int nleft)
+{
+       /*
+        * Our algorithm is simple, using a 32 bit accumulator,
+        * we add sequential 16 bit words to it, and at the end, fold
+        * back all the carry bits from the top 16 bits into the lower
+        * 16 bits.
+        */
+       unsigned sum = 0;
+       while (nleft > 1) {
+               sum += *addr++;
+               nleft -= 2;
+       }
+
+       /* Mop up an odd byte, if necessary */
+       if (nleft == 1) {
+               if (BB_LITTLE_ENDIAN)
+                       sum += *(uint8_t*)addr;
+               else
+                       sum += *(uint8_t*)addr << 8;
+       }
+
+       /* Add back carry outs from top 16 bits to low 16 bits */
+       sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
+       sum += (sum >> 16);                     /* add carry */
+
+       return (uint16_t)~sum;
+}
index 0fc08d6..b3e0802 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Heavily modified by Manuel Novoa III       Mar 12, 2001
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -23,7 +23,7 @@ int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostf
        s_in->sin_port = 0;
 
        /* Default is special, meaning 0.0.0.0. */
-       if (!strcmp(name, bb_str_default)) {
+       if (strcmp(name, "default") == 0) {
                s_in->sin_addr.s_addr = INADDR_ANY;
                return 1;
        }
@@ -97,7 +97,7 @@ char* FAST_FUNC INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t ne
        if (s_in->sin_family != AF_INET) {
 #ifdef DEBUG
                bb_error_msg("rresolve: unsupported address family %d!",
-                                 s_in->sin_family);
+                               s_in->sin_family);
 #endif
                errno = EAFNOSUPPORT;
                return NULL;
@@ -109,7 +109,7 @@ char* FAST_FUNC INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t ne
        if (ad == INADDR_ANY) {
                if ((numeric & 0x0FFF) == 0) {
                        if (numeric & 0x8000)
-                               return xstrdup(bb_str_default);
+                               return xstrdup("default");
                        return xstrdup("*");
                }
        }
@@ -164,17 +164,17 @@ char* FAST_FUNC INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t ne
 
 int FAST_FUNC INET6_resolve(const char *name, struct sockaddr_in6 *sin6)
 {
-       struct addrinfo req, *ai;
+       struct addrinfo req, *ai = NULL;
        int s;
 
-       memset(&req, '\0', sizeof req);
+       memset(&req, 0, sizeof(req));
        req.ai_family = AF_INET6;
        s = getaddrinfo(name, NULL, &req, &ai);
-       if (s) {
+       if (s != 0) {
                bb_error_msg("getaddrinfo: %s: %d", name, s);
                return -1;
        }
-       memcpy(sin6, ai->ai_addr, sizeof(struct sockaddr_in6));
+       memcpy(sin6, ai->ai_addr, sizeof(*sin6));
        freeaddrinfo(ai);
        return 0;
 }
@@ -194,7 +194,7 @@ char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric)
        if (sin6->sin6_family != AF_INET6) {
 #ifdef DEBUG
                bb_error_msg("rresolve: unsupported address family %d!",
-                                 sin6->sin6_family);
+                               sin6->sin6_family);
 #endif
                errno = EAFNOSUPPORT;
                return NULL;
@@ -205,17 +205,19 @@ char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric)
        }
        if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
                if (numeric & 0x8000)
-                       return xstrdup(bb_str_default);
+                       return xstrdup("default");
                return xstrdup("*");
        }
 
-       s = getnameinfo((struct sockaddr *) sin6, sizeof(struct sockaddr_in6),
-                               name, sizeof(name), NULL, 0, 0);
-       if (s) {
+       s = getnameinfo((struct sockaddr *) sin6, sizeof(*sin6),
+                               name, sizeof(name),
+                               /*serv,servlen:*/ NULL, 0,
+                               0);
+       if (s != 0) {
                bb_error_msg("getnameinfo failed");
                return NULL;
        }
        return xstrdup(name);
 }
 
-#endif         /* CONFIG_FEATURE_IPV6 */
+#endif  /* CONFIG_FEATURE_IPV6 */
index 81164fa..56ca2ef 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index b32bd26..715535e 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) many different people.
  * If you wrote this, please acknowledge your work.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -17,8 +17,8 @@ typedef struct ino_dev_hash_bucket_struct {
        char name[1];
 } ino_dev_hashtable_bucket_t;
 
-#define HASH_SIZE      311             /* Should be prime */
-#define hash_inode(i)  ((i) % HASH_SIZE)
+#define HASH_SIZE      311   /* Should be prime */
+#define hash_inode(i)  ((i) % HASH_SIZE)
 
 /* array of [HASH_SIZE] elements */
 static ino_dev_hashtable_bucket_t **ino_dev_hashtable;
index 4a2961e..ba6c52c 100644 (file)
@@ -3,9 +3,9 @@
  * Utility routines.
  *
  * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
- * Permission has been granted to redistribute this code under the GPL.
+ * Permission has been granted to redistribute this code under GPL.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include <sys/stat.h>
  * Return TRUE if fileName is a directory.
  * Nonexistent files return FALSE.
  */
-int FAST_FUNC is_directory(const char *fileName, int followLinks, struct stat *statBuf)
+int FAST_FUNC is_directory(const char *fileName, int followLinks)
 {
        int status;
-       struct stat astatBuf;
-
-       if (statBuf == NULL) {
-               /* use auto stack buffer */
-               statBuf = &astatBuf;
-       }
+       struct stat statBuf;
 
        if (followLinks)
-               status = stat(fileName, statBuf);
+               status = stat(fileName, &statBuf);
        else
-               status = lstat(fileName, statBuf);
+               status = lstat(fileName, &statBuf);
 
-       status = (status == 0 && S_ISDIR(statBuf->st_mode));
+       status = (status == 0 && S_ISDIR(statBuf.st_mode));
 
        return status;
 }
index cc23712..738ed02 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 int FAST_FUNC get_linux_version_code(void)
 {
        struct utsname name;
-       char *s;
+       char *s, *t;
        int i, r;
 
-       if (uname(&name) == -1) {
-               bb_perror_msg("can't get system information");
-               return 0;
-       }
-
+       uname(&name); /* never fails */
        s = name.release;
        r = 0;
        for (i = 0; i < 3; i++) {
-               r = r * 256 + atoi(strtok(s, "."));
+               t = strtok(s, ".");
+               r = r * 256 + (t ? atoi(t) : 0);
                s = NULL;
        }
        return r;
index b059256..65e6cdf 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2001 Larry Doolittle, <ldoolitt@recycle.lbl.gov>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index a9b790c..8564307 100644 (file)
@@ -1,6 +1,6 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Termios command line History and Editing.
+ * Command line editing.
  *
  * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license.
  * Written by:   Vladimir Oleynik <dzo@simtreas.ru>
  * and the \] escape to signal the end of such a sequence. Example:
  *
  * PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
+ *
+ * Unicode in PS1 is not fully supported: prompt length calulation is wrong,
+ * resulting in line wrap problems with long (multi-line) input.
+ *
+ * Multi-line PS1 (e.g. PS1="\n[\w]\n$ ") has problems with history
+ * browsing: up/down arrows result in scrolling.
+ * It stems from simplistic "cmdedit_y = cmdedit_prmt_len / cmdedit_termw"
+ * calculation of how many lines the prompt takes.
  */
 #include "libbb.h"
 #include "unicode.h"
+#ifndef _POSIX_VDISABLE
+# define _POSIX_VDISABLE '\0'
+#endif
+
 
 #ifdef TEST
 # define ENABLE_FEATURE_EDITING 0
 #if ENABLE_FEATURE_EDITING
 
 
-#define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \
+#define ENABLE_USERNAME_OR_HOMEDIR \
        (ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT)
-#define IF_FEATURE_GETUSERNAME_AND_HOMEDIR(...)
-#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
-#undef IF_FEATURE_GETUSERNAME_AND_HOMEDIR
-#define IF_FEATURE_GETUSERNAME_AND_HOMEDIR(...) __VA_ARGS__
+#define IF_USERNAME_OR_HOMEDIR(...)
+#if ENABLE_USERNAME_OR_HOMEDIR
+# undef IF_USERNAME_OR_HOMEDIR
+# define IF_USERNAME_OR_HOMEDIR(...) __VA_ARGS__
 #endif
 
 
-#define SEQ_CLEAR_TILL_END_OF_SCREEN "\033[J"
-//#define SEQ_CLEAR_TILL_END_OF_LINE   "\033[K"
-
-
 #undef CHAR_T
 #if ENABLE_UNICODE_SUPPORT
 # define BB_NUL ((wchar_t)0)
@@ -90,24 +98,27 @@ static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); }
 # define BB_isalnum(c) isalnum(c)
 # define BB_ispunct(c) ispunct(c)
 #endif
+#if ENABLE_UNICODE_PRESERVE_BROKEN
+# define unicode_mark_raw_byte(wc)   ((wc) | 0x20000000)
+# define unicode_is_raw_byte(wc)     ((wc) & 0x20000000)
+#else
+# define unicode_is_raw_byte(wc)     0
+#endif
 
 
-# if ENABLE_UNICODE_PRESERVE_BROKEN
-#  define unicode_mark_raw_byte(wc)   ((wc) | 0x20000000)
-#  define unicode_is_raw_byte(wc)     ((wc) & 0x20000000)
-# else
-#  define unicode_is_raw_byte(wc)     0
-# endif
+#define ESC "\033"
+
+#define SEQ_CLEAR_TILL_END_OF_SCREEN  ESC"[J"
+//#define SEQ_CLEAR_TILL_END_OF_LINE  ESC"[K"
 
 
 enum {
-       /* We use int16_t for positions, need to limit line len */
        MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0
                      ? CONFIG_FEATURE_EDITING_MAX_LEN
                      : 0x7ff0
 };
 
-#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
+#if ENABLE_USERNAME_OR_HOMEDIR
 static const char null_str[] ALIGN1 = "";
 #endif
 
@@ -130,11 +141,8 @@ struct lineedit_statics {
        CHAR_T *command_ps;
 
        const char *cmdedit_prompt;
-#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
-       int num_ok_lines; /* = 1; */
-#endif
 
-#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
+#if ENABLE_USERNAME_OR_HOMEDIR
        char *user_buf;
        char *home_pwd_buf; /* = (char*)null_str; */
 #endif
@@ -145,7 +153,7 @@ struct lineedit_statics {
 #endif
 
 #if ENABLE_FEATURE_EDITING_VI
-#define DELBUFSIZ 128
+# define DELBUFSIZ 128
        CHAR_T *delptr;
        smallint newdelflag;     /* whether delbuf should be reused yet */
        CHAR_T delbuf[DELBUFSIZ];  /* a place to store deleted characters */
@@ -153,14 +161,6 @@ struct lineedit_statics {
 #if ENABLE_FEATURE_EDITING_ASK_TERMINAL
        smallint sent_ESC_br6n;
 #endif
-
-       /* Formerly these were big buffers on stack: */
-#if ENABLE_FEATURE_TAB_COMPLETION
-       char exe_n_cwd_tab_completion__dirbuf[MAX_LINELEN];
-       char input_tab__matchBuf[MAX_LINELEN];
-       int16_t find_match__int_buf[MAX_LINELEN + 1]; /* need to have 9 bits at least */
-       int16_t find_match__pos_buf[MAX_LINELEN + 1];
-#endif
 };
 
 /* See lineedit_ptr_hack.c */
@@ -177,7 +177,6 @@ extern struct lineedit_statics *const lineedit_ptr_to_statics;
 #define command_len      (S.command_len     )
 #define command_ps       (S.command_ps      )
 #define cmdedit_prompt   (S.cmdedit_prompt  )
-#define num_ok_lines     (S.num_ok_lines    )
 #define user_buf         (S.user_buf        )
 #define home_pwd_buf     (S.home_pwd_buf    )
 #define matches          (S.matches         )
@@ -190,17 +189,18 @@ extern struct lineedit_statics *const lineedit_ptr_to_statics;
        (*(struct lineedit_statics**)&lineedit_ptr_to_statics) = xzalloc(sizeof(S)); \
        barrier(); \
        cmdedit_termw = 80; \
-       IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \
-       IF_FEATURE_GETUSERNAME_AND_HOMEDIR(home_pwd_buf = (char*)null_str;) \
+       IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \
+       IF_FEATURE_EDITING_VI(delptr = delbuf;) \
 } while (0)
+
 static void deinit_S(void)
 {
 #if ENABLE_FEATURE_EDITING_FANCY_PROMPT
        /* This one is allocated only if FANCY_PROMPT is on
-        * (otherwise it points to verbatim prompt (NOT malloced) */
+        * (otherwise it points to verbatim prompt (NOT malloced)) */
        free((char*)cmdedit_prompt);
 #endif
-#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
+#if ENABLE_USERNAME_OR_HOMEDIR
        free(user_buf);
        if (home_pwd_buf != null_str)
                free(home_pwd_buf);
@@ -211,64 +211,87 @@ static void deinit_S(void)
 
 
 #if ENABLE_UNICODE_SUPPORT
-static size_t load_string(const char *src, int maxsize)
+static size_t load_string(const char *src)
 {
-       ssize_t len = mbstowcs(command_ps, src, maxsize - 1);
-       if (len < 0)
-               len = 0;
-       command_ps[len] = 0;
-       return len;
+       if (unicode_status == UNICODE_ON) {
+               ssize_t len = mbstowcs(command_ps, src, S.maxsize - 1);
+               if (len < 0)
+                       len = 0;
+               command_ps[len] = BB_NUL;
+               return len;
+       } else {
+               unsigned i = 0;
+               while (src[i] && i < S.maxsize - 1) {
+                       command_ps[i] = src[i];
+                       i++;
+               }
+               command_ps[i] = BB_NUL;
+               return i;
+       }
 }
 static unsigned save_string(char *dst, unsigned maxsize)
 {
+       if (unicode_status == UNICODE_ON) {
 # if !ENABLE_UNICODE_PRESERVE_BROKEN
-       ssize_t len = wcstombs(dst, command_ps, maxsize - 1);
-       if (len < 0)
-               len = 0;
-       dst[len] = '\0';
-       return len;
+               ssize_t len = wcstombs(dst, command_ps, maxsize - 1);
+               if (len < 0)
+                       len = 0;
+               dst[len] = '\0';
+               return len;
 # else
-       unsigned dstpos = 0;
-       unsigned srcpos = 0;
-
-       maxsize--;
-       while (dstpos < maxsize) {
-               wchar_t wc;
-               int n = srcpos;
-               while ((wc = command_ps[srcpos]) != 0
-                   && !unicode_is_raw_byte(wc)
-               ) {
+               unsigned dstpos = 0;
+               unsigned srcpos = 0;
+
+               maxsize--;
+               while (dstpos < maxsize) {
+                       wchar_t wc;
+                       int n = srcpos;
+
+                       /* Convert up to 1st invalid byte (or up to end) */
+                       while ((wc = command_ps[srcpos]) != BB_NUL
+                           && !unicode_is_raw_byte(wc)
+                       ) {
+                               srcpos++;
+                       }
+                       command_ps[srcpos] = BB_NUL;
+                       n = wcstombs(dst + dstpos, command_ps + n, maxsize - dstpos);
+                       if (n < 0) /* should not happen */
+                               break;
+                       dstpos += n;
+                       if (wc == BB_NUL) /* usually is */
+                               break;
+
+                       /* We do have invalid byte here! */
+                       command_ps[srcpos] = wc; /* restore it */
                        srcpos++;
+                       if (dstpos == maxsize)
+                               break;
+                       dst[dstpos++] = (char) wc;
                }
-               command_ps[srcpos] = 0;
-               n = wcstombs(dst + dstpos, command_ps + n, maxsize - dstpos);
-               if (n < 0) /* should not happen */
-                       break;
-               dstpos += n;
-               if (wc == 0) /* usually is */
-                       break;
-               /* We do have invalid byte here! */
-               command_ps[srcpos] = wc; /* restore it */
-               srcpos++;
-               if (dstpos == maxsize)
-                       break;
-               dst[dstpos++] = (char) wc;
-       }
-       dst[dstpos] = '\0';
-       return dstpos;
+               dst[dstpos] = '\0';
+               return dstpos;
 # endif
+       } else {
+               unsigned i = 0;
+               while ((dst[i] = command_ps[i]) != 0)
+                       i++;
+               return i;
+       }
 }
 /* I thought just fputwc(c, stdout) would work. But no... */
 static void BB_PUTCHAR(wchar_t c)
 {
-       char buf[MB_CUR_MAX + 1];
-       mbstate_t mbst = { 0 };
-       ssize_t len;
-
-       len = wcrtomb(buf, c, &mbst);
-       if (len > 0) {
-               buf[len] = '\0';
-               fputs(buf, stdout);
+       if (unicode_status == UNICODE_ON) {
+               char buf[MB_CUR_MAX + 1];
+               mbstate_t mbst = { 0 };
+               ssize_t len = wcrtomb(buf, c, &mbst);
+               if (len > 0) {
+                       buf[len] = '\0';
+                       fputs(buf, stdout);
+               }
+       } else {
+               /* In this case, c is always one byte */
+               putchar(c);
        }
 }
 # if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS
@@ -303,9 +326,9 @@ static wchar_t adjust_width_and_validate_wc(wchar_t wc)
        return wc;
 }
 #else /* !UNICODE */
-static size_t load_string(const char *src, int maxsize)
+static size_t load_string(const char *src)
 {
-       safe_strncpy(command_ps, src, maxsize);
+       safe_strncpy(command_ps, src, S.maxsize);
        return strlen(command_ps);
 }
 # if ENABLE_FEATURE_TAB_COMPLETION
@@ -454,7 +477,7 @@ static void input_backward(unsigned num)
                        } while (--num);
                        return;
                }
-               printf("\033[%uD", num);
+               printf(ESC"[%uD", num);
                return;
        }
 
@@ -479,7 +502,7 @@ static void input_backward(unsigned num)
                 */
                unsigned sv_cursor;
                /* go to 1st column; go up to first line */
-               printf("\r" "\033[%uA", cmdedit_y);
+               printf("\r" ESC"[%uA", cmdedit_y);
                cmdedit_y = 0;
                sv_cursor = cursor;
                put_prompt(); /* sets cursor to 0 */
@@ -496,12 +519,12 @@ static void input_backward(unsigned num)
                cmdedit_x = (width * cmdedit_y - num) % width;
                cmdedit_y -= lines_up;
                /* go to 1st column; go up */
-               printf("\r" "\033[%uA", lines_up);
+               printf("\r" ESC"[%uA", lines_up);
                /* go to correct column.
                 * xterm, konsole, Linux VT interpret 0 as 1 below! wow.
                 * need to *make sure* we skip it if cmdedit_x == 0 */
                if (cmdedit_x)
-                       printf("\033[%uC", cmdedit_x);
+                       printf(ESC"[%uC", cmdedit_x);
        }
 }
 
@@ -509,7 +532,7 @@ static void input_backward(unsigned num)
 static void redraw(int y, int back_cursor)
 {
        if (y > 0) /* up y lines */
-               printf("\033[%uA", y);
+               printf(ESC"[%uA", y);
        bb_putchar('\r');
        put_prompt();
        put_till_end_and_adv_cursor();
@@ -590,6 +613,12 @@ static void input_forward(void)
 
 #if ENABLE_FEATURE_TAB_COMPLETION
 
+//FIXME:
+//needs to be more clever: currently it thinks that "foo\ b<TAB>
+//matches the file named "foo bar", which is untrue.
+//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>,
+//not "foo bar <cursor>...
+
 static void free_tab_completion_data(void)
 {
        if (matches) {
@@ -607,56 +636,63 @@ static void add_match(char *matched)
        num_matches++;
 }
 
-#if ENABLE_FEATURE_USERNAME_COMPLETION
-static void username_tab_completion(char *ud, char *with_shash_flg)
+# if ENABLE_FEATURE_USERNAME_COMPLETION
+/* Replace "~user/..." with "/homedir/...".
+ * The parameter is malloced, free it or return it
+ * unchanged if no user is matched.
+ */
+static char *username_path_completion(char *ud)
 {
        struct passwd *entry;
-       int userlen;
+       char *tilde_name = ud;
+       char *home = NULL;
 
-       ud++;                           /* ~user/... to user/... */
-       userlen = strlen(ud);
+       ud++; /* skip ~ */
+       if (*ud == '/') {       /* "~/..." */
+               home = home_pwd_buf;
+       } else {
+               /* "~user/..." */
+               ud = strchr(ud, '/');
+               *ud = '\0';           /* "~user" */
+               entry = getpwnam(tilde_name + 1);
+               *ud = '/';            /* restore "~user/..." */
+               if (entry)
+                       home = entry->pw_dir;
+       }
+       if (home) {
+               ud = concat_path_file(home, ud);
+               free(tilde_name);
+               tilde_name = ud;
+       }
+       return tilde_name;
+}
 
-       if (with_shash_flg) {           /* "~/..." or "~user/..." */
-               char *sav_ud = ud - 1;
-               char *home = NULL;
+/* ~use<tab> - find all users with this prefix.
+ * Return the length of the prefix used for matching.
+ */
+static NOINLINE unsigned complete_username(const char *ud)
+{
+       /* Using _r function to avoid pulling in static buffers */
+       char line_buff[256];
+       struct passwd pwd;
+       struct passwd *result;
+       unsigned userlen;
 
-               if (*ud == '/') {       /* "~/..."     */
-                       home = home_pwd_buf;
-               } else {
-                       /* "~user/..." */
-                       char *temp;
-                       temp = strchr(ud, '/');
-                       *temp = '\0';           /* ~user\0 */
-                       entry = getpwnam(ud);
-                       *temp = '/';            /* restore ~user/... */
-                       ud = temp;
-                       if (entry)
-                               home = entry->pw_dir;
-               }
-               if (home) {
-                       if ((userlen + strlen(home) + 1) < MAX_LINELEN) {
-                               /* /home/user/... */
-                               sprintf(sav_ud, "%s%s", home, ud);
-                       }
-               }
-       } else {
-               /* "~[^/]*" */
-               /* Using _r function to avoid pulling in static buffers */
-               char line_buff[256];
-               struct passwd pwd;
-               struct passwd *result;
-
-               setpwent();
-               while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) {
-                       /* Null usernames should result in all users as possible completions. */
-                       if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) {
-                               add_match(xasprintf("~%s/", pwd.pw_name));
-                       }
+       ud++; /* skip ~ */
+       userlen = strlen(ud);
+
+       setpwent();
+       while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) {
+               /* Null usernames should result in all users as possible completions. */
+               if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) {
+                       add_match(xasprintf("~%s/", pwd.pw_name));
                }
-               endpwent();
        }
+       endpwent();
+
+       return 1 + userlen;
 }
-#endif  /* FEATURE_COMMAND_USERNAME_COMPLETION */
+# endif  /* FEATURE_USERNAME_COMPLETION */
 
 enum {
        FIND_EXE_ONLY = 0,
@@ -664,22 +700,19 @@ enum {
        FIND_FILE_ONLY = 2,
 };
 
-static int path_parse(char ***p, int flags)
+static int path_parse(char ***p)
 {
        int npth;
        const char *pth;
        char *tmp;
        char **res;
 
-       /* if not setenv PATH variable, to search cur dir "." */
-       if (flags != FIND_EXE_ONLY)
-               return 1;
-
        if (state->flags & WITH_PATH_LOOKUP)
                pth = state->path_lookup;
        else
                pth = getenv("PATH");
-       /* PATH=<empty> or PATH=:<empty> */
+
+       /* PATH="" or PATH=":"? */
        if (!pth || !pth[0] || LONE_CHAR(pth, ':'))
                return 1;
 
@@ -689,12 +722,13 @@ static int path_parse(char ***p, int flags)
                tmp = strchr(tmp, ':');
                if (!tmp)
                        break;
-               if (*++tmp == '\0')
+               tmp++;
+               if (*tmp == '\0')
                        break;  /* :<empty> */
                npth++;
        }
 
-       res = xmalloc(npth * sizeof(char*));
+       *p = res = xmalloc(npth * sizeof(res[0]));
        res[0] = tmp = xstrdup(pth);
        npth = 1;
        while (1) {
@@ -706,235 +740,241 @@ static int path_parse(char ***p, int flags)
                        break; /* :<empty> */
                res[npth++] = tmp;
        }
-       *p = res;
        return npth;
 }
 
-static void exe_n_cwd_tab_completion(char *command, int type)
+/* Complete command, directory or file name.
+ * Return the length of the prefix used for matching.
+ */
+static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
 {
-       DIR *dir;
-       struct dirent *next;
-       struct stat st;
        char *path1[1];
        char **paths = path1;
        int npaths;
        int i;
-       char *found;
-       char *pfind = strrchr(command, '/');
-/*     char dirbuf[MAX_LINELEN]; */
-#define dirbuf (S.exe_n_cwd_tab_completion__dirbuf)
+       unsigned pf_len;
+       const char *pfind;
+       char *dirbuf = NULL;
 
        npaths = 1;
        path1[0] = (char*)".";
 
-       if (pfind == NULL) {
-               /* no dir, if flags==EXE_ONLY - get paths, else "." */
-               npaths = path_parse(&paths, type);
+       pfind = strrchr(command, '/');
+       if (!pfind) {
+               if (type == FIND_EXE_ONLY)
+                       npaths = path_parse(&paths);
                pfind = command;
        } else {
-               /* dirbuf = ".../.../.../" */
-               safe_strncpy(dirbuf, command, (pfind - command) + 2);
-#if ENABLE_FEATURE_USERNAME_COMPLETION
-               if (dirbuf[0] == '~')   /* ~/... or ~user/... */
-                       username_tab_completion(dirbuf, dirbuf);
-#endif
-               paths[0] = dirbuf;
                /* point to 'l' in "..../last_component" */
                pfind++;
+               /* dirbuf = ".../.../.../" */
+               dirbuf = xstrndup(command, pfind - command);
+# if ENABLE_FEATURE_USERNAME_COMPLETION
+               if (dirbuf[0] == '~')   /* ~/... or ~user/... */
+                       dirbuf = username_path_completion(dirbuf);
+# endif
+               path1[0] = dirbuf;
        }
+       pf_len = strlen(pfind);
 
        for (i = 0; i < npaths; i++) {
+               DIR *dir;
+               struct dirent *next;
+               struct stat st;
+               char *found;
+
                dir = opendir(paths[i]);
                if (!dir)
                        continue; /* don't print an error */
 
                while ((next = readdir(dir)) != NULL) {
-                       int len1;
-                       const char *str_found = next->d_name;
+                       unsigned len;
+                       const char *name_found = next->d_name;
 
-                       /* matched? */
-                       if (strncmp(str_found, pfind, strlen(pfind)))
+                       /* .../<tab>: bash 3.2.0 shows dotfiles, but not . and .. */
+                       if (!pfind[0] && DOT_OR_DOTDOT(name_found))
                                continue;
-                       /* not see .name without .match */
-                       if (*str_found == '.' && *pfind == '\0') {
-                               if (NOT_LONE_CHAR(paths[i], '/') || str_found[1])
-                                       continue;
-                               str_found = ""; /* only "/" */
-                       }
-                       found = concat_path_file(paths[i], str_found);
-                       /* hmm, remove in progress? */
+                       /* match? */
+                       if (strncmp(name_found, pfind, pf_len) != 0)
+                               continue; /* no */
+
+                       found = concat_path_file(paths[i], name_found);
                        /* NB: stat() first so that we see is it a directory;
                         * but if that fails, use lstat() so that
                         * we still match dangling links */
                        if (stat(found, &st) && lstat(found, &st))
-                               goto cont;
-                       /* find with dirs? */
-                       if (paths[i] != dirbuf)
-                               strcpy(found, next->d_name); /* only name */
+                               goto cont; /* hmm, remove in progress? */
 
-                       len1 = strlen(found);
-                       found = xrealloc(found, len1 + 2);
-                       found[len1] = '\0';
-                       found[len1+1] = '\0';
+                       /* Save only name */
+                       len = strlen(name_found);
+                       found = xrealloc(found, len + 2); /* +2: for slash and NUL */
+                       strcpy(found, name_found);
 
                        if (S_ISDIR(st.st_mode)) {
-                               /* name is a directory */
-                               if (found[len1-1] != '/') {
-                                       found[len1] = '/';
-                               }
+                               /* name is a directory, add slash */
+                               found[len] = '/';
+                               found[len + 1] = '\0';
                        } else {
-                               /* not put found file if search only dirs for cd */
+                               /* skip files if looking for dirs only (example: cd) */
                                if (type == FIND_DIR_ONLY)
                                        goto cont;
                        }
-                       /* Add it to the list */
+                       /* add it to the list */
                        add_match(found);
                        continue;
  cont:
                        free(found);
                }
                closedir(dir);
-       }
+       } /* for every path */
+
        if (paths != path1) {
                free(paths[0]); /* allocated memory is only in first member */
                free(paths);
        }
-#undef dirbuf
+       free(dirbuf);
+
+       return pf_len;
 }
 
+/* build_match_prefix:
+ * On entry, match_buf contains everything up to cursor at the moment <tab>
+ * was pressed. This function looks at it, figures out what part of it
+ * constitutes the command/file/directory prefix to use for completion,
+ * and rewrites match_buf to contain only that part.
+ */
+#define dbg_bmp 0
+/* Helpers: */
 /* QUOT is used on elements of int_buf[], which are bytes,
  * not Unicode chars. Therefore it works correctly even in Unicode mode.
  */
 #define QUOT (UCHAR_MAX+1)
-
-#define int_buf (S.find_match__int_buf)
-#define pos_buf (S.find_match__pos_buf)
-/* is must be <= in */
-static void collapse_pos(int is, int in)
+static void remove_chunk(int16_t *int_buf, int beg, int end)
 {
-       memmove(int_buf+is, int_buf+in, (MAX_LINELEN+1-in)*sizeof(int_buf[0]));
-       memmove(pos_buf+is, pos_buf+in, (MAX_LINELEN+1-in)*sizeof(pos_buf[0]));
+       /* beg must be <= end */
+       if (beg == end)
+               return;
+
+       while ((int_buf[beg] = int_buf[end]) != 0)
+               beg++, end++;
+
+       if (dbg_bmp) {
+               int i;
+               for (i = 0; int_buf[i]; i++)
+                       bb_putchar((unsigned char)int_buf[i]);
+               bb_putchar('\n');
+       }
 }
-static NOINLINE int find_match(char *matchBuf, int *len_with_quotes)
+/* Caller ensures that match_buf points to a malloced buffer
+ * big enough to hold strlen(match_buf)*2 + 2
+ */
+static NOINLINE int build_match_prefix(char *match_buf)
 {
        int i, j;
        int command_mode;
-       int c, c2;
-/*     Were local, but it uses too much stack */
-/*     int16_t int_buf[MAX_LINELEN + 1]; */
-/*     int16_t pos_buf[MAX_LINELEN + 1]; */
-
-       /* set to integer dimension characters and own positions */
-       for (i = 0;; i++) {
-               int_buf[i] = (unsigned char)matchBuf[i];
-               if (int_buf[i] == 0) {
-                       pos_buf[i] = -1; /* end-fo-line indicator */
-                       break;
-               }
-               pos_buf[i] = i;
-       }
+       int16_t *int_buf = (int16_t*)match_buf;
 
-       /* mask \+symbol and convert '\t' to ' ' */
-       for (i = j = 0; matchBuf[i]; i++, j++) {
-               if (matchBuf[i] == '\\') {
-                       collapse_pos(j, j + 1);
-                       int_buf[j] |= QUOT;
-                       i++;
+       if (dbg_bmp) printf("\n%s\n", match_buf);
+
+       /* Copy in reverse order, since they overlap */
+       i = strlen(match_buf);
+       do {
+               int_buf[i] = (unsigned char)match_buf[i];
+               i--;
+       } while (i >= 0);
+
+       /* Mark every \c as "quoted c" */
+       for (i = 0; int_buf[i]; i++) {
+               if (int_buf[i] == '\\') {
+                       remove_chunk(int_buf, i, i + 1);
+                       int_buf[i] |= QUOT;
                }
        }
-       /* mask "symbols" or 'symbols' */
-       c2 = 0;
-       for (i = 0; int_buf[i]; i++) {
-               c = int_buf[i];
-               if (c == '\'' || c == '"') {
-                       if (c2 == 0)
-                               c2 = c;
-                       else {
-                               if (c == c2)
-                                       c2 = 0;
-                               else
-                                       int_buf[i] |= QUOT;
+       /* Quote-mark "chars" and 'chars', drop delimiters */
+       {
+               int in_quote = 0;
+               i = 0;
+               while (int_buf[i]) {
+                       int cur = int_buf[i];
+                       if (!cur)
+                               break;
+                       if (cur == '\'' || cur == '"') {
+                               if (!in_quote || (cur == in_quote)) {
+                                       in_quote ^= cur;
+                                       remove_chunk(int_buf, i, i + 1);
+                                       continue;
+                               }
                        }
-               } else if (c2 != 0 && c != '$')
-                       int_buf[i] |= QUOT;
+                       if (in_quote)
+                               int_buf[i] = cur | QUOT;
+                       i++;
+               }
        }
 
-       /* skip commands with arguments if line has commands delimiters */
-       /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
+       /* Remove everything up to command delimiters:
+        * ';' ';;' '&' '|' '&&' '||',
+        * but careful with '>&' '<&' '>|'
+        */
        for (i = 0; int_buf[i]; i++) {
-               c = int_buf[i];
-               c2 = int_buf[i + 1];
-               j = i ? int_buf[i - 1] : -1;
-               command_mode = 0;
-               if (c == ';' || c == '&' || c == '|') {
-                       command_mode = 1 + (c == c2);
-                       if (c == '&') {
-                               if (j == '>' || j == '<')
-                                       command_mode = 0;
-                       } else if (c == '|' && j == '>')
-                               command_mode = 0;
-               }
-               if (command_mode) {
-                       collapse_pos(0, i + command_mode);
-                       i = -1;  /* hack incremet */
+               int cur = int_buf[i];
+               if (cur == ';' || cur == '&' || cur == '|') {
+                       int prev = i ? int_buf[i - 1] : 0;
+                       if (cur == '&' && (prev == '>' || prev == '<')) {
+                               continue;
+                       } else if (cur == '|' && prev == '>') {
+                               continue;
+                       }
+                       remove_chunk(int_buf, 0, i + 1 + (cur == int_buf[i + 1]));
+                       i = -1;  /* back to square 1 */
                }
        }
-       /* collapse `command...` */
+       /* Remove all `cmd` */
        for (i = 0; int_buf[i]; i++) {
                if (int_buf[i] == '`') {
-                       for (j = i + 1; int_buf[j]; j++)
+                       for (j = i + 1; int_buf[j]; j++) {
                                if (int_buf[j] == '`') {
-                                       collapse_pos(i, j + 1);
-                                       j = 0;
-                                       break;
+                                       /* `cmd` should count as a word:
+                                        * `cmd` c<tab> should search for files c*,
+                                        * not commands c*. Therefore we don't drop
+                                        * `cmd` entirely, we replace it with single `.
+                                        */
+                                       remove_chunk(int_buf, i, j);
+                                       goto next;
                                }
-                       if (j) {
-                               /* not found closing ` - command mode, collapse all previous */
-                               collapse_pos(0, i + 1);
-                               break;
-                       } else
-                               i--;  /* hack incremet */
+                       }
+                       /* No closing ` - command mode, remove all up to ` */
+                       remove_chunk(int_buf, 0, i + 1);
+                       break;
+ next: ;
                }
        }
 
-       /* collapse (command...(command...)...) or {command...{command...}...} */
-       c = 0;  /* "recursive" level */
-       c2 = 0;
+       /* Remove "cmd (" and "cmd {"
+        * Example: "if { c<tab>"
+        * In this example, c should be matched as command pfx.
+        */
        for (i = 0; int_buf[i]; i++) {
                if (int_buf[i] == '(' || int_buf[i] == '{') {
-                       if (int_buf[i] == '(')
-                               c++;
-                       else
-                               c2++;
-                       collapse_pos(0, i + 1);
-                       i = -1;  /* hack incremet */
-               }
-       }
-       for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++) {
-               if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
-                       if (int_buf[i] == ')')
-                               c--;
-                       else
-                               c2--;
-                       collapse_pos(0, i + 1);
-                       i = -1;  /* hack incremet */
+                       remove_chunk(int_buf, 0, i + 1);
+                       i = -1;  /* back to square 1 */
                }
        }
 
-       /* skip first not quote space */
+       /* Remove leading unquoted spaces */
        for (i = 0; int_buf[i]; i++)
                if (int_buf[i] != ' ')
                        break;
-       if (i)
-               collapse_pos(0, i);
+       remove_chunk(int_buf, 0, i);
 
-       /* set find mode for completion */
+       /* Determine completion mode */
        command_mode = FIND_EXE_ONLY;
        for (i = 0; int_buf[i]; i++) {
                if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
-                       if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
-                        && matchBuf[pos_buf[0]] == 'c'
-                        && matchBuf[pos_buf[1]] == 'd'
+                       if (int_buf[i] == ' '
+                        && command_mode == FIND_EXE_ONLY
+                        && (char)int_buf[0] == 'c'
+                        && (char)int_buf[1] == 'd'
+                        && i == 2 /* -> int_buf[2] == ' ' */
                        ) {
                                command_mode = FIND_DIR_ONLY;
                        } else {
@@ -943,44 +983,32 @@ static NOINLINE int find_match(char *matchBuf, int *len_with_quotes)
                        }
                }
        }
-       for (i = 0; int_buf[i]; i++)
-               /* "strlen" */;
-       /* find last word */
+       if (dbg_bmp) printf("command_mode(0:exe/1:dir/2:file):%d\n", command_mode);
+
+       /* Remove everything except last word */
+       for (i = 0; int_buf[i]; i++) /* quasi-strlen(int_buf) */
+               continue;
        for (--i; i >= 0; i--) {
-               c = int_buf[i];
-               if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
-                       collapse_pos(0, i + 1);
+               int cur = int_buf[i];
+               if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&') {
+                       remove_chunk(int_buf, 0, i + 1);
                        break;
                }
        }
-       /* skip first not quoted '\'' or '"' */
-       for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++)
-               /*skip*/;
-       /* collapse quote or unquote // or /~ */
-       while ((int_buf[i] & ~QUOT) == '/'
-        && ((int_buf[i+1] & ~QUOT) == '/' || (int_buf[i+1] & ~QUOT) == '~')
-       ) {
+
+       /* Convert back to string of _chars_ */
+       i = 0;
+       while ((match_buf[i] = int_buf[i]) != '\0')
                i++;
-       }
 
-       /* set only match and destroy quotes */
-       j = 0;
-       for (c = 0; pos_buf[i] >= 0; i++) {
-               matchBuf[c++] = matchBuf[pos_buf[i]];
-               j = pos_buf[i] + 1;
-       }
-       matchBuf[c] = '\0';
-       /* old length matchBuf with quotes symbols */
-       *len_with_quotes = j ? j - pos_buf[0] : 0;
+       if (dbg_bmp) printf("final match_buf:'%s'\n", match_buf);
 
        return command_mode;
 }
-#undef int_buf
-#undef pos_buf
 
 /*
- * display by column (original idea from ls applet,
- * very optimized by me :)
+ * Display by column (original idea from ls applet,
+ * very optimized by me [Vladimir] :)
  */
 static void showfiles(void)
 {
@@ -1022,13 +1050,18 @@ static void showfiles(void)
        }
 }
 
-static char *add_quote_for_spec_chars(char *found)
+static const char *is_special_char(char c)
+{
+       return strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", c);
+}
+
+static char *quote_special_chars(char *found)
 {
        int l = 0;
        char *s = xzalloc((strlen(found) + 1) * 2);
 
        while (*found) {
-               if (strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", *found))
+               if (is_special_char(*found))
                        s[l++] = '\\';
                s[l++] = *found++;
        }
@@ -1037,168 +1070,213 @@ static char *add_quote_for_spec_chars(char *found)
 }
 
 /* Do TAB completion */
-static void input_tab(smallint *lastWasTab)
+static NOINLINE void input_tab(smallint *lastWasTab)
 {
+       char *chosen_match;
+       char *match_buf;
+       size_t len_found;
+       /* Length of string used for matching */
+       unsigned match_pfx_len = match_pfx_len;
+       int find_type;
+# if ENABLE_UNICODE_SUPPORT
+       /* cursor pos in command converted to multibyte form */
+       int cursor_mb;
+# endif
        if (!(state->flags & TAB_COMPLETION))
                return;
 
-       if (!*lastWasTab) {
-               char *tmp, *tmp1;
-               size_t len_found;
-/*             char matchBuf[MAX_LINELEN]; */
-#define matchBuf (S.input_tab__matchBuf)
-               int find_type;
-               int recalc_pos;
-#if ENABLE_UNICODE_SUPPORT
-               /* cursor pos in command converted to multibyte form */
-               int cursor_mb;
-#endif
+       if (*lastWasTab) {
+               /* The last char was a TAB too.
+                * Print a list of all the available choices.
+                */
+               if (num_matches > 0) {
+                       /* cursor will be changed by goto_new_line() */
+                       int sav_cursor = cursor;
+                       goto_new_line();
+                       showfiles();
+                       redraw(0, command_len - sav_cursor);
+               }
+               return;
+       }
 
-               *lastWasTab = TRUE;             /* flop trigger */
+       *lastWasTab = 1;
+       chosen_match = NULL;
 
-               /* Make a local copy of the string --
-                * up to the position of the cursor */
-               save_string(matchBuf, cursor + 1);
-#if ENABLE_UNICODE_SUPPORT
-               cursor_mb = strlen(matchBuf);
-#endif
-               tmp = matchBuf;
+       /* Make a local copy of the string up to the position of the cursor.
+        * build_match_prefix will expand it into int16_t's, need to allocate
+        * twice as much as the string_len+1.
+        * (we then also (ab)use this extra space later - see (**))
+        */
+       match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t));
+# if !ENABLE_UNICODE_SUPPORT
+       save_string(match_buf, cursor + 1); /* +1 for NUL */
+# else
+       {
+               CHAR_T wc = command_ps[cursor];
+               command_ps[cursor] = BB_NUL;
+               save_string(match_buf, MAX_LINELEN);
+               command_ps[cursor] = wc;
+               cursor_mb = strlen(match_buf);
+       }
+# endif
+       find_type = build_match_prefix(match_buf);
 
-               find_type = find_match(matchBuf, &recalc_pos);
+       /* Free up any memory already allocated */
+       free_tab_completion_data();
 
-               /* Free up any memory already allocated */
-               free_tab_completion_data();
+# if ENABLE_FEATURE_USERNAME_COMPLETION
+       /* If the word starts with ~ and there is no slash in the word,
+        * then try completing this word as a username. */
+       if (state->flags & USERNAME_COMPLETION)
+               if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL)
+                       match_pfx_len = complete_username(match_buf);
+# endif
+       /* If complete_username() did not match,
+        * try to match a command in $PATH, or a directory, or a file */
+       if (!matches)
+               match_pfx_len = complete_cmd_dir_file(match_buf, find_type);
 
-#if ENABLE_FEATURE_USERNAME_COMPLETION
-               /* If the word starts with `~' and there is no slash in the word,
-                * then try completing this word as a username. */
-               if (state->flags & USERNAME_COMPLETION)
-                       if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL)
-                               username_tab_completion(matchBuf, NULL);
-#endif
-               /* Try to match any executable in our path and everything
-                * in the current working directory */
-               if (!matches)
-                       exe_n_cwd_tab_completion(matchBuf, find_type);
-               /* Sort, then remove any duplicates found */
-               if (matches) {
-                       unsigned i;
-                       int n = 0;
-                       qsort_string_vector(matches, num_matches);
-                       for (i = 0; i < num_matches - 1; ++i) {
-                               if (matches[i] && matches[i+1]) { /* paranoia */
-                                       if (strcmp(matches[i], matches[i+1]) == 0) {
-                                               free(matches[i]);
-                                               matches[i] = NULL; /* paranoia */
-                                       } else {
-                                               matches[n++] = matches[i];
-                                       }
+       /* Account for backslashes which will be inserted
+        * by quote_special_chars() later */
+       {
+               const char *e = match_buf + strlen(match_buf);
+               const char *s = e - match_pfx_len;
+               while (s < e)
+                       if (is_special_char(*s++))
+                               match_pfx_len++;
+       }
+
+       /* Remove duplicates */
+       if (matches) {
+               unsigned i, n = 0;
+               qsort_string_vector(matches, num_matches);
+               for (i = 0; i < num_matches - 1; ++i) {
+                       //if (matches[i] && matches[i+1]) { /* paranoia */
+                               if (strcmp(matches[i], matches[i+1]) == 0) {
+                                       free(matches[i]);
+                                       //matches[i] = NULL; /* paranoia */
+                               } else {
+                                       matches[n++] = matches[i];
                                }
-                       }
-                       matches[n] = matches[i];
-                       num_matches = n + 1;
+                       //}
                }
-               /* Did we find exactly one match? */
-               if (!matches || num_matches > 1) { /* no */
-                       beep();
-                       if (!matches)
-                               return;         /* not found */
-                       /* find minimal match */
-                       tmp1 = xstrdup(matches[0]);
-                       for (tmp = tmp1; *tmp; tmp++) {
-                               for (len_found = 1; len_found < num_matches; len_found++) {
-                                       if (matches[len_found][tmp - tmp1] != *tmp) {
-                                               *tmp = '\0';
-                                               break;
-                                       }
+               matches[n++] = matches[i];
+               num_matches = n;
+       }
+
+       /* Did we find exactly one match? */
+       if (num_matches != 1) { /* no */
+               char *cp;
+               beep();
+               if (!matches)
+                       goto ret; /* no matches at all */
+               /* Find common prefix */
+               chosen_match = xstrdup(matches[0]);
+               for (cp = chosen_match; *cp; cp++) {
+                       unsigned n;
+                       for (n = 1; n < num_matches; n++) {
+                               if (matches[n][cp - chosen_match] != *cp) {
+                                       goto stop;
                                }
                        }
-                       if (*tmp1 == '\0') {        /* have unique */
-                               free(tmp1);
-                               return;
-                       }
-                       tmp = add_quote_for_spec_chars(tmp1);
-                       free(tmp1);
-               } else {                        /* one match */
-                       tmp = add_quote_for_spec_chars(matches[0]);
-                       /* for next completion current found */
-                       *lastWasTab = FALSE;
-
-                       len_found = strlen(tmp);
-                       if (tmp[len_found-1] != '/') {
-                               tmp[len_found] = ' ';
-                               tmp[len_found+1] = '\0';
-                       }
                }
+ stop:
+               if (cp == chosen_match) { /* have unique prefix? */
+                       goto ret; /* no */
+               }
+               *cp = '\0';
+               cp = quote_special_chars(chosen_match);
+               free(chosen_match);
+               chosen_match = cp;
+               len_found = strlen(chosen_match);
+       } else {                        /* exactly one match */
+               /* Next <tab> is not a double-tab */
+               *lastWasTab = 0;
+
+               chosen_match = quote_special_chars(matches[0]);
+               len_found = strlen(chosen_match);
+               if (chosen_match[len_found-1] != '/') {
+                       chosen_match[len_found] = ' ';
+                       chosen_match[++len_found] = '\0';
+               }
+       }
 
-               len_found = strlen(tmp);
-#if !ENABLE_UNICODE_SUPPORT
-               /* have space to place the match? */
-               /* The result consists of three parts with these lengths: */
-               /* (cursor - recalc_pos) + len_found + (command_len - cursor) */
-               /* it simplifies into: */
-               if ((int)(len_found + command_len - recalc_pos) < S.maxsize) {
+# if !ENABLE_UNICODE_SUPPORT
+       /* Have space to place the match? */
+       /* The result consists of three parts with these lengths: */
+       /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */
+       /* it simplifies into: */
+       if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) {
+               int pos;
+               /* save tail */
+               strcpy(match_buf, &command_ps[cursor]);
+               /* add match and tail */
+               sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf);
+               command_len = strlen(command_ps);
+               /* new pos */
+               pos = cursor + len_found - match_pfx_len;
+               /* write out the matched command */
+               redraw(cmdedit_y, command_len - pos);
+       }
+# else
+       {
+               /* Use 2nd half of match_buf as scratch space - see (**) */
+               char *command = match_buf + MAX_LINELEN;
+               int len = save_string(command, MAX_LINELEN);
+               /* Have space to place the match? */
+               /* cursor_mb + (len_found - match_pfx_len) + (len - cursor_mb) */
+               if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) {
+                       int pos;
                        /* save tail */
-                       strcpy(matchBuf, command_ps + cursor);
+                       strcpy(match_buf, &command[cursor_mb]);
+                       /* where do we want to have cursor after all? */
+                       strcpy(&command[cursor_mb], chosen_match + match_pfx_len);
+                       len = load_string(command);
                        /* add match and tail */
-                       sprintf(&command_ps[cursor - recalc_pos], "%s%s", tmp, matchBuf);
-                       command_len = strlen(command_ps);
-                       /* new pos */
-                       recalc_pos = cursor - recalc_pos + len_found;
+                       sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, match_buf);
+                       command_len = load_string(command);
                        /* write out the matched command */
-                       redraw(cmdedit_y, command_len - recalc_pos);
-               }
-#else
-               {
-                       char command[MAX_LINELEN];
-                       int len = save_string(command, sizeof(command));
-                       /* have space to place the match? */
-                       /* (cursor_mb - recalc_pos) + len_found + (len - cursor_mb) */
-                       if ((int)(len_found + len - recalc_pos) < MAX_LINELEN) {
-                               /* save tail */
-                               strcpy(matchBuf, command + cursor_mb);
-                               /* where do we want to have cursor after all? */
-                               strcpy(&command[cursor_mb - recalc_pos], tmp);
-                               len = load_string(command, S.maxsize);
-                               /* add match and tail */
-                               sprintf(&command[cursor_mb - recalc_pos], "%s%s", tmp, matchBuf);
-                               command_len = load_string(command, S.maxsize);
-                               /* write out the matched command */
-                               redraw(cmdedit_y, command_len - len);
-                       }
-               }
-#endif
-               free(tmp);
-#undef matchBuf
-       } else {
-               /* Ok -- the last char was a TAB.  Since they
-                * just hit TAB again, print a list of all the
-                * available choices... */
-               if (matches && num_matches > 0) {
-                       /* changed by goto_new_line() */
-                       int sav_cursor = cursor;
-
-                       /* Go to the next line */
-                       goto_new_line();
-                       showfiles();
-                       redraw(0, command_len - sav_cursor);
+                       /* paranoia: load_string can return 0 on conv error,
+                        * prevent passing pos = (0 - 12) to redraw */
+                       pos = command_len - len;
+                       redraw(cmdedit_y, pos >= 0 ? pos : 0);
                }
        }
+# endif
+ ret:
+       free(chosen_match);
+       free(match_buf);
 }
 
-#endif  /* FEATURE_COMMAND_TAB_COMPLETION */
+#endif  /* FEATURE_TAB_COMPLETION */
 
 
 line_input_t* FAST_FUNC new_line_input_t(int flags)
 {
        line_input_t *n = xzalloc(sizeof(*n));
        n->flags = flags;
+#if MAX_HISTORY > 0
+       n->max_history = MAX_HISTORY;
+#endif
        return n;
 }
 
 
 #if MAX_HISTORY > 0
 
+unsigned FAST_FUNC size_from_HISTFILESIZE(const char *hp)
+{
+       int size = MAX_HISTORY;
+       if (hp) {
+               size = atoi(hp);
+               if (size <= 0)
+                       return 1;
+               if (size > MAX_HISTORY)
+                       return MAX_HISTORY;
+       }
+       return size;
+}
+
 static void save_command_ps_at_cur_history(void)
 {
        if (command_ps[0] != BB_NUL) {
@@ -1241,6 +1319,17 @@ static int get_next_history(void)
        return 0;
 }
 
+/* Lists command history. Used by shell 'history' builtins */
+void FAST_FUNC show_history(const line_input_t *st)
+{
+       int i;
+
+       if (!st)
+               return;
+       for (i = 0; i < st->cnt_history; i++)
+               printf("%4d %s\n", i, st->history[i]);
+}
+
 # if ENABLE_FEATURE_EDITING_SAVEHISTORY
 /* We try to ensure that concurrent additions to the history
  * do not overwrite each other.
@@ -1279,7 +1368,8 @@ static void load_history(line_input_t *st_parm)
 
                /* fill temp_h[], retaining only last MAX_HISTORY lines */
                memset(temp_h, 0, sizeof(temp_h));
-               st_parm->cnt_history_in_file = idx = 0;
+               idx = 0;
+               st_parm->cnt_history_in_file = 0;
                while ((line = xmalloc_fgetline(fp)) != NULL) {
                        if (line[0] == '\0') {
                                free(line);
@@ -1289,7 +1379,7 @@ static void load_history(line_input_t *st_parm)
                        temp_h[idx] = line;
                        st_parm->cnt_history_in_file++;
                        idx++;
-                       if (idx == MAX_HISTORY)
+                       if (idx == st_parm->max_history)
                                idx = 0;
                }
                fclose(fp);
@@ -1298,18 +1388,18 @@ static void load_history(line_input_t *st_parm)
                if (st_parm->cnt_history_in_file) {
                        while (temp_h[idx] == NULL) {
                                idx++;
-                               if (idx == MAX_HISTORY)
+                               if (idx == st_parm->max_history)
                                        idx = 0;
                        }
                }
 
                /* copy temp_h[] to st_parm->history[] */
-               for (i = 0; i < MAX_HISTORY;) {
+               for (i = 0; i < st_parm->max_history;) {
                        line = temp_h[idx];
                        if (!line)
                                break;
                        idx++;
-                       if (idx == MAX_HISTORY)
+                       if (idx == st_parm->max_history)
                                idx = 0;
                        line_len = strlen(line);
                        if (line_len >= MAX_LINELEN)
@@ -1317,16 +1407,63 @@ static void load_history(line_input_t *st_parm)
                        st_parm->history[i++] = line;
                }
                st_parm->cnt_history = i;
+               if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT)
+                       st_parm->cnt_history_in_file = i;
        }
 }
 
-/* state->flags is already checked to be nonzero */
+#  if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
+void save_history(line_input_t *st)
+{
+       FILE *fp;
+
+       if (!st->hist_file)
+               return;
+       if (st->cnt_history <= st->cnt_history_in_file)
+               return;
+
+       fp = fopen(st->hist_file, "a");
+       if (fp) {
+               int i, fd;
+               char *new_name;
+               line_input_t *st_temp;
+
+               for (i = st->cnt_history_in_file; i < st->cnt_history; i++)
+                       fprintf(fp, "%s\n", st->history[i]);
+               fclose(fp);
+
+               /* we may have concurrently written entries from others.
+                * load them */
+               st_temp = new_line_input_t(st->flags);
+               st_temp->hist_file = st->hist_file;
+               st_temp->max_history = st->max_history;
+               load_history(st_temp);
+
+               /* write out temp file and replace hist_file atomically */
+               new_name = xasprintf("%s.%u.new", st->hist_file, (int) getpid());
+               fd = open(new_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+               if (fd >= 0) {
+                       fp = xfdopen_for_write(fd);
+                       for (i = 0; i < st_temp->cnt_history; i++)
+                               fprintf(fp, "%s\n", st_temp->history[i]);
+                       fclose(fp);
+                       if (rename(new_name, st->hist_file) == 0)
+                               st->cnt_history_in_file = st_temp->cnt_history;
+               }
+               free(new_name);
+               free_line_input_t(st_temp);
+       }
+}
+#  else
 static void save_history(char *str)
 {
        int fd;
        int len, len2;
 
-       fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0666);
+       if (!state->hist_file)
+               return;
+
+       fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600);
        if (fd < 0)
                return;
        xlseek(fd, 0, SEEK_END); /* paranoia */
@@ -1340,22 +1477,25 @@ static void save_history(char *str)
 
        /* did we write so much that history file needs trimming? */
        state->cnt_history_in_file++;
-       if (state->cnt_history_in_file > MAX_HISTORY * 4) {
-               FILE *fp;
+       if (state->cnt_history_in_file > state->max_history * 4) {
                char *new_name;
                line_input_t *st_temp;
-               int i;
 
                /* we may have concurrently written entries from others.
                 * load them */
                st_temp = new_line_input_t(state->flags);
                st_temp->hist_file = state->hist_file;
+               st_temp->max_history = state->max_history;
                load_history(st_temp);
 
                /* write out temp file and replace hist_file atomically */
                new_name = xasprintf("%s.%u.new", state->hist_file, (int) getpid());
-               fp = fopen_for_write(new_name);
-               if (fp) {
+               fd = open(new_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+               if (fd >= 0) {
+                       FILE *fp;
+                       int i;
+
+                       fp = xfdopen_for_write(fd);
                        for (i = 0; i < st_temp->cnt_history; i++)
                                fprintf(fp, "%s\n", st_temp->history[i]);
                        fclose(fp);
@@ -1366,6 +1506,7 @@ static void save_history(char *str)
                free_line_input_t(st_temp);
        }
 }
+#  endif
 # else
 #  define load_history(a) ((void)0)
 #  define save_history(a) ((void)0)
@@ -1384,27 +1525,29 @@ static void remember_in_history(char *str)
        if (i && strcmp(state->history[i-1], str) == 0)
                return;
 
-       free(state->history[MAX_HISTORY]); /* redundant, paranoia */
-       state->history[MAX_HISTORY] = NULL; /* redundant, paranoia */
+       free(state->history[state->max_history]); /* redundant, paranoia */
+       state->history[state->max_history] = NULL; /* redundant, paranoia */
 
        /* If history[] is full, remove the oldest command */
-       /* we need to keep history[MAX_HISTORY] empty, hence >=, not > */
-       if (i >= MAX_HISTORY) {
+       /* we need to keep history[state->max_history] empty, hence >=, not > */
+       if (i >= state->max_history) {
                free(state->history[0]);
-               for (i = 0; i < MAX_HISTORY-1; i++)
+               for (i = 0; i < state->max_history-1; i++)
                        state->history[i] = state->history[i+1];
-               /* i == MAX_HISTORY-1 */
+               /* i == state->max_history-1 */
+# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
+               if (state->cnt_history_in_file)
+                       state->cnt_history_in_file--;
+# endif
        }
-       /* i <= MAX_HISTORY-1 */
+       /* i <= state->max_history-1 */
        state->history[i++] = xstrdup(str);
-       /* i <= MAX_HISTORY */
+       /* i <= state->max_history */
        state->cur_history = i;
        state->cnt_history = i;
-# if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
-       if ((state->flags & SAVE_HISTORY) && state->hist_file)
-               save_history(str);
+# if ENABLE_FEATURE_EDITING_SAVEHISTORY && !ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
+       save_history(str);
 # endif
-       IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;)
 }
 
 #else /* MAX_HISTORY == 0 */
@@ -1609,7 +1752,7 @@ static void ask_terminal(void)
         * write(1, "~/srcdevel/bbox/fix/busybox.t4 # ", 33) = 33
         * poll([{fd=0, events=POLLIN}], 1, 0) = 0 (Timeout)  <-- no input exists
         * write(1, "\33[6n", 4) = 4  <-- send the ESC sequence, quick!
-        * poll([{fd=0, events=POLLIN}], 1, 4294967295) = 1 ([{fd=0, revents=POLLIN}])
+        * poll([{fd=0, events=POLLIN}], 1, -1) = 1 ([{fd=0, revents=POLLIN}])
         * read(0, "\n", 1)      = 1  <-- oh crap, user's input got in first
         */
        struct pollfd pfd;
@@ -1618,7 +1761,7 @@ static void ask_terminal(void)
        pfd.events = POLLIN;
        if (safe_poll(&pfd, 1, 0) == 0) {
                S.sent_ESC_br6n = 1;
-               fputs("\033" "[6n", stdout);
+               fputs(ESC"[6n", stdout);
                fflush_all(); /* make terminal see it ASAP! */
        }
 }
@@ -1626,87 +1769,143 @@ static void ask_terminal(void)
 #define ask_terminal() ((void)0)
 #endif
 
+/* Called just once at read_line_input() init time */
 #if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
 static void parse_and_put_prompt(const char *prmt_ptr)
 {
+       const char *p;
        cmdedit_prompt = prmt_ptr;
-       cmdedit_prmt_len = strlen(prmt_ptr);
+       p = strrchr(prmt_ptr, '\n');
+       cmdedit_prmt_len = unicode_strwidth(p ? p+1 : prmt_ptr);
        put_prompt();
 }
 #else
 static void parse_and_put_prompt(const char *prmt_ptr)
 {
-       int prmt_len = 0;
-       size_t cur_prmt_len = 0;
-       char flg_not_length = '[';
+       int prmt_size = 0;
        char *prmt_mem_ptr = xzalloc(1);
-       char *cwd_buf = xrealloc_getcwd_or_warn(NULL);
+# if ENABLE_USERNAME_OR_HOMEDIR
+       char *cwd_buf = NULL;
+# endif
+       char flg_not_length = '[';
        char cbuf[2];
-       char c;
-       char *pbuf;
 
-       cmdedit_prmt_len = 0;
-
-       if (!cwd_buf) {
-               cwd_buf = (char *)bb_msg_unknown;
-       }
+       /*cmdedit_prmt_len = 0; - already is */
 
        cbuf[1] = '\0'; /* never changes */
 
        while (*prmt_ptr) {
+               char timebuf[sizeof("HH:MM:SS")];
                char *free_me = NULL;
+               char *pbuf;
+               char c;
 
                pbuf = cbuf;
                c = *prmt_ptr++;
                if (c == '\\') {
-                       const char *cp = prmt_ptr;
+                       const char *cp;
                        int l;
-
-                       c = bb_process_escape_sequence(&prmt_ptr);
+/*
+ * Supported via bb_process_escape_sequence:
+ * \a  ASCII bell character (07)
+ * \e  ASCII escape character (033)
+ * \n  newline
+ * \r  carriage return
+ * \\  backslash
+ * \nnn        char with octal code nnn
+ * Supported:
+ * \$  if the effective UID is 0, a #, otherwise a $
+ * \w  current working directory, with $HOME abbreviated with a tilde
+ *     Note: we do not support $PROMPT_DIRTRIM=n feature
+ * \W  basename of the current working directory, with $HOME abbreviated with a tilde
+ * \h  hostname up to the first '.'
+ * \H  hostname
+ * \u  username
+ * \[  begin a sequence of non-printing characters
+ * \]  end a sequence of non-printing characters
+ * \T  current time in 12-hour HH:MM:SS format
+ * \@  current time in 12-hour am/pm format
+ * \A  current time in 24-hour HH:MM format
+ * \t  current time in 24-hour HH:MM:SS format
+ *     (all of the above work as \A)
+ * Not supported:
+ * \!  history number of this command
+ * \#  command number of this command
+ * \j  number of jobs currently managed by the shell
+ * \l  basename of the shell's terminal device name
+ * \s  name of the shell, the basename of $0 (the portion following the final slash)
+ * \V  release of bash, version + patch level (e.g., 2.00.0)
+ * \d  date in "Weekday Month Date" format (e.g., "Tue May 26")
+ * \D{format}
+ *     format is passed to strftime(3).
+ *     An empty format results in a locale-specific time representation.
+ *     The braces are required.
+ * Mishandled by bb_process_escape_sequence:
+ * \v  version of bash (e.g., 2.00)
+ */
+                       cp = prmt_ptr;
+                       c = *cp;
+                       if (c != 't') /* don't treat \t as tab */
+                               c = bb_process_escape_sequence(&prmt_ptr);
                        if (prmt_ptr == cp) {
                                if (*cp == '\0')
                                        break;
                                c = *prmt_ptr++;
 
                                switch (c) {
-# if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
+# if ENABLE_USERNAME_OR_HOMEDIR
                                case 'u':
                                        pbuf = user_buf ? user_buf : (char*)"";
                                        break;
 # endif
+                               case 'H':
                                case 'h':
                                        pbuf = free_me = safe_gethostname();
-                                       *strchrnul(pbuf, '.') = '\0';
+                                       if (c == 'h')
+                                               strchrnul(pbuf, '.')[0] = '\0';
                                        break;
                                case '$':
                                        c = (geteuid() == 0 ? '#' : '$');
                                        break;
-# if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
-                               case 'w':
-                                       /* /home/user[/something] -> ~[/something] */
-                                       pbuf = cwd_buf;
-                                       l = strlen(home_pwd_buf);
-                                       if (l != 0
-                                        && strncmp(home_pwd_buf, cwd_buf, l) == 0
-                                        && (cwd_buf[l]=='/' || cwd_buf[l]=='\0')
-                                        && strlen(cwd_buf + l) < PATH_MAX
-                                       ) {
-                                               pbuf = free_me = xasprintf("~%s", cwd_buf + l);
-                                       }
+                               case 'T': /* 12-hour HH:MM:SS format */
+                               case '@': /* 12-hour am/pm format */
+                               case 'A': /* 24-hour HH:MM format */
+                               case 't': /* 24-hour HH:MM:SS format */
+                                       /* We show all of them as 24-hour HH:MM */
+                                       strftime_HHMMSS(timebuf, sizeof(timebuf), NULL)[-3] = '\0';
+                                       pbuf = timebuf;
                                        break;
-# endif
-                               case 'W':
+# if ENABLE_USERNAME_OR_HOMEDIR
+                               case 'w': /* current dir */
+                               case 'W': /* basename of cur dir */
+                                       if (!cwd_buf) {
+                                               cwd_buf = xrealloc_getcwd_or_warn(NULL);
+                                               if (!cwd_buf)
+                                                       cwd_buf = (char *)bb_msg_unknown;
+                                               else {
+                                                       /* /home/user[/something] -> ~[/something] */
+                                                       l = strlen(home_pwd_buf);
+                                                       if (l != 0
+                                                        && strncmp(home_pwd_buf, cwd_buf, l) == 0
+                                                        && (cwd_buf[l] == '/' || cwd_buf[l] == '\0')
+                                                       ) {
+                                                               cwd_buf[0] = '~';
+                                                               overlapping_strcpy(cwd_buf + 1, cwd_buf + l);
+                                                       }
+                                               }
+                                       }
                                        pbuf = cwd_buf;
+                                       if (c == 'w')
+                                               break;
                                        cp = strrchr(pbuf, '/');
-                                       if (cp != NULL && cp != pbuf)
-                                               pbuf += (cp-pbuf) + 1;
-                                       break;
-                               case '!':
-                                       pbuf = free_me = xasprintf("%d", num_ok_lines);
-                                       break;
-                               case 'e': case 'E':     /* \e \E = \033 */
-                                       c = '\033';
+                                       if (cp)
+                                               pbuf = (char*)cp + 1;
                                        break;
+# endif
+// bb_process_escape_sequence does this now:
+//                             case 'e': case 'E':     /* \e \E = \033 */
+//                                     c = '\033';
+//                                     break;
                                case 'x': case 'X': {
                                        char buf2[4];
                                        for (l = 0; l < 3;) {
@@ -1728,7 +1927,8 @@ static void parse_and_put_prompt(const char *prmt_ptr)
                                }
                                case '[': case ']':
                                        if (c == flg_not_length) {
-                                               flg_not_length = (flg_not_length == '[' ? ']' : '[');
+                                               /* Toggle '['/']' hex 5b/5d */
+                                               flg_not_length ^= 6;
                                                continue;
                                        }
                                        break;
@@ -1736,16 +1936,29 @@ static void parse_and_put_prompt(const char *prmt_ptr)
                        } /* if */
                } /* if */
                cbuf[0] = c;
-               cur_prmt_len = strlen(pbuf);
-               prmt_len += cur_prmt_len;
-               if (flg_not_length != ']')
-                       cmdedit_prmt_len += cur_prmt_len;
-               prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
+               {
+                       int n = strlen(pbuf);
+                       prmt_size += n;
+                       if (c == '\n')
+                               cmdedit_prmt_len = 0;
+                       else if (flg_not_length != ']') {
+#if 0 /*ENABLE_UNICODE_SUPPORT*/
+/* Won't work, pbuf is one BYTE string here instead of an one Unicode char string. */
+/* FIXME */
+                               cmdedit_prmt_len += unicode_strwidth(pbuf);
+#else
+                               cmdedit_prmt_len += n;
+#endif
+                       }
+               }
+               prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_size+1), pbuf);
                free(free_me);
        } /* while */
 
+# if ENABLE_USERNAME_OR_HOMEDIR
        if (cwd_buf != (char *)bb_msg_unknown)
                free(cwd_buf);
+# endif
        cmdedit_prompt = prmt_mem_ptr;
        put_prompt();
 }
@@ -1767,17 +1980,17 @@ static void win_changed(int nsig)
 {
        int sv_errno = errno;
        unsigned width;
+
        get_terminal_width_height(0, &width, NULL);
-       cmdedit_setwidth(width, nsig /* - just a yes/no flag */);
-       if (nsig == SIGWINCH)
-               signal(SIGWINCH, win_changed); /* rearm ourself */
+//FIXME: cmdedit_setwidth() -> redraw() -> printf() -> KABOOM! (we are in signal handler!)
+       cmdedit_setwidth(width, /*redraw_flg:*/ nsig);
+
        errno = sv_errno;
 }
 
-static int lineedit_read_key(char *read_key_buffer)
+static int lineedit_read_key(char *read_key_buffer, int timeout)
 {
        int64_t ic;
-       int timeout = -1;
 #if ENABLE_UNICODE_SUPPORT
        char unicode_buf[MB_CUR_MAX + 1];
        int unicode_idx = 0;
@@ -1807,7 +2020,15 @@ static int lineedit_read_key(char *read_key_buffer)
                        S.sent_ESC_br6n = 0;
                        if (cursor == 0) { /* otherwise it may be bogus */
                                int col = ((ic >> 32) & 0x7fff) - 1;
-                               if (col > cmdedit_prmt_len) {
+                               /*
+                                * Is col > cmdedit_prmt_len?
+                                * If yes (terminal says cursor is farther to the right
+                                * of where we think it should be),
+                                * the prompt wasn't printed starting at col 1,
+                                * there was additional text before it.
+                                */
+                               if ((int)(col - cmdedit_prmt_len) > 0) {
+                                       /* Fix our understanding of current x position */
                                        cmdedit_x += (col - cmdedit_prmt_len);
                                        while (cmdedit_x >= cmdedit_termw) {
                                                cmdedit_x -= cmdedit_termw;
@@ -1876,17 +2097,153 @@ static int isrtl_str(void)
 #undef CTRL
 #define CTRL(a) ((a) & ~0x40)
 
+enum {
+       VI_CMDMODE_BIT = 0x40000000,
+       /* 0x80000000 bit flags KEYCODE_xxx */
+};
+
+#if ENABLE_FEATURE_REVERSE_SEARCH
+/* Mimic readline Ctrl-R reverse history search.
+ * When invoked, it shows the following prompt:
+ * (reverse-i-search)'': user_input [cursor pos unchanged by Ctrl-R]
+ * and typing results in search being performed:
+ * (reverse-i-search)'tmp': cd /tmp [cursor under t in /tmp]
+ * Search is performed by looking at progressively older lines in history.
+ * Ctrl-R again searches for the next match in history.
+ * Backspace deletes last matched char.
+ * Control keys exit search and return to normal editing (at current history line).
+ */
+static int32_t reverse_i_search(void)
+{
+       char match_buf[128]; /* for user input */
+       char read_key_buffer[KEYCODE_BUFFER_SIZE];
+       const char *matched_history_line;
+       const char *saved_prompt;
+       unsigned saved_prmt_len;
+       int32_t ic;
+
+       matched_history_line = NULL;
+       read_key_buffer[0] = 0;
+       match_buf[0] = '\0';
+
+       /* Save and replace the prompt */
+       saved_prompt = cmdedit_prompt;
+       saved_prmt_len = cmdedit_prmt_len;
+       goto set_prompt;
+
+       while (1) {
+               int h;
+               unsigned match_buf_len = strlen(match_buf);
+
+               fflush_all();
+//FIXME: correct timeout?
+               ic = lineedit_read_key(read_key_buffer, -1);
+
+               switch (ic) {
+               case CTRL('R'): /* searching for the next match */
+                       break;
+
+               case '\b':
+               case '\x7f':
+                       /* Backspace */
+                       if (unicode_status == UNICODE_ON) {
+                               while (match_buf_len != 0) {
+                                       uint8_t c = match_buf[--match_buf_len];
+                                       if ((c & 0xc0) != 0x80) /* start of UTF-8 char? */
+                                               break; /* yes */
+                               }
+                       } else {
+                               if (match_buf_len != 0)
+                                       match_buf_len--;
+                       }
+                       match_buf[match_buf_len] = '\0';
+                       break;
+
+               default:
+                       if (ic < ' '
+                        || (!ENABLE_UNICODE_SUPPORT && ic >= 256)
+                        || (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT)
+                       ) {
+                               goto ret;
+                       }
+
+                       /* Append this char */
+#if ENABLE_UNICODE_SUPPORT
+                       if (unicode_status == UNICODE_ON) {
+                               mbstate_t mbstate = { 0 };
+                               char buf[MB_CUR_MAX + 1];
+                               int len = wcrtomb(buf, ic, &mbstate);
+                               if (len > 0) {
+                                       buf[len] = '\0';
+                                       if (match_buf_len + len < sizeof(match_buf))
+                                               strcpy(match_buf + match_buf_len, buf);
+                               }
+                       } else
+#endif
+                       if (match_buf_len < sizeof(match_buf) - 1) {
+                               match_buf[match_buf_len] = ic;
+                               match_buf[match_buf_len + 1] = '\0';
+                       }
+                       break;
+               } /* switch (ic) */
+
+               /* Search in history for match_buf */
+               h = state->cur_history;
+               if (ic == CTRL('R'))
+                       h--;
+               while (h >= 0) {
+                       if (state->history[h]) {
+                               char *match = strstr(state->history[h], match_buf);
+                               if (match) {
+                                       state->cur_history = h;
+                                       matched_history_line = state->history[h];
+                                       command_len = load_string(matched_history_line);
+                                       cursor = match - matched_history_line;
+//FIXME: cursor position for Unicode case
+
+                                       free((char*)cmdedit_prompt);
+ set_prompt:
+                                       cmdedit_prompt = xasprintf("(reverse-i-search)'%s': ", match_buf);
+                                       cmdedit_prmt_len = unicode_strwidth(cmdedit_prompt);
+                                       goto do_redraw;
+                               }
+                       }
+                       h--;
+               }
+
+               /* Not found */
+               match_buf[match_buf_len] = '\0';
+               beep();
+               continue;
+
+ do_redraw:
+               redraw(cmdedit_y, command_len - cursor);
+       } /* while (1) */
+
+ ret:
+       if (matched_history_line)
+               command_len = load_string(matched_history_line);
+
+       free((char*)cmdedit_prompt);
+       cmdedit_prompt = saved_prompt;
+       cmdedit_prmt_len = saved_prmt_len;
+       redraw(cmdedit_y, command_len - cursor);
+
+       return ic;
+}
+#endif
+
 /* maxsize must be >= 2.
  * Returns:
  * -1 on read errors or EOF, or on bare Ctrl-D,
  * 0  on ctrl-C (the line entered is still returned in 'command'),
  * >0 length of input string, including terminating '\n'
  */
-int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, line_input_t *st)
+int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize, int timeout)
 {
        int len;
 #if ENABLE_FEATURE_TAB_COMPLETION
-       smallint lastWasTab = FALSE;
+       smallint lastWasTab = 0;
 #endif
        smallint break_out = 0;
 #if ENABLE_FEATURE_EDITING_VI
@@ -1919,11 +2276,11 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
                maxsize = MAX_LINELEN;
        S.maxsize = maxsize;
 
-       /* With null flags, no other fields are ever used */
+       /* With zero flags, no other fields are ever used */
        state = st ? st : (line_input_t*) &const_int_0;
 #if MAX_HISTORY > 0
 # if ENABLE_FEATURE_EDITING_SAVEHISTORY
-       if ((state->flags & SAVE_HISTORY) && state->hist_file)
+       if (state->hist_file)
                if (state->cnt_history == 0)
                        load_history(state);
 # endif
@@ -1943,23 +2300,20 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
 #define command command_must_not_be_used
 
        new_settings = initial_settings;
-       new_settings.c_lflag &= ~ICANON;        /* unbuffered input */
-       /* Turn off echoing and CTRL-C, so we can trap it */
-       new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG);
-       /* Hmm, in linux c_cc[] is not parsed if ICANON is off */
+       /* ~ICANON: unbuffered input (most c_cc[] are disabled, VMIN/VTIME are enabled) */
+       /* ~ECHO, ~ECHONL: turn off echoing, including newline echoing */
+       /* ~ISIG: turn off INTR (ctrl-C), QUIT, SUSP */
+       new_settings.c_lflag &= ~(ICANON | ECHO | ECHONL | ISIG);
+       /* reads would block only if < 1 char is available */
        new_settings.c_cc[VMIN] = 1;
+       /* no timeout (reads block forever) */
        new_settings.c_cc[VTIME] = 0;
-       /* Turn off CTRL-C, so we can trap it */
-#ifndef _POSIX_VDISABLE
-# define _POSIX_VDISABLE '\0'
-#endif
-       new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
+       /* Should be not needed if ISIG is off: */
+       /* Turn off CTRL-C */
+       /* new_settings.c_cc[VINTR] = _POSIX_VDISABLE; */
        tcsetattr_stdin_TCSANOW(&new_settings);
 
-       /* Now initialize things */
-       previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
-       win_changed(0); /* do initial resizing */
-#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
+#if ENABLE_USERNAME_OR_HOMEDIR
        {
                struct passwd *entry;
 
@@ -1972,7 +2326,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
 #endif
 
 #if 0
-       for (i = 0; i <= MAX_HISTORY; i++)
+       for (i = 0; i <= state->max_history; i++)
                bb_error_msg("history[%d]:'%s'", i, state->history[i]);
        bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history);
 #endif
@@ -1981,6 +2335,11 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
        parse_and_put_prompt(prompt);
        ask_terminal();
 
+       /* Install window resize handler (NB: after *all* init is complete) */
+//FIXME: save entire sigaction!
+       previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
+       win_changed(0); /* get initial window size */
+
        read_key_buffer[0] = 0;
        while (1) {
                /*
@@ -1991,15 +2350,14 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
                 * clutters the big switch a bit, but keeps all the code
                 * in one place.
                 */
-               enum {
-                       VI_CMDMODE_BIT = 0x40000000,
-                       /* 0x80000000 bit flags KEYCODE_xxx */
-               };
                int32_t ic, ic_raw;
 
                fflush_all();
-               ic = ic_raw = lineedit_read_key(read_key_buffer);
+               ic = ic_raw = lineedit_read_key(read_key_buffer, timeout);
 
+#if ENABLE_FEATURE_REVERSE_SEARCH
+ again:
+#endif
 #if ENABLE_FEATURE_EDITING_VI
                newdelflag = 1;
                if (vi_cmdmode) {
@@ -2066,7 +2424,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
                case CTRL('L'):
                vi_case(CTRL('L')|VI_CMDMODE_BIT:)
                        /* Control-l -- clear screen */
-                       printf("\033[H"); /* cursor to top,left */
+                       printf(ESC"[H"); /* cursor to top,left */
                        redraw(0, command_len - cursor);
                        break;
 #if MAX_HISTORY > 0
@@ -2103,6 +2461,11 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
                        while (cursor > 0 && !BB_isspace(command_ps[cursor-1]))
                                input_backspace();
                        break;
+#if ENABLE_FEATURE_REVERSE_SEARCH
+               case CTRL('R'):
+                       ic = ic_raw = reverse_i_search();
+                       goto again;
+#endif
 
 #if ENABLE_FEATURE_EDITING_VI
                case 'i'|VI_CMDMODE_BIT:
@@ -2159,9 +2522,9 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
                case 'd'|VI_CMDMODE_BIT: {
                        int nc, sc;
 
-                       ic = lineedit_read_key(read_key_buffer);
+                       ic = lineedit_read_key(read_key_buffer, timeout);
                        if (errno) /* error */
-                               goto prepare_to_die;
+                               goto return_error_indicator;
                        if (ic == ic_raw) { /* "cc", "dd" */
                                input_backward(cursor);
                                goto clear_to_eol;
@@ -2223,9 +2586,9 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
                        break;
                case 'r'|VI_CMDMODE_BIT:
 //FIXME: unicode case?
-                       ic = lineedit_read_key(read_key_buffer);
+                       ic = lineedit_read_key(read_key_buffer, timeout);
                        if (errno) /* error */
-                               goto prepare_to_die;
+                               goto return_error_indicator;
                        if (ic < ' ' || ic > 255) {
                                beep();
                        } else {
@@ -2240,6 +2603,44 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
                                vi_cmdmode = 1;
                                input_backward(1);
                        }
+                       /* Handle a few ESC-<key> combinations the same way
+                        * standard readline bindings (IOW: bash) do.
+                        * Often, Alt-<key> generates ESC-<key>.
+                        */
+                       ic = lineedit_read_key(read_key_buffer, timeout);
+                       switch (ic) {
+                               //case KEYCODE_LEFT: - bash doesn't do this
+                               case 'b':
+                                       ctrl_left();
+                                       break;
+                               //case KEYCODE_RIGHT: - bash doesn't do this
+                               case 'f':
+                                       ctrl_right();
+                                       break;
+                               //case KEYCODE_DELETE: - bash doesn't do this
+                               case 'd':  /* Alt-D */
+                               {
+                                       /* Delete word forward */
+                                       int nc, sc = cursor;
+                                       ctrl_right();
+                                       nc = cursor - sc;
+                                       input_backward(nc);
+                                       while (--nc >= 0)
+                                               input_delete(1);
+                                       break;
+                               }
+                               case '\b':   /* Alt-Backspace(?) */
+                               case '\x7f': /* Alt-Backspace(?) */
+                               //case 'w': - bash doesn't do this
+                               {
+                                       /* Delete word backward */
+                                       int sc = cursor;
+                                       ctrl_left();
+                                       while (sc-- > cursor)
+                                               input_delete(1);
+                                       break;
+                               }
+                       }
                        break;
 #endif /* FEATURE_COMMAND_EDITING_VI */
 
@@ -2256,7 +2657,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
                        /* Rewrite the line with the selected history item */
                        /* change command */
                        command_len = load_string(state->history[state->cur_history] ?
-                                       state->history[state->cur_history] : "", maxsize);
+                                       state->history[state->cur_history] : "");
                        /* redraw and go to eol (bol, in vi) */
                        redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
                        break;
@@ -2268,9 +2669,11 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
                        input_backward(1);
                        break;
                case KEYCODE_CTRL_LEFT:
+               case KEYCODE_ALT_LEFT: /* bash doesn't do it */
                        ctrl_left();
                        break;
                case KEYCODE_CTRL_RIGHT:
+               case KEYCODE_ALT_RIGHT: /* bash doesn't do it */
                        ctrl_right();
                        break;
                case KEYCODE_HOME:
@@ -2297,9 +2700,9 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
                                 * or exit if len=0 and no chars to delete */
                                if (command_len == 0) {
                                        errno = 0;
-#if ENABLE_FEATURE_EDITING_VI
- prepare_to_die:
-#endif
+
+               case -1: /* error (e.g. EIO when tty is destroyed) */
+ IF_FEATURE_EDITING_VI(return_error_indicator:)
                                        break_out = command_len = -1;
                                        break;
                                }
@@ -2309,7 +2712,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
 //                     /* Control-V -- force insert of next char */
 //                     if (c == CTRL('V')) {
 //                             if (safe_read(STDIN_FILENO, &c, 1) < 1)
-//                                     goto prepare_to_die;
+//                                     goto return_error_indicator;
 //                             if (c == 0) {
 //                                     beep();
 //                                     break;
@@ -2362,7 +2765,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
 
 #if ENABLE_FEATURE_TAB_COMPLETION
                if (ic_raw != '\t')
-                       lastWasTab = FALSE;
+                       lastWasTab = 0;
 #endif
        } /* while (1) */
 
@@ -2381,7 +2784,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
        }
 #endif
 
-/* Stop bug catching using "command_must_not_be_used" trick */
+/* End of bug-catching "command_must_not_be_used" trick */
 #undef command
 
 #if ENABLE_UNICODE_SUPPORT
@@ -2391,8 +2794,9 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
        free(command_ps);
 #endif
 
-       if (command_len > 0)
+       if (command_len > 0) {
                remember_in_history(command);
+       }
 
        if (break_out > 0) {
                command[command_len++] = '\n';
@@ -2415,18 +2819,19 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
        return len; /* can't return command_len, DEINIT_S() destroys it */
 }
 
-#else
+#else  /* !FEATURE_EDITING */
 
 #undef read_line_input
 int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize)
 {
        fputs(prompt, stdout);
        fflush_all();
-       fgets(command, maxsize, stdin);
+       if (!fgets(command, maxsize, stdin))
+               return -1;
        return strlen(command);
 }
 
-#endif  /* FEATURE_EDITING */
+#endif  /* !FEATURE_EDITING */
 
 
 /*
@@ -2456,7 +2861,7 @@ int main(int argc, char **argv)
                l = read_line_input(prompt, buff);
                if (l <= 0 || buff[l-1] != '\n')
                        break;
-               buff[l-1] = 0;
+               buff[l-1] = '\0';
                printf("*** read_line_input() returned line =%s=\n", buff);
        }
        printf("*** read_line_input() detect ^D\n");
index 53716a2..dc45855 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2008 by Denys Vlasenko <vda.linux@googlemail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 struct lineedit_statics;
index 51b1ce6..032e9fa 100644 (file)
@@ -7,7 +7,7 @@
  * Copyright (C) 2005 Bernhard Reutner-Fischer
  * Copyright (C) 2006 Rob Landley <rob@landley.net>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -62,7 +62,7 @@ void FAST_FUNC llist_unlink(llist_t **head, llist_t *elm)
 
 /* Recursively free all elements in the linked list.  If freeit != NULL
  * call it on each datum in the list */
-void FAST_FUNC llist_free(llist_t *elm, void (*freeit) (void *data))
+void FAST_FUNC llist_free(llist_t *elm, void (*freeit)(void *data))
 {
        while (elm) {
                void *data = llist_pop(&elm);
index 740c588..8f080b7 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Optimize and correcting OCRNL by Vladimir Oleynik <dzo@simtreas.ru>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -16,7 +16,6 @@
 #define LOGIN " login: "
 
 static const char fmtstr_d[] ALIGN1 = "%A, %d %B %Y";
-static const char fmtstr_t[] ALIGN1 = "%H:%M:%S";
 
 void FAST_FUNC print_login_issue(const char *issue_file, const char *tty)
 {
@@ -30,7 +29,7 @@ void FAST_FUNC print_login_issue(const char *issue_file, const char *tty)
        time(&t);
        uname(&uts);
 
-       puts("\r");     /* start a new line */
+       puts("\r");  /* start a new line */
 
        fp = fopen_for_read(issue_file);
        if (!fp)
@@ -73,7 +72,7 @@ void FAST_FUNC print_login_issue(const char *issue_file, const char *tty)
                                strftime(buf, sizeof(buf), fmtstr_d, localtime(&t));
                                break;
                        case 't':
-                               strftime(buf, sizeof(buf), fmtstr_t, localtime(&t));
+                               strftime_HHMMSS(buf, sizeof(buf), &t);
                                break;
                        case 'l':
                                outbuf = tty;
index eb7016d..823fba0 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  * Copyright (C) 2005 by Rob Landley <rob@landley.net>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 #include <linux/version.h>
@@ -84,7 +84,7 @@ int FAST_FUNC del_loop(const char *device)
    search will re-use an existing loop device already bound to that
    file/offset if it finds one.
  */
-int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offset)
+int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offset, int ro)
 {
        char dev[LOOP_NAMESIZE];
        char *try;
@@ -93,11 +93,13 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse
        int i, dfd, ffd, mode, rc = -1;
 
        /* Open the file.  Barf if this doesn't work.  */
-       mode = O_RDWR;
+       mode = ro ? O_RDONLY : O_RDWR;
        ffd = open(file, mode);
        if (ffd < 0) {
-               mode = O_RDONLY;
-               ffd = open(file, mode);
+               if (mode != O_RDONLY) {
+                       mode = O_RDONLY;
+                       ffd = open(file, mode);
+               }
                if (ffd < 0)
                        return -errno;
        }
@@ -148,9 +150,9 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse
                        }
 
                /* If this block device already set up right, re-use it.
-                  (Yes this is racy, but associating two loop devices with the same
-                  file isn't pretty either.  In general, mounting the same file twice
-                  without using losetup manually is problematic.)
+                * (Yes this is racy, but associating two loop devices with the same
+                * file isn't pretty either.  In general, mounting the same file twice
+                * without using losetup manually is problematic.)
                 */
                } else
                if (strcmp(file, (char *)loopinfo.lo_file_name) != 0
index 4486eb1..7826b90 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* Mar 5, 2003    Manuel Novoa III
@@ -44,7 +44,7 @@ int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
        while (1) {
                c = '\0';
 
-               if (flags & FILEUTILS_RECUR) {  /* Get the parent */
+               if (flags & FILEUTILS_RECUR) {  /* Get the parent */
                        /* Bypass leading non-'/'s and then subsequent '/'s */
                        while (*s) {
                                if (*s == '/') {
@@ -86,7 +86,7 @@ int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
                if (mkdir(path, 0777) < 0) {
                        /* If we failed for any other reason than the directory
                         * already exists, output a diagnostic and return -1 */
-                       if (errno != EEXIST
+                       if ((errno != EEXIST && errno != EISDIR)
                         || !(flags & FILEUTILS_RECUR)
                         || ((stat(path, &st) < 0) || !S_ISDIR(st.st_mode))
                        ) {
@@ -107,6 +107,10 @@ int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
                         * an error. */
                        if ((mode != -1) && (chmod(path, mode) < 0)) {
                                fail_msg = "set permissions of";
+                               if (flags & FILEUTILS_IGNORE_CHMOD_ERR) {
+                                       flags = 0;
+                                       goto print_err;
+                               }
                                break;
                        }
                        goto ret0;
@@ -116,8 +120,9 @@ int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
                *s = c;
        } /* while (1) */
 
-       bb_perror_msg("can't %s directory '%s'", fail_msg, path);
        flags = -1;
+ print_err:
+       bb_perror_msg("can't %s directory '%s'", fail_msg, path);
        goto ret;
  ret0:
        flags = 0;
index ca71fdb..06c4039 100644 (file)
@@ -3,21 +3,28 @@
  *
  * Copyright (C) 2006 Denys Vlasenko
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /* We do not include libbb.h - #define makedev() is there! */
 #include "platform.h"
-#include <features.h>
-#include <sys/sysmacros.h>
+
+/* Different Unixes want different headers for makedev */
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
+ || defined(__APPLE__)
+# include <sys/types.h>
+#else
+# include <features.h>
+# include <sys/sysmacros.h>
+#endif
 
 #ifdef __GLIBC__
-/* At least glibc has horrendously large inline for this, so wrap it */
+/* At least glibc has horrendously large inline for this, so wrap it. */
 /* uclibc people please check - do we need "&& !__UCLIBC__" above? */
 
-/* suppress gcc "no previous prototype" warning */
-unsigned long long FAST_FUNC bb_makedev(unsigned int major, unsigned int minor);
-unsigned long long FAST_FUNC bb_makedev(unsigned int major, unsigned int minor)
+/* Suppress gcc "no previous prototype" warning */
+unsigned long long FAST_FUNC bb_makedev(unsigned major, unsigned minor);
+unsigned long long FAST_FUNC bb_makedev(unsigned major, unsigned minor)
 {
        return makedev(major, minor);
 }
index 9360e75..32c3d7f 100644 (file)
@@ -7,11 +7,13 @@
  *
  * Returns 1 for a match, otherwise 0
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 
+#ifdef HAVE_MNTENT_H
+
 int FAST_FUNC match_fstype(const struct mntent *mt, const char *t_fstype)
 {
        int match = 1;
@@ -40,3 +42,5 @@ int FAST_FUNC match_fstype(const struct mntent *mt, const char *t_fstype)
 
        return !match;
 }
+
+#endif /* HAVE_MNTENT_H */
diff --git a/libbb/md5.c b/libbb/md5.c
deleted file mode 100644 (file)
index a98631d..0000000
+++ /dev/null
@@ -1,427 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- *  md5.c - Compute MD5 checksum of strings according to the
- *          definition of MD5 in RFC 1321 from April 1992.
- *
- *  Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
- *
- *  Copyright (C) 1995-1999 Free Software Foundation, Inc.
- *  Copyright (C) 2001 Manuel Novoa III
- *  Copyright (C) 2003 Glenn L. McGrath
- *  Copyright (C) 2003 Erik Andersen
- *
- *  Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
- */
-
-#include "libbb.h"
-
-/* 0: fastest, 3: smallest */
-#if CONFIG_MD5_SIZE_VS_SPEED < 0
-# define MD5_SIZE_VS_SPEED 0
-#elif CONFIG_MD5_SIZE_VS_SPEED > 3
-# define MD5_SIZE_VS_SPEED 3
-#else
-# define MD5_SIZE_VS_SPEED CONFIG_MD5_SIZE_VS_SPEED
-#endif
-
-/* Initialize structure containing state of computation.
- * (RFC 1321, 3.3: Step 3)
- */
-void FAST_FUNC md5_begin(md5_ctx_t *ctx)
-{
-       ctx->A = 0x67452301;
-       ctx->B = 0xefcdab89;
-       ctx->C = 0x98badcfe;
-       ctx->D = 0x10325476;
-       ctx->total = 0;
-       ctx->buflen = 0;
-}
-
-/* These are the four functions used in the four steps of the MD5 algorithm
- * and defined in the RFC 1321.  The first function is a little bit optimized
- * (as found in Colin Plumbs public domain implementation).
- * #define FF(b, c, d) ((b & c) | (~b & d))
- */
-#define FF(b, c, d) (d ^ (b & (c ^ d)))
-#define FG(b, c, d) FF(d, b, c)
-#define FH(b, c, d) (b ^ c ^ d)
-#define FI(b, c, d) (c ^ (b | ~d))
-
-#define rotl32(w, s) (((w) << (s)) | ((w) >> (32 - (s))))
-
-/* Hash a single block, 64 bytes long and 4-byte aligned. */
-static void md5_hash_block(const void *buffer, md5_ctx_t *ctx)
-{
-       uint32_t correct_words[16];
-       const uint32_t *words = buffer;
-
-#if MD5_SIZE_VS_SPEED > 0
-       static const uint32_t C_array[] = {
-               /* round 1 */
-               0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
-               0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
-               0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
-               0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
-               /* round 2 */
-               0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
-               0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8,
-               0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
-               0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
-               /* round 3 */
-               0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
-               0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
-               0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
-               0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
-               /* round 4 */
-               0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
-               0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
-               0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
-               0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
-       };
-       static const char P_array[] ALIGN1 = {
-# if MD5_SIZE_VS_SPEED > 1
-               0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,   /* 1 */
-# endif
-               1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12,   /* 2 */
-               5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2,   /* 3 */
-               0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9    /* 4 */
-       };
-# if MD5_SIZE_VS_SPEED > 1
-       static const char S_array[] ALIGN1 = {
-               7, 12, 17, 22,
-               5, 9, 14, 20,
-               4, 11, 16, 23,
-               6, 10, 15, 21
-       };
-# endif        /* MD5_SIZE_VS_SPEED > 1 */
-#endif
-       uint32_t A = ctx->A;
-       uint32_t B = ctx->B;
-       uint32_t C = ctx->C;
-       uint32_t D = ctx->D;
-
-       /* Process all bytes in the buffer with 64 bytes in each round of
-          the loop.  */
-       uint32_t *cwp = correct_words;
-       uint32_t A_save = A;
-       uint32_t B_save = B;
-       uint32_t C_save = C;
-       uint32_t D_save = D;
-
-#if MD5_SIZE_VS_SPEED > 1
-       const uint32_t *pc;
-       const char *pp;
-       const char *ps;
-       int i;
-       uint32_t temp;
-
-       for (i = 0; i < 16; i++)
-               cwp[i] = SWAP_LE32(words[i]);
-       words += 16;
-
-# if MD5_SIZE_VS_SPEED > 2
-       pc = C_array;
-       pp = P_array;
-       ps = S_array - 4;
-
-       for (i = 0; i < 64; i++) {
-               if ((i & 0x0f) == 0)
-                       ps += 4;
-               temp = A;
-               switch (i >> 4) {
-               case 0:
-                       temp += FF(B, C, D);
-                       break;
-               case 1:
-                       temp += FG(B, C, D);
-                       break;
-               case 2:
-                       temp += FH(B, C, D);
-                       break;
-               case 3:
-                       temp += FI(B, C, D);
-               }
-               temp += cwp[(int) (*pp++)] + *pc++;
-               temp = rotl32(temp, ps[i & 3]);
-               temp += B;
-               A = D;
-               D = C;
-               C = B;
-               B = temp;
-       }
-# else
-       pc = C_array;
-       pp = P_array;
-       ps = S_array;
-
-       for (i = 0; i < 16; i++) {
-               temp = A + FF(B, C, D) + cwp[(int) (*pp++)] + *pc++;
-               temp = rotl32(temp, ps[i & 3]);
-               temp += B;
-               A = D;
-               D = C;
-               C = B;
-               B = temp;
-       }
-       ps += 4;
-       for (i = 0; i < 16; i++) {
-               temp = A + FG(B, C, D) + cwp[(int) (*pp++)] + *pc++;
-               temp = rotl32(temp, ps[i & 3]);
-               temp += B;
-               A = D;
-               D = C;
-               C = B;
-               B = temp;
-       }
-       ps += 4;
-       for (i = 0; i < 16; i++) {
-               temp = A + FH(B, C, D) + cwp[(int) (*pp++)] + *pc++;
-               temp = rotl32(temp, ps[i & 3]);
-               temp += B;
-               A = D;
-               D = C;
-               C = B;
-               B = temp;
-       }
-       ps += 4;
-       for (i = 0; i < 16; i++) {
-               temp = A + FI(B, C, D) + cwp[(int) (*pp++)] + *pc++;
-               temp = rotl32(temp, ps[i & 3]);
-               temp += B;
-               A = D;
-               D = C;
-               C = B;
-               B = temp;
-       }
-
-# endif /* MD5_SIZE_VS_SPEED > 2 */
-#else
-       /* First round: using the given function, the context and a constant
-          the next context is computed.  Because the algorithms processing
-          unit is a 32-bit word and it is determined to work on words in
-          little endian byte order we perhaps have to change the byte order
-          before the computation.  To reduce the work for the next steps
-          we store the swapped words in the array CORRECT_WORDS.  */
-# define OP(a, b, c, d, s, T) \
-       do { \
-               a += FF(b, c, d) + (*cwp++ = SWAP_LE32(*words)) + T; \
-               ++words; \
-               a = rotl32(a, s); \
-               a += b; \
-       } while (0)
-
-       /* Before we start, one word to the strange constants.
-          They are defined in RFC 1321 as
-          T[i] = (int)(4294967296.0 * fabs(sin(i))), i=1..64
-        */
-
-# if MD5_SIZE_VS_SPEED == 1
-       const uint32_t *pc;
-       const char *pp;
-       int i;
-# endif        /* MD5_SIZE_VS_SPEED */
-
-       /* Round 1.  */
-# if MD5_SIZE_VS_SPEED == 1
-       pc = C_array;
-       for (i = 0; i < 4; i++) {
-               OP(A, B, C, D, 7, *pc++);
-               OP(D, A, B, C, 12, *pc++);
-               OP(C, D, A, B, 17, *pc++);
-               OP(B, C, D, A, 22, *pc++);
-       }
-# else
-       OP(A, B, C, D, 7, 0xd76aa478);
-       OP(D, A, B, C, 12, 0xe8c7b756);
-       OP(C, D, A, B, 17, 0x242070db);
-       OP(B, C, D, A, 22, 0xc1bdceee);
-       OP(A, B, C, D, 7, 0xf57c0faf);
-       OP(D, A, B, C, 12, 0x4787c62a);
-       OP(C, D, A, B, 17, 0xa8304613);
-       OP(B, C, D, A, 22, 0xfd469501);
-       OP(A, B, C, D, 7, 0x698098d8);
-       OP(D, A, B, C, 12, 0x8b44f7af);
-       OP(C, D, A, B, 17, 0xffff5bb1);
-       OP(B, C, D, A, 22, 0x895cd7be);
-       OP(A, B, C, D, 7, 0x6b901122);
-       OP(D, A, B, C, 12, 0xfd987193);
-       OP(C, D, A, B, 17, 0xa679438e);
-       OP(B, C, D, A, 22, 0x49b40821);
-# endif /* MD5_SIZE_VS_SPEED == 1 */
-
-       /* For the second to fourth round we have the possibly swapped words
-          in CORRECT_WORDS.  Redefine the macro to take an additional first
-          argument specifying the function to use.  */
-# undef OP
-# define OP(f, a, b, c, d, k, s, T) \
-       do { \
-               a += f(b, c, d) + correct_words[k] + T; \
-               a = rotl32(a, s); \
-               a += b; \
-       } while (0)
-
-       /* Round 2.  */
-# if MD5_SIZE_VS_SPEED == 1
-       pp = P_array;
-       for (i = 0; i < 4; i++) {
-               OP(FG, A, B, C, D, (int) (*pp++), 5, *pc++);
-               OP(FG, D, A, B, C, (int) (*pp++), 9, *pc++);
-               OP(FG, C, D, A, B, (int) (*pp++), 14, *pc++);
-               OP(FG, B, C, D, A, (int) (*pp++), 20, *pc++);
-       }
-# else
-       OP(FG, A, B, C, D, 1, 5, 0xf61e2562);
-       OP(FG, D, A, B, C, 6, 9, 0xc040b340);
-       OP(FG, C, D, A, B, 11, 14, 0x265e5a51);
-       OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
-       OP(FG, A, B, C, D, 5, 5, 0xd62f105d);
-       OP(FG, D, A, B, C, 10, 9, 0x02441453);
-       OP(FG, C, D, A, B, 15, 14, 0xd8a1e681);
-       OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
-       OP(FG, A, B, C, D, 9, 5, 0x21e1cde6);
-       OP(FG, D, A, B, C, 14, 9, 0xc33707d6);
-       OP(FG, C, D, A, B, 3, 14, 0xf4d50d87);
-       OP(FG, B, C, D, A, 8, 20, 0x455a14ed);
-       OP(FG, A, B, C, D, 13, 5, 0xa9e3e905);
-       OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8);
-       OP(FG, C, D, A, B, 7, 14, 0x676f02d9);
-       OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
-# endif /* MD5_SIZE_VS_SPEED == 1 */
-
-       /* Round 3.  */
-# if MD5_SIZE_VS_SPEED == 1
-       for (i = 0; i < 4; i++) {
-               OP(FH, A, B, C, D, (int) (*pp++), 4, *pc++);
-               OP(FH, D, A, B, C, (int) (*pp++), 11, *pc++);
-               OP(FH, C, D, A, B, (int) (*pp++), 16, *pc++);
-               OP(FH, B, C, D, A, (int) (*pp++), 23, *pc++);
-       }
-# else
-       OP(FH, A, B, C, D, 5, 4, 0xfffa3942);
-       OP(FH, D, A, B, C, 8, 11, 0x8771f681);
-       OP(FH, C, D, A, B, 11, 16, 0x6d9d6122);
-       OP(FH, B, C, D, A, 14, 23, 0xfde5380c);
-       OP(FH, A, B, C, D, 1, 4, 0xa4beea44);
-       OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9);
-       OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60);
-       OP(FH, B, C, D, A, 10, 23, 0xbebfbc70);
-       OP(FH, A, B, C, D, 13, 4, 0x289b7ec6);
-       OP(FH, D, A, B, C, 0, 11, 0xeaa127fa);
-       OP(FH, C, D, A, B, 3, 16, 0xd4ef3085);
-       OP(FH, B, C, D, A, 6, 23, 0x04881d05);
-       OP(FH, A, B, C, D, 9, 4, 0xd9d4d039);
-       OP(FH, D, A, B, C, 12, 11, 0xe6db99e5);
-       OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8);
-       OP(FH, B, C, D, A, 2, 23, 0xc4ac5665);
-# endif /* MD5_SIZE_VS_SPEED == 1 */
-
-       /* Round 4.  */
-# if MD5_SIZE_VS_SPEED == 1
-       for (i = 0; i < 4; i++) {
-               OP(FI, A, B, C, D, (int) (*pp++), 6, *pc++);
-               OP(FI, D, A, B, C, (int) (*pp++), 10, *pc++);
-               OP(FI, C, D, A, B, (int) (*pp++), 15, *pc++);
-               OP(FI, B, C, D, A, (int) (*pp++), 21, *pc++);
-       }
-# else
-       OP(FI, A, B, C, D, 0, 6, 0xf4292244);
-       OP(FI, D, A, B, C, 7, 10, 0x432aff97);
-       OP(FI, C, D, A, B, 14, 15, 0xab9423a7);
-       OP(FI, B, C, D, A, 5, 21, 0xfc93a039);
-       OP(FI, A, B, C, D, 12, 6, 0x655b59c3);
-       OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92);
-       OP(FI, C, D, A, B, 10, 15, 0xffeff47d);
-       OP(FI, B, C, D, A, 1, 21, 0x85845dd1);
-       OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f);
-       OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
-       OP(FI, C, D, A, B, 6, 15, 0xa3014314);
-       OP(FI, B, C, D, A, 13, 21, 0x4e0811a1);
-       OP(FI, A, B, C, D, 4, 6, 0xf7537e82);
-       OP(FI, D, A, B, C, 11, 10, 0xbd3af235);
-       OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
-       OP(FI, B, C, D, A, 9, 21, 0xeb86d391);
-# endif        /* MD5_SIZE_VS_SPEED == 1 */
-#endif /* MD5_SIZE_VS_SPEED > 1 */
-
-       /* Add the starting values of the context.  */
-       A += A_save;
-       B += B_save;
-       C += C_save;
-       D += D_save;
-
-       /* Put checksum in context given as argument.  */
-       ctx->A = A;
-       ctx->B = B;
-       ctx->C = C;
-       ctx->D = D;
-}
-
-/* Feed data through a temporary buffer to call md5_hash_aligned_block()
- * with chunks of data that are 4-byte aligned and a multiple of 64 bytes.
- * This function's internal buffer remembers previous data until it has 64
- * bytes worth to pass on.  Call md5_end() to flush this buffer. */
-void FAST_FUNC md5_hash(const void *buffer, size_t len, md5_ctx_t *ctx)
-{
-       char *buf = (char *)buffer;
-
-       /* RFC 1321 specifies the possible length of the file up to 2^64 bits,
-        * Here we only track the number of bytes.  */
-       ctx->total += len;
-
-       /* Process all input. */
-       while (len) {
-               unsigned i = 64 - ctx->buflen;
-
-               /* Copy data into aligned buffer. */
-               if (i > len)
-                       i = len;
-               memcpy(ctx->buffer + ctx->buflen, buf, i);
-               len -= i;
-               ctx->buflen += i;
-               buf += i;
-
-               /* When buffer fills up, process it. */
-               if (ctx->buflen == 64) {
-                       md5_hash_block(ctx->buffer, ctx);
-                       ctx->buflen = 0;
-               }
-       }
-}
-
-/* Process the remaining bytes in the buffer and put result from CTX
- * in first 16 bytes following RESBUF.  The result is always in little
- * endian byte order, so that a byte-wise output yields to the wanted
- * ASCII representation of the message digest.
- */
-void FAST_FUNC md5_end(void *resbuf, md5_ctx_t *ctx)
-{
-       char *buf = ctx->buffer;
-       int i;
-
-       /* Pad data to block size.  */
-       buf[ctx->buflen++] = 0x80;
-       memset(buf + ctx->buflen, 0, 128 - ctx->buflen);
-
-       /* Put the 64-bit file length in *bits* at the end of the buffer.  */
-       ctx->total <<= 3;
-       if (ctx->buflen > 56)
-               buf += 64;
-       for (i = 0; i < 8; i++)
-               buf[56 + i] = ctx->total >> (i*8);
-
-       /* Process last bytes.  */
-       if (buf != ctx->buffer)
-               md5_hash_block(ctx->buffer, ctx);
-       md5_hash_block(buf, ctx);
-
-       /* The MD5 result is in little endian byte order.
-        * We (ab)use the fact that A-D are consecutive in memory.
-        */
-#if BB_BIG_ENDIAN
-       ctx->A = SWAP_LE32(ctx->A);
-       ctx->B = SWAP_LE32(ctx->B);
-       ctx->C = SWAP_LE32(ctx->C);
-       ctx->D = SWAP_LE32(ctx->D);
-#endif
-       memcpy(resbuf, &ctx->A, sizeof(ctx->A) * 4);
-}
index 1d0e587..fad82c9 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 const char bb_banner[] ALIGN1 = BANNER;
 
 
-const char bb_msg_memory_exhausted[] ALIGN1 = "memory exhausted";
+const char bb_msg_memory_exhausted[] ALIGN1 = "out of memory";
 const char bb_msg_invalid_date[] ALIGN1 = "invalid date '%s'";
-const char bb_msg_write_error[] ALIGN1 = "write error";
-const char bb_msg_read_error[] ALIGN1 = "read error";
 const char bb_msg_unknown[] ALIGN1 = "(unknown)";
 const char bb_msg_can_not_create_raw_socket[] ALIGN1 = "can't create raw socket";
 const char bb_msg_perm_denied_are_you_root[] ALIGN1 = "permission denied (are you root?)";
@@ -35,15 +33,8 @@ const char bb_msg_invalid_arg[] ALIGN1 = "invalid argument '%s' to '%s'";
 const char bb_msg_standard_input[] ALIGN1 = "standard input";
 const char bb_msg_standard_output[] ALIGN1 = "standard output";
 
-const char bb_str_default[] ALIGN1 = "default";
 const char bb_hexdigits_upcase[] ALIGN1 = "0123456789ABCDEF";
 
-const char bb_path_passwd_file[] ALIGN1 = "/etc/passwd";
-const char bb_path_shadow_file[] ALIGN1 = "/etc/shadow";
-const char bb_path_group_file[] ALIGN1 = "/etc/group";
-const char bb_path_gshadow_file[] ALIGN1 = "/etc/gshadow";
-const char bb_path_motd_file[] ALIGN1 = "/etc/motd";
-const char bb_dev_null[] ALIGN1 = "/dev/null";
 const char bb_busybox_exec_path[] ALIGN1 = CONFIG_BUSYBOX_EXEC_PATH;
 const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL;
 /* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin,
@@ -57,15 +48,16 @@ const int const_int_1 = 1;
  * and it will end up in bss */
 const int const_int_0 = 0;
 
-#include <utmp.h>
+#if ENABLE_FEATURE_WTMP
 /* This is usually something like "/var/adm/wtmp" or "/var/log/wtmp" */
 const char bb_path_wtmp_file[] ALIGN1 =
-#if defined _PATH_WTMP
+# if defined _PATH_WTMP
        _PATH_WTMP;
-#elif defined WTMP_FILE
+# elif defined WTMP_FILE
        WTMP_FILE;
-#else
-#error unknown path to wtmp file
+# else
+#  error unknown path to wtmp file
+# endif
 #endif
 
 /* We use it for "global" data via *(struct global*)&bb_common_bufsiz1.
diff --git a/libbb/missing_syscalls.c b/libbb/missing_syscalls.c
new file mode 100644 (file)
index 0000000..dd430e3
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2012, Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//kbuild:lib-y += missing_syscalls.o
+
+/*#include <linux/timex.h> - for struct timex, but may collide with <time.h> */
+#include <sys/syscall.h>
+#include "libbb.h"
+
+#if defined(ANDROID) || defined(__ANDROID__)
+pid_t getsid(pid_t pid)
+{
+       return syscall(__NR_getsid, pid);
+}
+
+int stime(const time_t *t)
+{
+       struct timeval tv;
+       tv.tv_sec = *t;
+       tv.tv_usec = 0;
+       return settimeofday(&tv, NULL);
+}
+
+int sethostname(const char *name, size_t len)
+{
+       return syscall(__NR_sethostname, name, len);
+}
+
+struct timex;
+int adjtimex(struct timex *buf)
+{
+       return syscall(__NR_adjtimex, buf);
+}
+
+int pivot_root(const char *new_root, const char *put_old)
+{
+       return syscall(__NR_pivot_root, new_root, put_old);
+}
+#endif
index 7d4e514..f1afe7d 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* Aug 13, 2003
index 586a661..22bff64 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include <mntent.h>
diff --git a/libbb/mtab_file.c b/libbb/mtab_file.c
deleted file mode 100644 (file)
index c9d9a69..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * Utility routines.
- *
- * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
- *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
- */
-
-#include "libbb.h"
-
-/* Busybox mount uses either /proc/mounts or /etc/mtab to
- * get the list of currently mounted filesystems */
-const char bb_path_mtab_file[] ALIGN1 =
-IF_FEATURE_MTAB_SUPPORT("/etc/mtab")IF_NOT_FEATURE_MTAB_SUPPORT("/proc/mounts");
diff --git a/libbb/nuke_str.c b/libbb/nuke_str.c
new file mode 100644 (file)
index 0000000..56b808b
--- /dev/null
@@ -0,0 +1,21 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2008 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//kbuild:lib-y += nuke_str.o
+
+#include "libbb.h"
+
+void FAST_FUNC nuke_str(char *str)
+{
+        if (str) {
+               while (*str)
+                       *str++ = 0;
+               /* or: memset(str, 0, strlen(str)); - not as small as above */
+       }
+}
index 19b8752..9ecc1f6 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2006 Tito Ragusa <farmatito@tiscali.it>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*     A good password:
@@ -45,53 +45,59 @@ static int string_checker_helper(const char *p1, const char *p2) __attribute__ (
 
 static int string_checker_helper(const char *p1, const char *p2)
 {
-       /* as-is or capitalized */
-       if (strcasecmp(p1, p2) == 0
        /* as sub-string */
-       || strcasestr(p2, p1) != NULL
+       if (strcasestr(p2, p1) != NULL
        /* invert in case haystack is shorter than needle */
-       || strcasestr(p1, p2) != NULL)
+        || strcasestr(p1, p2) != NULL
+       /* as-is or capitalized */
+       /* || strcasecmp(p1, p2) == 0 - 1st strcasestr should catch this too */
+       ) {
                return 1;
+       }
        return 0;
 }
 
 static int string_checker(const char *p1, const char *p2)
 {
-       int size;
+       int size, i;
        /* check string */
        int ret = string_checker_helper(p1, p2);
-       /* Make our own copy */
+       /* make our own copy */
        char *p = xstrdup(p1);
-       /* reverse string */
-       size = strlen(p);
 
-       while (size--) {
-               *p = p1[size];
-               p++;
+       /* reverse string */
+       i = size = strlen(p1);
+       while (--i >= 0) {
+               *p++ = p1[i];
        }
-       /* restore pointer */
-       p -= strlen(p1);
+       p -= size; /* restore pointer */
+
        /* check reversed string */
        ret |= string_checker_helper(p, p2);
+
        /* clean up */
-       memset(p, 0, strlen(p1));
+       memset(p, 0, size);
        free(p);
+
        return ret;
 }
 
-#define LOWERCASE          1
-#define UPPERCASE          2
-#define NUMBERS            4
-#define SPECIAL            8
+#define CATEGORIES  4
+
+#define LOWERCASE   1
+#define UPPERCASE   2
+#define NUMBERS     4
+#define SPECIAL     8
+
+#define LAST_CAT    8
 
 static const char *obscure_msg(const char *old_p, const char *new_p, const struct passwd *pw)
 {
-       int i;
-       int c;
-       int length;
-       int mixed = 0;
-       /* Add 2 for each type of characters to the minlen of password */
-       int size = CONFIG_PASSWORD_MINLEN + 8;
+       unsigned length;
+       unsigned size;
+       unsigned mixed;
+       unsigned c;
+       unsigned i;
        const char *p;
        char *hostname;
 
@@ -103,10 +109,12 @@ static const char *obscure_msg(const char *old_p, const char *new_p, const struc
        if (string_checker(new_p, pw->pw_name)) {
                return "similar to username";
        }
+#ifndef __BIONIC__
        /* no gecos as-is, as sub-string, reversed, capitalized, doubled */
-       if (*pw->pw_gecos && string_checker(new_p, pw->pw_gecos)) {
+       if (pw->pw_gecos[0] && string_checker(new_p, pw->pw_gecos)) {
                return "similar to gecos";
        }
+#endif
        /* hostname as-is, as sub-string, reversed, capitalized, doubled */
        hostname = safe_gethostname();
        i = string_checker(new_p, hostname);
@@ -115,6 +123,7 @@ static const char *obscure_msg(const char *old_p, const char *new_p, const struc
                return "similar to hostname";
 
        /* Should / Must contain a mix of: */
+       mixed = 0;
        for (i = 0; i < length; i++) {
                if (islower(new_p[i])) {        /* a-z */
                        mixed |= LOWERCASE;
@@ -125,7 +134,7 @@ static const char *obscure_msg(const char *old_p, const char *new_p, const struc
                } else  {                       /* special characters */
                        mixed |= SPECIAL;
                }
-               /* More than 50% similar characters ? */
+               /* Count i'th char */
                c = 0;
                p = new_p;
                while (1) {
@@ -134,26 +143,31 @@ static const char *obscure_msg(const char *old_p, const char *new_p, const struc
                                break;
                        }
                        c++;
-                       if (!++p) {
-                               break; /* move past the matched char if possible */
+                       p++;
+                       if (!*p) {
+                               break;
                        }
                }
-
-               if (c >= (length / 2)) {
+               /* More than 50% similar characters ? */
+               if (c*2 >= length) {
                        return "too many similar characters";
                }
        }
-       for (i=0; i<4; i++)
-               if (mixed & (1<<i)) size -= 2;
+
+       size = CONFIG_PASSWORD_MINLEN + 2*CATEGORIES;
+       for (i = 1; i <= LAST_CAT; i <<= 1)
+               if (mixed & i)
+                       size -= 2;
        if (length < size)
                return "too weak";
 
-       if (old_p && old_p[0] != '\0') {
+       if (old_p && old_p[0]) {
                /* check vs. old password */
                if (string_checker(new_p, old_p)) {
                        return "similar to old password";
                }
        }
+
        return NULL;
 }
 
index b7c3a00..1590d9a 100644 (file)
@@ -4,10 +4,32 @@
  *
  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  * Also for use in uClibc (http://uclibc.org/) licensed under LGPLv2.1 or later.
  */
 
+/* Uncomment to enable test applet */
+////config:config PARSE
+////config:    bool "Uniform config file parser debugging applet: parse"
+////config:    default n
+////config:    help
+////config:      Typical usage of parse API:
+////config:            char *t[3];
+////config:            parser_t *p = config_open(filename);
+////config:            while (config_read(p, t, 3, 0, delimiters, flags)) { // 1..3 tokens
+////config:                    bb_error_msg("TOKENS: '%s''%s''%s'", t[0], t[1], t[2]);
+////config:            }
+////config:            config_close(p);
+
+////applet:IF_PARSE(APPLET(parse, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-y += parse_config.o
+
+//usage:#define parse_trivial_usage
+//usage:       "[-x] [-n MAXTOKENS] [-m MINTOKENS] [-d DELIMS] [-f FLAGS] FILE..."
+//usage:#define parse_full_usage "\n\n"
+//usage:       "       -x      Suppress output (for benchmarking)"
+
 #include "libbb.h"
 
 #if defined ENABLE_PARSE && ENABLE_PARSE
@@ -15,52 +37,34 @@ int parse_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int parse_main(int argc UNUSED_PARAM, char **argv)
 {
        const char *delims = "# \t";
+       char **t;
        unsigned flags = PARSE_NORMAL;
        int mintokens = 0, ntokens = 128;
+       unsigned noout;
 
        opt_complementary = "-1:n+:m+:f+";
-       getopt32(argv, "n:m:d:f:", &ntokens, &mintokens, &delims, &flags);
+       noout = 1 & getopt32(argv, "xn:m:d:f:", &ntokens, &mintokens, &delims, &flags);
        //argc -= optind;
        argv += optind;
+
+       t = xmalloc(sizeof(t[0]) * ntokens);
        while (*argv) {
+               int n;
                parser_t *p = config_open(*argv);
-               if (p) {
-                       int n;
-                       char **t = xmalloc(sizeof(char *) * ntokens);
-                       while ((n = config_read(p, t, ntokens, mintokens, delims, flags)) != 0) {
+               while ((n = config_read(p, t, ntokens, mintokens, delims, flags)) != 0) {
+                       if (!noout) {
                                for (int i = 0; i < n; ++i)
                                        printf("[%s]", t[i]);
                                puts("");
                        }
-                       config_close(p);
                }
+               config_close(p);
                argv++;
        }
        return EXIT_SUCCESS;
 }
 #endif
 
-/*
-
-Typical usage:
-
------ CUT -----
-       char *t[3];     // tokens placeholder
-       parser_t *p = config_open(filename);
-       if (p) {
-               // parse line-by-line
-               while (config_read(p, t, 3, 0, delimiters, flags)) { // 1..3 tokens
-                       // use tokens
-                       bb_error_msg("TOKENS: [%s][%s][%s]", t[0], t[1], t[2]);
-               }
-               ...
-               // free parser
-               config_close(p);
-       }
------ CUT -----
-
-*/
-
 parser_t* FAST_FUNC config_open2(const char *filename, FILE* FAST_FUNC (*fopen_func)(const char *path))
 {
        FILE* fp;
@@ -79,25 +83,58 @@ parser_t* FAST_FUNC config_open(const char *filename)
        return config_open2(filename, fopen_or_warn_stdin);
 }
 
-static void config_free_data(parser_t *parser)
-{
-       free(parser->line);
-       parser->line = NULL;
-       if (PARSE_KEEP_COPY) { /* compile-time constant */
-               free(parser->data);
-               parser->data = NULL;
-       }
-}
-
 void FAST_FUNC config_close(parser_t *parser)
 {
        if (parser) {
-               config_free_data(parser);
+               if (PARSE_KEEP_COPY) /* compile-time constant */
+                       free(parser->data);
                fclose(parser->fp);
+               free(parser->line);
+               free(parser->nline);
                free(parser);
        }
 }
 
+/* This function reads an entire line from a text file,
+ * up to a newline, exclusive.
+ * Trailing '\' is recognized as line continuation.
+ * Returns -1 if EOF/error.
+ */
+static int get_line_with_continuation(parser_t *parser)
+{
+       ssize_t len, nlen;
+       char *line;
+
+       len = getline(&parser->line, &parser->line_alloc, parser->fp);
+       if (len <= 0)
+               return len;
+
+       line = parser->line;
+       for (;;) {
+               parser->lineno++;
+               if (line[len - 1] == '\n')
+                       len--;
+               if (len == 0 || line[len - 1] != '\\')
+                       break;
+               len--;
+
+               nlen = getline(&parser->nline, &parser->nline_alloc, parser->fp);
+               if (nlen <= 0)
+                       break;
+
+               if (parser->line_alloc < len + nlen + 1) {
+                       parser->line_alloc = len + nlen + 1;
+                       line = parser->line = xrealloc(line, parser->line_alloc);
+               }
+               memcpy(&line[len], parser->nline, nlen);
+               len += nlen;
+       }
+
+       line[len] = '\0';
+       return len;
+}
+
+
 /*
 0. If parser is NULL return 0.
 1. Read a line from config file. If nothing to read then return 0.
@@ -126,27 +163,22 @@ int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const
 {
        char *line;
        int ntokens, mintokens;
-       int t, len;
+       int t;
+
+       if (!parser)
+               return 0;
 
        ntokens = (uint8_t)flags;
        mintokens = (uint8_t)(flags >> 8);
 
-       if (parser == NULL)
-               return 0;
-
-again:
+ again:
        memset(tokens, 0, sizeof(tokens[0]) * ntokens);
-       config_free_data(parser);
 
        /* Read one line (handling continuations with backslash) */
-       line = bb_get_chunk_with_continuation(parser->fp, &len, &parser->lineno);
-       if (line == NULL)
+       if (get_line_with_continuation(parser) < 0)
                return 0;
-       parser->line = line;
 
-       /* Strip trailing line-feed if any */
-       if (len && line[len-1] == '\n')
-               line[len-1] = '\0';
+       line = parser->line;
 
        /* Skip token in the start of line? */
        if (flags & PARSE_TRIM)
@@ -155,8 +187,10 @@ again:
        if (line[0] == '\0' || line[0] == delims[0])
                goto again;
 
-       if (flags & PARSE_KEEP_COPY)
+       if (flags & PARSE_KEEP_COPY) {
+               free(parser->data);
                parser->data = xstrdup(line);
+       }
 
        /* Tokenize the line */
        t = 0;
@@ -170,7 +204,7 @@ again:
                        line += strcspn(line, delims[0] ? delims : delims + 1);
                } else {
                        /* Combining, find comment char if any */
-                       line = strchrnul(line, delims[0]);
+                       line = strchrnul(line, PARSE_EOL_COMMENTS ? delims[0] : '\0');
 
                        /* Trim any extra delimiters from the end */
                        if (flags & PARSE_TRIM) {
@@ -187,19 +221,7 @@ again:
 
 #if 0 /* unused so far */
                if (flags & PARSE_ESCAPE) {
-                       const char *from;
-                       char *to;
-
-                       from = to = tokens[t];
-                       while (*from) {
-                               if (*from == '\\') {
-                                       from++;
-                                       *to++ = bb_process_escape_sequence(&from);
-                               } else {
-                                       *to++ = *from++;
-                               }
-                       }
-                       *to = '\0';
+                       strcpy_and_process_escape_sequences(tokens[t], tokens[t]);
                }
 #endif
                /* Skip possible delimiters */
@@ -214,8 +236,6 @@ again:
                                parser->lineno, t, mintokens);
                if (flags & PARSE_MIN_DIE)
                        xfunc_die();
-               if (flags & PARSE_KEEP_COPY)
-                       free(parser->data);
                goto again;
        }
 
index 6eca00a..5a4e1c5 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
@@ -57,8 +57,8 @@ int FAST_FUNC bb_parse_mode(const char *s, mode_t *current_mode)
        /* Note: we allow empty clauses, and hence empty modes.
         * We treat an empty mode as no change to perms. */
 
-       while (*s) {    /* Process clauses. */
-               if (*s == ',') {        /* We allow empty clauses. */
+       while (*s) {  /* Process clauses. */
+               if (*s == ',') {  /* We allow empty clauses. */
                        ++s;
                        continue;
                }
@@ -77,7 +77,7 @@ int FAST_FUNC bb_parse_mode(const char *s, mode_t *current_mode)
                        }
                } while (*++p);
 
-               do {    /* Process action list. */
+               do {    /* Process action list. */
                        if ((*s != '+') && (*s != '-')) {
                                if (*s != '=') {
                                        return 0;
@@ -93,7 +93,7 @@ int FAST_FUNC bb_parse_mode(const char *s, mode_t *current_mode)
                        op = *s++;
 
                        /* Check for permcopy. */
-                       p = who_chars + 1;      /* Skip 'a' entry. */
+                       p = who_chars + 1;  /* Skip 'a' entry. */
                        do {
                                if (*p == *s) {
                                        int i = 0;
@@ -128,7 +128,7 @@ int FAST_FUNC bb_parse_mode(const char *s, mode_t *current_mode)
                                }
                        } while (*++p);
  GOT_ACTION:
-                       if (permlist) { /* The permlist was nonempty. */
+                       if (permlist) { /* The permlist was nonempty. */
                                mode_t tmp = wholist;
                                if (!wholist) {
                                        mode_t u_mask = umask(0);
diff --git a/libbb/percent_decode.c b/libbb/percent_decode.c
new file mode 100644 (file)
index 0000000..9a9d80c
--- /dev/null
@@ -0,0 +1,69 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//kbuild:lib-y += percent_decode.o
+
+#include "libbb.h"
+
+static unsigned hex_to_bin(unsigned char c)
+{
+       unsigned v;
+
+       v = c - '0';
+       if (v <= 9)
+               return v;
+       /* c | 0x20: letters to lower case, non-letters
+        * to (potentially different) non-letters */
+       v = (unsigned)(c | 0x20) - 'a';
+       if (v <= 5)
+               return v + 10;
+       return ~0;
+/* For testing:
+void t(char c) { printf("'%c'(%u) %u\n", c, c, hex_to_bin(c)); }
+int main() { t(0x10); t(0x20); t('0'); t('9'); t('A'); t('F'); t('a'); t('f');
+t('0'-1); t('9'+1); t('A'-1); t('F'+1); t('a'-1); t('f'+1); return 0; }
+*/
+}
+
+char* FAST_FUNC percent_decode_in_place(char *str, int strict)
+{
+       /* note that decoded string is always shorter than original */
+       char *src = str;
+       char *dst = str;
+       char c;
+
+       while ((c = *src++) != '\0') {
+               unsigned v;
+
+               if (!strict && c == '+') {
+                       *dst++ = ' ';
+                       continue;
+               }
+               if (c != '%') {
+                       *dst++ = c;
+                       continue;
+               }
+               v = hex_to_bin(src[0]);
+               if (v > 15) {
+ bad_hex:
+                       if (strict)
+                               return NULL;
+                       *dst++ = '%';
+                       continue;
+               }
+               v = (v * 16) | hex_to_bin(src[1]);
+               if (v > 255)
+                       goto bad_hex;
+               if (strict && (v == '/' || v == '\0')) {
+                       /* caller takes it as indication of invalid
+                        * (dangerous wrt exploits) chars */
+                       return str + 1;
+               }
+               *dst++ = v;
+               src += 2;
+       }
+       *dst = '\0';
+       return str;
+}
index cbba805..fa1f0d3 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
index a157caa..a2a11cc 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* gcc warns about a null format string, therefore we provide
index d56e05d..543ff51 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* gcc warns about a null format string, therefore we provide
index 7b8fee2..a48dfc3 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2007 by Stephane Billiart <stephane.billiart@gmail.com>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* Override ENABLE_FEATURE_PIDFILE */
index 7a8b176..1973451 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2009 by Dan Fandrich <dan@coneharvesters.com>, et. al.
  *
- * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
@@ -28,23 +28,25 @@ int FAST_FUNC vasprintf(char **string_ptr, const char *format, va_list p)
        r = vsnprintf(buf, 128, format, p);
        va_end(p);
 
+       /* Note: can't use xstrdup/xmalloc, they call vasprintf (us) on failure! */
+
        if (r < 128) {
                va_end(p2);
-               *string_ptr = xstrdup(buf);
-               return r;
+               *string_ptr = strdup(buf);
+               return (*string_ptr ? r : -1);
        }
 
-       *string_ptr = xmalloc(r+1);
-       r = vsnprintf(*string_ptr, r+1, format, p2);
+       *string_ptr = malloc(r+1);
+       r = (*string_ptr ? vsnprintf(*string_ptr, r+1, format, p2) : -1);
        va_end(p2);
 
        return r;
 }
 #endif
 
-#ifndef HAVE_FDPRINTF
-/* dprintf is now actually part of POSIX.1, but was only added in 2008 */
-int fdprintf(int fd, const char *format, ...)
+#ifndef HAVE_DPRINTF
+/* dprintf is now part of POSIX.1, but was only added in 2008 */
+int dprintf(int fd, const char *format, ...)
 {
        va_list p;
        int r;
@@ -134,3 +136,43 @@ char* FAST_FUNC strsep(char **stringp, const char *delim)
        return start;
 }
 #endif
+
+#ifndef HAVE_STPCPY
+char* FAST_FUNC stpcpy(char *p, const char *to_add)
+{
+       while ((*p = *to_add) != '\0') {
+               p++;
+               to_add++;
+       }
+       return p;
+}
+#endif
+
+#ifndef HAVE_GETLINE
+ssize_t FAST_FUNC getline(char **lineptr, size_t *n, FILE *stream)
+{
+       int ch;
+       char *line = *lineptr;
+       size_t alloced = *n;
+       size_t len = 0;
+
+       do {
+               ch = fgetc(stream);
+               if (ch == EOF)
+                       break;
+               if (len + 1 >= alloced) {
+                       alloced += alloced/4 + 64;
+                       line = xrealloc(line, alloced);
+               }
+               line[len++] = ch;
+       } while (ch != '\n');
+
+       if (len == 0)
+               return -1;
+
+       line[len] = '\0';
+       *lineptr = line;
+       *n = alloced;
+       return len;
+}
+#endif
index 9639dc6..eaec731 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2008 Natanael Copa <natanael.copa@gmail.com>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
index ae93359..9a42343 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2007 Denys Vlasenko
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -32,3 +32,27 @@ void FAST_FUNC fputc_printable(int ch, FILE *file)
        }
        fputc(ch, file);
 }
+
+void FAST_FUNC visible(unsigned ch, char *buf, int flags)
+{
+       if (ch == '\t' && !(flags & VISIBLE_SHOW_TABS)) {
+               goto raw;
+       }
+       if (ch == '\n') {
+               if (flags & VISIBLE_ENDLINE)
+                       *buf++ = '$';
+       } else {
+               if (ch >= 128) {
+                       ch -= 128;
+                       *buf++ = 'M';
+                       *buf++ = '-';
+               }
+               if (ch < 32 || ch == 127) {
+                       *buf++ = '^';
+                       ch ^= 0x40;
+               }
+       }
+ raw:
+       *buf++ = ch;
+       *buf = '\0';
+}
index 83a4821..a316f60 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2010 Denys Vlasenko
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 #include "unicode.h"
index 3ad908b..346ecfa 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) Manuel Novoa III <mjn3@codepoet.org>
  * and Vladimir Oleynik <dzo@simtreas.ru>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 
 char FAST_FUNC bb_process_escape_sequence(const char **ptr)
 {
-       /* bash builtin "echo -e '\ec'" interprets \e as ESC,
-        * but coreutils "/bin/echo -e '\ec'" does not.
-        * manpages tend to support coreutils way.
-        * Update: coreutils added support for \e on 28 Oct 2009. */
-       static const char charmap[] ALIGN1 = {
-               'a',  'b', 'e', 'f',  'n',  'r',  't',  'v',  '\\', 0,
-               '\a', '\b', 27, '\f', '\n', '\r', '\t', '\v', '\\', '\\' };
-
-       const char *p;
        const char *q;
        unsigned num_digits;
-       unsigned r;
        unsigned n;
-       unsigned d;
        unsigned base;
 
        num_digits = n = 0;
        base = 8;
        q = *ptr;
 
-#ifdef WANT_HEX_ESCAPES
-       if (*q == 'x') {
+       if (WANT_HEX_ESCAPES && *q == 'x') {
                ++q;
                base = 16;
                ++num_digits;
        }
-#endif
 
        /* bash requires leading 0 in octal escapes:
         * \02 works, \2 does not (prints \ and 2).
         * We treat \2 as a valid octal escape sequence. */
        do {
-               d = (unsigned char)(*q) - '0';
-#ifdef WANT_HEX_ESCAPES
-               if (d >= 10) {
-                       d = (unsigned char)(_tolower(*q)) - 'a' + 10;
-               }
+               unsigned r;
+#if !WANT_HEX_ESCAPES
+               unsigned d = (unsigned char)(*q) - '0';
+#else
+               unsigned d = (unsigned char)_tolower(*q) - '0';
+               if (d >= 10)
+                       d += ('0' - 'a' + 10);
 #endif
-
                if (d >= base) {
-#ifdef WANT_HEX_ESCAPES
-                       if ((base == 16) && (!--num_digits)) {
-/*                             return '\\'; */
-                               --q;
+                       if (WANT_HEX_ESCAPES && base == 16) {
+                               --num_digits;
+                               if (num_digits == 0) {
+                                       /* \x<bad_char>: return '\',
+                                        * leave ptr pointing to x */
+                                       return '\\';
+                               }
                        }
-#endif
                        break;
                }
 
@@ -76,21 +66,47 @@ char FAST_FUNC bb_process_escape_sequence(const char **ptr)
                ++q;
        } while (++num_digits < 3);
 
-       if (num_digits == 0) {  /* mnemonic escape sequence? */
-               p = charmap;
+       if (num_digits == 0) {
+               /* Not octal or hex escape sequence.
+                * Is it one-letter one? */
+
+               /* bash builtin "echo -e '\ec'" interprets \e as ESC,
+                * but coreutils "/bin/echo -e '\ec'" does not.
+                * Manpages tend to support coreutils way.
+                * Update: coreutils added support for \e on 28 Oct 2009. */
+               static const char charmap[] ALIGN1 = {
+                       'a',  'b', 'e', 'f',  'n',  'r',  't',  'v',  '\\', '\0',
+                       '\a', '\b', 27, '\f', '\n', '\r', '\t', '\v', '\\', '\\',
+               };
+               const char *p = charmap;
                do {
                        if (*p == *q) {
                                q++;
                                break;
                        }
-               } while (*++p);
+               } while (*++p != '\0');
                /* p points to found escape char or NUL,
-                * advance it and find what it translates to */
-               p += sizeof(charmap) / 2;
-               n = *p;
+                * advance it and find what it translates to.
+                * Note that \NUL and unrecognized sequence \z return '\'
+                * and leave ptr pointing to NUL or z. */
+               n = p[sizeof(charmap) / 2];
        }
 
        *ptr = q;
 
        return (char) n;
 }
+
+char* FAST_FUNC strcpy_and_process_escape_sequences(char *dst, const char *src)
+{
+       while (1) {
+               char c, c1;
+               c = c1 = *src++;
+               if (c1 == '\\')
+                       c1 = bb_process_escape_sequence(&src);
+               *dst = c1;
+               if (c == '\0')
+                       return dst;
+               dst++;
+       }
+}
index 48e60a7..5b68d34 100644 (file)
@@ -6,19 +6,19 @@
  * Copyright (C) 2002 by Vladimir Oleynik <dzo@simtreas.ru>
  * SELinux support: (c) 2007 by Yuichi Nakamura <ynakam@hitachisoft.jp>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 
 
-typedef struct unsigned_to_name_map_t {
-       long id;
+typedef struct id_to_name_map_t {
+       uid_t id;
        char name[USERNAME_MAX_SIZE];
-} unsigned_to_name_map_t;
+} id_to_name_map_t;
 
 typedef struct cache_t {
-       unsigned_to_name_map_t *cache;
+       id_to_name_map_t *cache;
        int size;
 } cache_t;
 
@@ -39,7 +39,7 @@ void FAST_FUNC clear_username_cache(void)
 #if 0 /* more generic, but we don't need that yet */
 /* Returns -N-1 if not found. */
 /* cp->cache[N] is allocated and must be filled in this case */
-static int get_cached(cache_t *cp, unsigned id)
+static int get_cached(cache_t *cp, uid_t id)
 {
        int i;
        for (i = 0; i < cp->size; i++)
@@ -52,8 +52,8 @@ static int get_cached(cache_t *cp, unsigned id)
 }
 #endif
 
-static char* get_cached(cache_t *cp, long id,
-                       char* FAST_FUNC x2x_utoa(long id))
+static char* get_cached(cache_t *cp, uid_t id,
+                       char* FAST_FUNC x2x_utoa(uid_t id))
 {
        int i;
        for (i = 0; i < cp->size; i++)
@@ -120,43 +120,43 @@ void FAST_FUNC free_procps_scan(procps_status_t* sp)
        free(sp);
 }
 
-#if ENABLE_FEATURE_TOPMEM
+#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
 static unsigned long fast_strtoul_16(char **endptr)
 {
        unsigned char c;
        char *str = *endptr;
        unsigned long n = 0;
 
-       while ((c = *str++) != ' ') {
+       /* Need to stop on both ' ' and '\n' */
+       while ((c = *str++) > ' ') {
                c = ((c|0x20) - '0');
                if (c > 9)
-                       // c = c + '0' - 'a' + 10:
+                       /* c = c + '0' - 'a' + 10: */
                        c = c - ('a' - '0' - 10);
                n = n*16 + c;
        }
        *endptr = str; /* We skip trailing space! */
        return n;
 }
-/* TOPMEM uses fast_strtoul_10, so... */
-# undef ENABLE_FEATURE_FAST_TOP
-# define ENABLE_FEATURE_FAST_TOP 1
 #endif
 
-#if ENABLE_FEATURE_FAST_TOP
+#if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
 /* We cut a lot of corners here for speed */
 static unsigned long fast_strtoul_10(char **endptr)
 {
-       char c;
+       unsigned char c;
        char *str = *endptr;
        unsigned long n = *str - '0';
 
-       while ((c = *++str) != ' ')
+       /* Need to stop on both ' ' and '\n' */
+       while ((c = *++str) > ' ')
                n = n*10 + (c - '0');
 
        *endptr = str + 1; /* We skip trailing space! */
        return n;
 }
 
+# if ENABLE_FEATURE_FAST_TOP
 static long fast_strtol_10(char **endptr)
 {
        if (**endptr != '-')
@@ -165,6 +165,7 @@ static long fast_strtol_10(char **endptr)
        (*endptr)++;
        return - (long)fast_strtoul_10(endptr);
 }
+# endif
 
 static char *skip_fields(char *str, int count)
 {
@@ -177,24 +178,128 @@ static char *skip_fields(char *str, int count)
 }
 #endif
 
-void BUG_comm_size(void);
-procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
+#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
+int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
+               void (*cb)(struct smaprec *, void *), void *data)
 {
-       struct dirent *entry;
+       FILE *file;
+       struct smaprec currec;
+       char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
        char buf[PROCPS_BUFSIZE];
-       char filename[sizeof("/proc//cmdline") + sizeof(int)*3];
-       char *filename_tail;
-       long tasknice;
-       unsigned pid;
-       int n;
-       struct stat sb;
+#if !ENABLE_PMAP
+       void (*cb)(struct smaprec *, void *) = NULL;
+       void *data = NULL;
+#endif
+
+       sprintf(filename, "/proc/%u/smaps", (int)pid);
+
+       file = fopen_for_read(filename);
+       if (!file)
+               return 1;
+
+       memset(&currec, 0, sizeof(currec));
+       while (fgets(buf, PROCPS_BUFSIZE, file)) {
+               // Each mapping datum has this form:
+               // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
+               // Size:                nnn kB
+               // Rss:                 nnn kB
+               // .....
+
+               char *tp = buf, *p;
+
+#define SCAN(S, X) \
+               if (strncmp(tp, S, sizeof(S)-1) == 0) {              \
+                       tp = skip_whitespace(tp + sizeof(S)-1);      \
+                       total->X += currec.X = fast_strtoul_10(&tp); \
+                       continue;                                    \
+               }
+               if (cb) {
+                       SCAN("Pss:"  , smap_pss     );
+                       SCAN("Swap:" , smap_swap    );
+               }
+               SCAN("Private_Dirty:", private_dirty);
+               SCAN("Private_Clean:", private_clean);
+               SCAN("Shared_Dirty:" , shared_dirty );
+               SCAN("Shared_Clean:" , shared_clean );
+#undef SCAN
+               tp = strchr(buf, '-');
+               if (tp) {
+                       // We reached next mapping - the line of this form:
+                       // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
+
+                       if (cb) {
+                               /* If we have a previous record, there's nothing more
+                                * for it, call the callback and clear currec
+                                */
+                               if (currec.smap_size)
+                                       cb(&currec, data);
+                               free(currec.smap_name);
+                       }
+                       memset(&currec, 0, sizeof(currec));
+
+                       *tp = ' ';
+                       tp = buf;
+                       currec.smap_start = fast_strtoul_16(&tp);
+                       currec.smap_size = (fast_strtoul_16(&tp) - currec.smap_start) >> 10;
+
+                       strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1);
+
+                       // skipping "rw-s FILEOFS M:m INODE "
+                       tp = skip_whitespace(skip_fields(tp, 4));
+                       // filter out /dev/something (something != zero)
+                       if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) {
+                               if (currec.smap_mode[1] == 'w') {
+                                       currec.mapped_rw = currec.smap_size;
+                                       total->mapped_rw += currec.smap_size;
+                               } else if (currec.smap_mode[1] == '-') {
+                                       currec.mapped_ro = currec.smap_size;
+                                       total->mapped_ro += currec.smap_size;
+                               }
+                       }
+
+                       if (strcmp(tp, "[stack]\n") == 0)
+                               total->stack += currec.smap_size;
+                       if (cb) {
+                               p = skip_non_whitespace(tp);
+                               if (p == tp) {
+                                       currec.smap_name = xstrdup("  [ anon ]");
+                               } else {
+                                       *p = '\0';
+                                       currec.smap_name = xstrdup(tp);
+                               }
+                       }
+                       total->smap_size += currec.smap_size;
+               }
+       }
+       fclose(file);
+
+       if (cb) {
+               if (currec.smap_size)
+                       cb(&currec, data);
+               free(currec.smap_name);
+       }
+
+       return 0;
+}
+#endif
 
+void BUG_comm_size(void);
+procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
+{
        if (!sp)
                sp = alloc_procps_scan();
 
        for (;;) {
+               struct dirent *entry;
+               char buf[PROCPS_BUFSIZE];
+               long tasknice;
+               unsigned pid;
+               int n;
+               char filename[sizeof("/proc/%u/task/%u/cmdline") + sizeof(int)*3 * 2];
+               char *filename_tail;
+
 #if ENABLE_FEATURE_SHOW_THREADS
-               if ((flags & PSSCAN_TASKS) && sp->task_dir) {
+               if (sp->task_dir) {
                        entry = readdir(sp->task_dir);
                        if (entry)
                                goto got_entry;
@@ -216,9 +321,10 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
                        /* We found another /proc/PID. Do not use it,
                         * there will be /proc/PID/task/PID (same PID!),
                         * so just go ahead and dive into /proc/PID/task. */
-                       char task_dir[sizeof("/proc/%u/task") + sizeof(int)*3];
-                       sprintf(task_dir, "/proc/%u/task", pid);
-                       sp->task_dir = xopendir(task_dir);
+                       sprintf(filename, "/proc/%u/task", pid);
+                       /* Note: if opendir fails, we just go to next /proc/XXX */
+                       sp->task_dir = opendir(filename);
+                       sp->main_thread_pid = pid;
                        continue;
                }
 #endif
@@ -241,9 +347,15 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
                }
 #endif
 
-               filename_tail = filename + sprintf(filename, "/proc/%u/", pid);
+#if ENABLE_FEATURE_SHOW_THREADS
+               if (sp->task_dir)
+                       filename_tail = filename + sprintf(filename, "/proc/%u/task/%u/", sp->main_thread_pid, pid);
+               else
+#endif
+                       filename_tail = filename + sprintf(filename, "/proc/%u/", pid);
 
                if (flags & PSSCAN_UIDGID) {
+                       struct stat sb;
                        if (stat(filename, &sb))
                                continue; /* process probably exited */
                        /* Effective UID/GID, not real */
@@ -251,7 +363,14 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
                        sp->gid = sb.st_gid;
                }
 
-               if (flags & PSSCAN_STAT) {
+               /* These are all retrieved from proc/NN/stat in one go: */
+               if (flags & (PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID
+                       | PSSCAN_COMM | PSSCAN_STATE
+                       | PSSCAN_VSZ | PSSCAN_RSS
+                       | PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_START_TIME
+                       | PSSCAN_TTY | PSSCAN_NICE
+                       | PSSCAN_CPU)
+               ) {
                        char *cp, *comm1;
                        int tty;
 #if !ENABLE_FEATURE_FAST_TOP
@@ -306,7 +425,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
                        if (n < 11)
                                continue; /* bogus data, get next /proc/XXX */
 # if ENABLE_FEATURE_TOP_SMP_PROCESS
-                       if (n < 11+15)
+                       if (n == 11)
                                sp->last_seen_on_cpu = 0;
 # endif
 
@@ -346,7 +465,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
 //FIXME: is it safe to assume this field exists?
                        sp->last_seen_on_cpu = fast_strtoul_10(&cp);
 # endif
-#endif /* end of !ENABLE_FEATURE_TOP_SMP_PROCESS */
+#endif /* FEATURE_FAST_TOP */
 
 #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
                        sp->niceness = tasknice;
@@ -365,54 +484,8 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
                }
 
 #if ENABLE_FEATURE_TOPMEM
-               if (flags & (PSSCAN_SMAPS)) {
-                       FILE *file;
-
-                       strcpy(filename_tail, "smaps");
-                       file = fopen_for_read(filename);
-                       if (file) {
-                               while (fgets(buf, sizeof(buf), file)) {
-                                       unsigned long sz;
-                                       char *tp;
-                                       char w;
-#define SCAN(str, name) \
-       if (strncmp(buf, str, sizeof(str)-1) == 0) { \
-               tp = skip_whitespace(buf + sizeof(str)-1); \
-               sp->name += fast_strtoul_10(&tp); \
-               continue; \
-       }
-                                       SCAN("Shared_Clean:" , shared_clean );
-                                       SCAN("Shared_Dirty:" , shared_dirty );
-                                       SCAN("Private_Clean:", private_clean);
-                                       SCAN("Private_Dirty:", private_dirty);
-#undef SCAN
-                                       // f7d29000-f7d39000 rw-s ADR M:m OFS FILE
-                                       tp = strchr(buf, '-');
-                                       if (tp) {
-                                               *tp = ' ';
-                                               tp = buf;
-                                               sz = fast_strtoul_16(&tp); /* start */
-                                               sz = (fast_strtoul_16(&tp) - sz) >> 10; /* end - start */
-                                               // tp -> "rw-s" string
-                                               w = tp[1];
-                                               // skipping "rw-s ADR M:m OFS "
-                                               tp = skip_whitespace(skip_fields(tp, 4));
-                                               // filter out /dev/something (something != zero)
-                                               if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) {
-                                                       if (w == 'w') {
-                                                               sp->mapped_rw += sz;
-                                                       } else if (w == '-') {
-                                                               sp->mapped_ro += sz;
-                                                       }
-                                               }
-//else printf("DROPPING %s (%s)\n", buf, tp);
-                                               if (strcmp(tp, "[stack]\n") == 0)
-                                                       sp->stack += sz;
-                                       }
-                               }
-                               fclose(file);
-                       }
-               }
+               if (flags & PSSCAN_SMAPS)
+                       procps_read_smaps(pid, &sp->smaps, NULL, NULL);
 #endif /* TOPMEM */
 #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
                if (flags & PSSCAN_RUIDGID) {
@@ -499,18 +572,49 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
 void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
 {
        int sz;
-       char filename[sizeof("/proc//cmdline") + sizeof(int)*3];
+       char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
 
        sprintf(filename, "/proc/%u/cmdline", pid);
        sz = open_read_close(filename, buf, col - 1);
        if (sz > 0) {
+               const char *base;
+               int comm_len;
+
                buf[sz] = '\0';
                while (--sz >= 0 && buf[sz] == '\0')
                        continue;
-               do {
+               /* Prevent basename("process foo/bar") = "bar" */
+               strchrnul(buf, ' ')[0] = '\0';
+               base = bb_basename(buf); /* before we replace argv0's NUL with space */
+               while (sz >= 0) {
                        if ((unsigned char)(buf[sz]) < ' ')
                                buf[sz] = ' ';
-               } while (--sz >= 0);
+                       sz--;
+               }
+
+               /* If comm differs from argv0, prepend "{comm} ".
+                * It allows to see thread names set by prctl(PR_SET_NAME).
+                */
+               if (base[0] == '-') /* "-sh" (login shell)? */
+                       base++;
+               comm_len = strlen(comm);
+               /* Why compare up to comm_len, not COMM_LEN-1?
+                * Well, some processes rewrite argv, and use _spaces_ there
+                * while rewriting. (KDE is observed to do it).
+                * I prefer to still treat argv0 "process foo bar"
+                * as 'equal' to comm "process".
+                */
+               if (strncmp(base, comm, comm_len) != 0) {
+                       comm_len += 3;
+                       if (col > comm_len)
+                               memmove(buf + comm_len, buf, col - comm_len);
+                       snprintf(buf, col, "{%s}", comm);
+                       if (col <= comm_len)
+                               return;
+                       buf[comm_len - 1] = ' ';
+                       buf[col - 1] = '\0';
+               }
+
        } else {
                snprintf(buf, col, "[%s]", comm);
        }
index e960390..372feb0 100644 (file)
@@ -7,7 +7,7 @@
  */
 /*-
  * Copyright (c) 1992, 1993
- *     The Regents of the University of California.  All rights reserved.
+ * The Regents of the University of California.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -18,8 +18,8 @@
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
- * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
- *             ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ * 3. BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
  *
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
@@ -52,102 +52,156 @@ static unsigned int get_tty2_width(void)
        return width;
 }
 
-void FAST_FUNC bb_progress_init(bb_progress_t *p)
+void FAST_FUNC bb_progress_init(bb_progress_t *p, const char *curfile)
 {
+#if ENABLE_UNICODE_SUPPORT
+       init_unicode();
+       p->curfile = unicode_conv_to_printable_fixedwidth(/*NULL,*/ curfile, 20);
+#else
+       p->curfile = curfile;
+#endif
        p->start_sec = monotonic_sec();
-       p->lastupdate_sec = p->start_sec;
-       p->lastsize = 0;
-       p->inited = 1;
+       p->last_update_sec = p->start_sec;
+       p->last_change_sec = p->start_sec;
+       p->last_size = 0;
 }
 
+/* File already had beg_size bytes.
+ * Then we started downloading.
+ * We downloaded "transferred" bytes so far.
+ * Download is expected to stop when total size (beg_size + transferred)
+ * will be "totalsize" bytes.
+ * If totalsize == 0, then it is unknown.
+ */
 void FAST_FUNC bb_progress_update(bb_progress_t *p,
-               const char *curfile,
-               off_t beg_range,
-               off_t transferred,
-               off_t totalsize)
+               uoff_t beg_size,
+               uoff_t transferred,
+               uoff_t totalsize)
 {
-       off_t abbrevsize;
+       uoff_t beg_and_transferred;
        unsigned since_last_update, elapsed;
-       unsigned ratio;
-       int barlength, i;
-
-       ratio = 100;
-       if (totalsize) {
-               /* long long helps to have it working even if !LFS */
-               ratio = (unsigned) (100ULL * (transferred+beg_range) / totalsize);
-               if (ratio > 100) ratio = 100;
+       int barlength;
+       int kiloscale;
+
+       //transferred = 1234; /* use for stall detection testing */
+       //totalsize = 0; /* use for unknown size download testing */
+
+       elapsed = monotonic_sec();
+       since_last_update = elapsed - p->last_update_sec;
+       p->last_update_sec = elapsed;
+
+       if (totalsize != 0 && transferred >= totalsize - beg_size) {
+               /* Last call. Do not skip this update */
+               transferred = totalsize - beg_size; /* sanitize just in case */
+       }
+       else if (since_last_update == 0) {
+               /*
+                * Do not update on every call
+                * (we can be called on every network read!)
+                */
+               return;
        }
 
-#if ENABLE_UNICODE_SUPPORT
-       init_unicode();
-       /* libbb candidate? */
-       {
-               wchar_t wbuf21[21];
-               char *buf = xstrdup(curfile);
-               unsigned len;
-
-               /* trim to 20 wide chars max (sets wbuf21[20] to 0)
-                * also, in case mbstowcs fails, we at least
-                * dont get garbage */
-               memset(wbuf21, 0, sizeof(wbuf21));
-               /* convert to wide chars, no more than 20 */
-               len = mbstowcs(wbuf21, curfile, 20); /* NB: may return -1 */
-               /* back to multibyte; cant overflow */
-               wcstombs(buf, wbuf21, INT_MAX);
-               len = (len > 20) ? 0 : 20 - len;
-               fprintf(stderr, "\r%s%*s%4d%% ", buf, len, "", ratio);
-               free(buf);
+       kiloscale = 0;
+       /*
+        * Scale sizes down if they are close to overflowing.
+        * This allows calculations like (100 * transferred / totalsize)
+        * without risking overflow: we guarantee 10 highest bits to be 0.
+        * Introduced error is less than 1 / 2^12 ~= 0.025%
+        */
+       if (ULONG_MAX > 0xffffffff || sizeof(off_t) == 4 || sizeof(off_t) != 8) {
+               /*
+                * 64-bit CPU || small off_t: in either case,
+                * >> is cheap, single-word operation.
+                * ... || strange off_t: also use this code
+                * (it is safe, just suboptimal wrt code size),
+                * because 32/64 optimized one works only for 64-bit off_t.
+                */
+               if (totalsize >= (1 << 22)) {
+                       totalsize >>= 10;
+                       beg_size >>= 10;
+                       transferred >>= 10;
+                       kiloscale = 1;
+               }
+       } else {
+               /* 32-bit CPU and 64-bit off_t.
+                * Use a 40-bit shift, it is easier to do on 32-bit CPU.
+                */
+/* ONE suppresses "warning: shift count >= width of type" */
+#define ONE (sizeof(off_t) > 4)
+               if (totalsize >= (uoff_t)(1ULL << 54*ONE)) {
+                       totalsize = (uint32_t)(totalsize >> 32*ONE) >> 8;
+                       beg_size = (uint32_t)(beg_size >> 32*ONE) >> 8;
+                       transferred = (uint32_t)(transferred >> 32*ONE) >> 8;
+                       kiloscale = 4;
+               }
        }
-#else
-       fprintf(stderr, "\r%-20.20s%4d%% ", curfile, ratio);
-#endif
 
-       barlength = get_tty2_width() - 49;
-       if (barlength > 0) {
-               /* god bless gcc for variable arrays :) */
-               i = barlength * ratio / 100;
-               {
-                       char buf[i+1];
-                       memset(buf, '*', i);
-                       buf[i] = '\0';
-                       fprintf(stderr, "|%s%*s|", buf, barlength - i, "");
+       if (ENABLE_UNICODE_SUPPORT)
+               fprintf(stderr, "\r%s", p->curfile);
+       else
+               fprintf(stderr, "\r%-20.20s", p->curfile);
+
+       beg_and_transferred = beg_size + transferred;
+
+       if (totalsize != 0) {
+               unsigned ratio = 100 * beg_and_transferred / totalsize;
+               fprintf(stderr, "%4u%%", ratio);
+
+               barlength = get_tty2_width() - 49;
+               if (barlength > 0) {
+                       /* god bless gcc for variable arrays :) */
+                       char buf[barlength + 1];
+                       unsigned stars = (unsigned)barlength * beg_and_transferred / totalsize;
+                       memset(buf, ' ', barlength);
+                       buf[barlength] = '\0';
+                       memset(buf, '*', stars);
+                       fprintf(stderr, " |%s|", buf);
                }
        }
-       i = 0;
-       abbrevsize = transferred + beg_range;
-       while (abbrevsize >= 100000) {
-               i++;
-               abbrevsize >>= 10;
+
+       while (beg_and_transferred >= 100000) {
+               beg_and_transferred >>= 10;
+               kiloscale++;
        }
        /* see http://en.wikipedia.org/wiki/Tera */
-       fprintf(stderr, "%6d%c ", (int)abbrevsize, " kMGTPEZY"[i]);
+       fprintf(stderr, "%6u%c", (unsigned)beg_and_transferred, " kMGTPEZY"[kiloscale]);
+#define beg_and_transferred dont_use_beg_and_transferred_below()
 
-       elapsed = monotonic_sec();
-       since_last_update = elapsed - p->lastupdate_sec;
-       if (transferred > p->lastsize) {
-               p->lastupdate_sec = elapsed;
-               p->lastsize = transferred;
+       since_last_update = elapsed - p->last_change_sec;
+       if ((unsigned)transferred != p->last_size) {
+               p->last_change_sec = elapsed;
+               p->last_size = (unsigned)transferred;
                if (since_last_update >= STALLTIME) {
-                       /* We "cut off" these seconds from elapsed time
+                       /* We "cut out" these seconds from elapsed time
                         * by adjusting start time */
                        p->start_sec += since_last_update;
                }
                since_last_update = 0; /* we are un-stalled now */
        }
+
        elapsed -= p->start_sec; /* now it's "elapsed since start" */
 
        if (since_last_update >= STALLTIME) {
-               fprintf(stderr, " - stalled -");
+               fprintf(stderr, "  - stalled -");
+       } else if (!totalsize || !transferred || (int)elapsed < 0) {
+               fprintf(stderr, " --:--:-- ETA");
        } else {
-               off_t to_download = totalsize - beg_range;
-               if (!totalsize || transferred <= 0 || (int)elapsed <= 0 || transferred > to_download) {
-                       fprintf(stderr, "--:--:-- ETA");
-               } else {
-                       /* to_download / (transferred/elapsed) - elapsed: */
-                       int eta = (int) ((unsigned long long)to_download*elapsed/transferred - elapsed);
-                       /* (long long helps to have working ETA even if !LFS) */
-                       i = eta % 3600;
-                       fprintf(stderr, "%02d:%02d:%02d ETA", eta / 3600, i / 60, i % 60);
-               }
+               unsigned eta, secs, hours;
+
+               totalsize -= beg_size; /* now it's "total to upload" */
+
+               /* Estimated remaining time =
+                * estimated_sec_to_dl_totalsize_bytes - elapsed_sec =
+                * totalsize / average_bytes_sec_so_far - elapsed =
+                * totalsize / (transferred/elapsed) - elapsed =
+                * totalsize * elapsed / transferred - elapsed
+                */
+               eta = totalsize * elapsed / transferred - elapsed;
+               if (eta >= 1000*60*60)
+                       eta = 1000*60*60 - 1;
+               secs = eta % 3600;
+               hours = eta / 3600;
+               fprintf(stderr, "%3u:%02u:%02u ETA", hours, secs / 60, secs % 60);
        }
 }
index 5f30e2a..1074538 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2008 by Denys Vlasenko <vda.linux@googlemail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include <errno.h>
index 6fc0ba8..39ffa08 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -27,9 +27,10 @@ static int i64c(int i)
        return ('a' - 38 + i);
 }
 
-int FAST_FUNC crypt_make_salt(char *p, int cnt, int x)
+int FAST_FUNC crypt_make_salt(char *p, int cnt /*, int x */)
 {
-       x += getpid() + time(NULL);
+       /* was: x += ... */
+       int x = getpid() + monotonic_us();
        do {
                /* x = (x*1664525 + 1013904223) % 2^32 generator is lame
                 * (low-order bit is not "random", etc...),
@@ -47,6 +48,26 @@ int FAST_FUNC crypt_make_salt(char *p, int cnt, int x)
        return x;
 }
 
+char* FAST_FUNC crypt_make_pw_salt(char salt[MAX_PW_SALT_LEN], const char *algo)
+{
+       int len = 2/2;
+       char *salt_ptr = salt;
+       if (algo[0] != 'd') { /* not des */
+               len = 8/2; /* so far assuming md5 */
+               *salt_ptr++ = '$';
+               *salt_ptr++ = '1';
+               *salt_ptr++ = '$';
+#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA
+               if (algo[0] == 's') { /* sha */
+                       salt[1] = '5' + (strcmp(algo, "sha512") == 0);
+                       len = 16/2;
+               }
+#endif
+       }
+       crypt_make_salt(salt_ptr, len);
+       return salt_ptr;
+}
+
 #if ENABLE_USE_BB_CRYPT
 
 static char*
index 58964b5..1e52eca 100644 (file)
@@ -86,16 +86,16 @@ md5_crypt(char result[MD5_OUT_BUFSIZE], const unsigned char *pw, const unsigned
 
        /* Get the length of the salt including "$1$" */
        sl = 3;
-       while (salt[sl] && salt[sl] != '$' && sl < (3 + 8))
+       while (sl < (3 + 8) && salt[sl] && salt[sl] != '$')
                sl++;
 
        /* Hash. the password first, since that is what is most unknown */
        md5_begin(&ctx);
        pw_len = strlen((char*)pw);
-       md5_hash(pw, pw_len, &ctx);
+       md5_hash(&ctx, pw, pw_len);
 
        /* Then the salt including "$1$" */
-       md5_hash(salt, sl, &ctx);
+       md5_hash(&ctx, salt, sl);
 
        /* Copy salt to result; skip "$1$" */
        memcpy(result, salt, sl);
@@ -105,19 +105,19 @@ md5_crypt(char result[MD5_OUT_BUFSIZE], const unsigned char *pw, const unsigned
 
        /* Then just as many characters of the MD5(pw, salt, pw) */
        md5_begin(&ctx1);
-       md5_hash(pw, pw_len, &ctx1);
-       md5_hash(salt, sl, &ctx1);
-       md5_hash(pw, pw_len, &ctx1);
-       md5_end(final, &ctx1);
+       md5_hash(&ctx1, pw, pw_len);
+       md5_hash(&ctx1, salt, sl);
+       md5_hash(&ctx1, pw, pw_len);
+       md5_end(&ctx1, final);
        for (pl = pw_len; pl > 0; pl -= 16)
-               md5_hash(final, pl > 16 ? 16 : pl, &ctx);
+               md5_hash(&ctx, final, pl > 16 ? 16 : pl);
 
        /* Then something really weird... */
        memset(final, 0, sizeof(final));
        for (i = pw_len; i; i >>= 1) {
-               md5_hash(((i & 1) ? final : (const unsigned char *) pw), 1, &ctx);
+               md5_hash(&ctx, ((i & 1) ? final : (const unsigned char *) pw), 1);
        }
-       md5_end(final, &ctx);
+       md5_end(&ctx, final);
 
        /* And now, just to make sure things don't run too fast.
         * On a 60 Mhz Pentium this takes 34 msec, so you would
@@ -126,21 +126,21 @@ md5_crypt(char result[MD5_OUT_BUFSIZE], const unsigned char *pw, const unsigned
        for (i = 0; i < 1000; i++) {
                md5_begin(&ctx1);
                if (i & 1)
-                       md5_hash(pw, pw_len, &ctx1);
+                       md5_hash(&ctx1, pw, pw_len);
                else
-                       md5_hash(final, 16, &ctx1);
+                       md5_hash(&ctx1, final, 16);
 
                if (i % 3)
-                       md5_hash(salt, sl, &ctx1);
+                       md5_hash(&ctx1, salt, sl);
 
                if (i % 7)
-                       md5_hash(pw, pw_len, &ctx1);
+                       md5_hash(&ctx1, pw, pw_len);
 
                if (i & 1)
-                       md5_hash(final, 16, &ctx1);
+                       md5_hash(&ctx1, final, 16);
                else
-                       md5_hash(pw, pw_len, &ctx1);
-               md5_end(final, &ctx1);
+                       md5_hash(&ctx1, pw, pw_len);
+               md5_end(&ctx1, final);
        }
 
        p = result + sl + 4; /* 12 bytes max (sl is up to 8 bytes) */
index 070e0d4..8aeaaca 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /* Prefix for optional rounds specification.  */
-static const char str_rounds[] = "rounds=%u$";
+static const char str_rounds[] ALIGN1 = "rounds=%u$";
 
 /* Maximum salt string length.  */
 #define SALT_LEN_MAX 16
@@ -19,8 +19,8 @@ NOINLINE
 sha_crypt(/*const*/ char *key_data, /*const*/ char *salt_data)
 {
        void (*sha_begin)(void *ctx) FAST_FUNC;
-       void (*sha_hash)(const void *buffer, size_t len, void *ctx) FAST_FUNC;
-       void (*sha_end)(void *resbuf, void *ctx) FAST_FUNC;
+       void (*sha_hash)(void *ctx, const void *buffer, size_t len) FAST_FUNC;
+       void (*sha_end)(void *ctx, void *resbuf) FAST_FUNC;
        int _32or64;
 
        char *result, *resptr;
@@ -103,40 +103,40 @@ sha_crypt(/*const*/ char *key_data, /*const*/ char *salt_data)
 
        /* Add KEY, SALT.  */
        sha_begin(&ctx);
-       sha_hash(key_data, key_len, &ctx);
-       sha_hash(salt_data, salt_len, &ctx);
+       sha_hash(&ctx, key_data, key_len);
+       sha_hash(&ctx, salt_data, salt_len);
 
        /* Compute alternate SHA sum with input KEY, SALT, and KEY.
           The final result will be added to the first context.  */
        sha_begin(&alt_ctx);
-       sha_hash(key_data, key_len, &alt_ctx);
-       sha_hash(salt_data, salt_len, &alt_ctx);
-       sha_hash(key_data, key_len, &alt_ctx);
-       sha_end(alt_result, &alt_ctx);
+       sha_hash(&alt_ctx, key_data, key_len);
+       sha_hash(&alt_ctx, salt_data, salt_len);
+       sha_hash(&alt_ctx, key_data, key_len);
+       sha_end(&alt_ctx, alt_result);
 
        /* Add result of this to the other context.  */
        /* Add for any character in the key one byte of the alternate sum.  */
        for (cnt = key_len; cnt > _32or64; cnt -= _32or64)
-               sha_hash(alt_result, _32or64, &ctx);
-       sha_hash(alt_result, cnt, &ctx);
+               sha_hash(&ctx, alt_result, _32or64);
+       sha_hash(&ctx, alt_result, cnt);
 
        /* Take the binary representation of the length of the key and for every
           1 add the alternate sum, for every 0 the key.  */
        for (cnt = key_len; cnt != 0; cnt >>= 1)
                if ((cnt & 1) != 0)
-                       sha_hash(alt_result, _32or64, &ctx);
+                       sha_hash(&ctx, alt_result, _32or64);
                else
-                       sha_hash(key_data, key_len, &ctx);
+                       sha_hash(&ctx, key_data, key_len);
 
        /* Create intermediate result.  */
-       sha_end(alt_result, &ctx);
+       sha_end(&ctx, alt_result);
 
        /* Start computation of P byte sequence.  */
        /* For every character in the password add the entire password.  */
        sha_begin(&alt_ctx);
        for (cnt = 0; cnt < key_len; ++cnt)
-               sha_hash(key_data, key_len, &alt_ctx);
-       sha_end(temp_result, &alt_ctx);
+               sha_hash(&alt_ctx, key_data, key_len);
+       sha_end(&alt_ctx, temp_result);
 
        /* NB: past this point, raw key_data is not used anymore */
 
@@ -153,8 +153,8 @@ sha_crypt(/*const*/ char *key_data, /*const*/ char *salt_data)
        /* For every character in the password add the entire password.  */
        sha_begin(&alt_ctx);
        for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt)
-               sha_hash(salt_data, salt_len, &alt_ctx);
-       sha_end(temp_result, &alt_ctx);
+               sha_hash(&alt_ctx, salt_data, salt_len);
+       sha_end(&alt_ctx, temp_result);
 
        /* NB: past this point, raw salt_data is not used anymore */
 
@@ -174,31 +174,31 @@ sha_crypt(/*const*/ char *key_data, /*const*/ char *salt_data)
 
                /* Add key or last result.  */
                if ((cnt & 1) != 0)
-                       sha_hash(p_bytes, key_len, &ctx);
+                       sha_hash(&ctx, p_bytes, key_len);
                else
-                       sha_hash(alt_result, _32or64, &ctx);
+                       sha_hash(&ctx, alt_result, _32or64);
                /* Add salt for numbers not divisible by 3.  */
                if (cnt % 3 != 0)
-                       sha_hash(s_bytes, salt_len, &ctx);
+                       sha_hash(&ctx, s_bytes, salt_len);
                /* Add key for numbers not divisible by 7.  */
                if (cnt % 7 != 0)
-                       sha_hash(p_bytes, key_len, &ctx);
+                       sha_hash(&ctx, p_bytes, key_len);
                /* Add key or last result.  */
                if ((cnt & 1) != 0)
-                       sha_hash(alt_result, _32or64, &ctx);
+                       sha_hash(&ctx, alt_result, _32or64);
                else
-                       sha_hash(p_bytes, key_len, &ctx);
+                       sha_hash(&ctx, p_bytes, key_len);
 
-               sha_end(alt_result, &ctx);
+               sha_end(&ctx, alt_result);
        }
 
        /* Append encrypted password to result buffer */
 //TODO: replace with something like
 //     bb_uuencode(cp, src, length, bb_uuenc_tbl_XXXbase64);
 #define b64_from_24bit(B2, B1, B0, N) \
-do {                                                   \
-       unsigned w = ((B2) << 16) | ((B1) << 8) | (B0); \
-       resptr = to64(resptr, w, N);                    \
+do { \
+       unsigned w = ((B2) << 16) | ((B1) << 8) | (B0); \
+       resptr = to64(resptr, w, N); \
 } while (0)
        if (is_sha512 == '5') {
                unsigned i = 0;
index 1ed7c5f..5906bc2 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
index 64557ab..ace23de 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) 2008 Rob Landley <rob@landley.net>
  * Copyright (C) 2008 Denys Vlasenko <vda.linux@googlemail.com>
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
@@ -15,7 +15,10 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
        const char *seq;
        int n;
 
-       /* Known escape sequences for cursor and function keys */
+       /* Known escape sequences for cursor and function keys.
+        * See "Xterm Control Sequences"
+        * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+        */
        static const char esccmds[] ALIGN1 = {
                'O','A'        |0x80,KEYCODE_UP      ,
                'O','B'        |0x80,KEYCODE_DOWN    ,
@@ -40,13 +43,16 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
                '[','C'        |0x80,KEYCODE_RIGHT   ,
                '[','D'        |0x80,KEYCODE_LEFT    ,
                /* ESC [ 1 ; 2 x, where x = A/B/C/D: Shift-<arrow> */
-               /* ESC [ 1 ; 3 x, where x = A/B/C/D: Alt-<arrow> */
+               /* ESC [ 1 ; 3 x, where x = A/B/C/D: Alt-<arrow> - implemented below */
                /* ESC [ 1 ; 4 x, where x = A/B/C/D: Alt-Shift-<arrow> */
                /* ESC [ 1 ; 5 x, where x = A/B/C/D: Ctrl-<arrow> - implemented below */
                /* ESC [ 1 ; 6 x, where x = A/B/C/D: Ctrl-Shift-<arrow> */
+               /* ESC [ 1 ; 7 x, where x = A/B/C/D: Ctrl-Alt-<arrow> */
+               /* ESC [ 1 ; 8 x, where x = A/B/C/D: Ctrl-Alt-Shift-<arrow> */
                '[','H'        |0x80,KEYCODE_HOME    , /* xterm */
-               /* [ESC] ESC [ [2] H - [Alt-][Shift-]Home */
                '[','F'        |0x80,KEYCODE_END     , /* xterm */
+               /* [ESC] ESC [ [2] H - [Alt-][Shift-]Home (End similarly?) */
+               /* '[','Z'        |0x80,KEYCODE_SHIFT_TAB, */
                '[','1','~'    |0x80,KEYCODE_HOME    , /* vt100? linux vt? or what? */
                '[','2','~'    |0x80,KEYCODE_INSERT  ,
                /* ESC [ 2 ; 3 ~ - Alt-Insert */
@@ -63,10 +69,10 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
                '[','7','~'    |0x80,KEYCODE_HOME    , /* vt100? linux vt? or what? */
                '[','8','~'    |0x80,KEYCODE_END     , /* vt100? linux vt? or what? */
 #if 0
-               '[','1','1','~'|0x80,KEYCODE_FUN1    ,
-               '[','1','2','~'|0x80,KEYCODE_FUN2    ,
-               '[','1','3','~'|0x80,KEYCODE_FUN3    ,
-               '[','1','4','~'|0x80,KEYCODE_FUN4    ,
+               '[','1','1','~'|0x80,KEYCODE_FUN1    , /* old xterm, deprecated by ESC O P */
+               '[','1','2','~'|0x80,KEYCODE_FUN2    , /* old xterm... */
+               '[','1','3','~'|0x80,KEYCODE_FUN3    , /* old xterm... */
+               '[','1','4','~'|0x80,KEYCODE_FUN4    , /* old xterm... */
                '[','1','5','~'|0x80,KEYCODE_FUN5    ,
                /* [ESC] ESC [ 1 5 [;2] ~ - [Alt-][Shift-]F5 */
                '[','1','7','~'|0x80,KEYCODE_FUN6    ,
@@ -86,8 +92,12 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
                /* '[','1',';','5','B' |0x80,KEYCODE_CTRL_DOWN , - unused */
                '[','1',';','5','C' |0x80,KEYCODE_CTRL_RIGHT,
                '[','1',';','5','D' |0x80,KEYCODE_CTRL_LEFT ,
+               /* '[','1',';','3','A' |0x80,KEYCODE_ALT_UP    , - unused */
+               /* '[','1',';','3','B' |0x80,KEYCODE_ALT_DOWN  , - unused */
+               '[','1',';','3','C' |0x80,KEYCODE_ALT_RIGHT,
+               '[','1',';','3','D' |0x80,KEYCODE_ALT_LEFT ,
+               /* '[','3',';','3','~' |0x80,KEYCODE_ALT_DELETE, - unused */
                0
-               /* ESC [ Z - Shift-Tab */
        };
 
        pfd.fd = fd;
@@ -214,7 +224,10 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
                }
                n++;
                /* Try to decipher "ESC [ NNN ; NNN R" sequence */
-               if ((ENABLE_FEATURE_EDITING_ASK_TERMINAL || ENABLE_FEATURE_VI_ASK_TERMINAL)
+               if ((ENABLE_FEATURE_EDITING_ASK_TERMINAL
+                   || ENABLE_FEATURE_VI_ASK_TERMINAL
+                   || ENABLE_FEATURE_LESS_ASK_TERMINAL
+                   )
                 && n >= 5
                 && buffer[0] == '['
                 && buffer[n-1] == 'R'
index 1b215f9..5ed6e36 100644 (file)
@@ -4,20 +4,10 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
-#define ZIPPED (ENABLE_FEATURE_SEAMLESS_LZMA \
-       || ENABLE_FEATURE_SEAMLESS_BZ2 \
-       || ENABLE_FEATURE_SEAMLESS_GZ \
-       /* || ENABLE_FEATURE_SEAMLESS_Z */ \
-)
-
-#if ZIPPED
-# include "unarchive.h"
-#endif
-
 
 /* Suppose that you are a shell. You start child processes.
  * They work and eventually exit. You want to get user input.
  * which detects EAGAIN and uses poll() to wait on the fd.
  * Thankfully, poll() doesn't care about O_NONBLOCK flag.
  */
-ssize_t FAST_FUNC nonblock_safe_read(int fd, void *buf, size_t count)
+ssize_t FAST_FUNC nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR)
 {
        struct pollfd pfd[1];
        ssize_t n;
 
        while (1) {
-               n = safe_read(fd, buf, count);
+               n = loop_on_EINTR ? safe_read(fd, buf, count) : read(fd, buf, count);
                if (n >= 0 || errno != EAGAIN)
                        return n;
                /* fd is in O_NONBLOCK mode. Wait using poll and repeat */
                pfd[0].fd = fd;
                pfd[0].events = POLLIN;
-               safe_poll(pfd, 1, -1); /* note: this pulls in printf */
+               /* note: safe_poll pulls in printf */
+               loop_on_EINTR ? safe_poll(pfd, 1, -1) : poll(pfd, 1, -1);
        }
 }
 
 // Reads one line a-la fgets (but doesn't save terminating '\n').
 // Reads byte-by-byte. Useful when it is important to not read ahead.
 // Bytes are appended to pfx (which must be malloced, or NULL).
-char* FAST_FUNC xmalloc_reads(int fd, char *buf, size_t *maxsz_p)
+char* FAST_FUNC xmalloc_reads(int fd, size_t *maxsz_p)
 {
        char *p;
-       size_t sz = buf ? strlen(buf) : 0;
+       char *buf = NULL;
+       size_t sz = 0;
        size_t maxsz = maxsz_p ? *maxsz_p : (INT_MAX - 4095);
 
        goto jump_in;
+
        while (sz < maxsz) {
                if ((size_t)(p - buf) == sz) {
  jump_in:
@@ -88,8 +81,8 @@ char* FAST_FUNC xmalloc_reads(int fd, char *buf, size_t *maxsz_p)
                        p = buf + sz;
                        sz += 128;
                }
-               /* nonblock_safe_read() because we are used by e.g. shells */
-               if (nonblock_safe_read(fd, p, 1) != 1) { /* EOF/error */
+               if (nonblock_immune_read(fd, p, 1, /*loop_on_EINTR:*/ 1) != 1) {
+                       /* EOF/error */
                        if (p == buf) { /* we read nothing */
                                free(buf);
                                return NULL;
@@ -241,132 +234,3 @@ void* FAST_FUNC xmalloc_xopen_read_close(const char *filename, size_t *maxsz_p)
                bb_perror_msg_and_die("can't read '%s'", filename);
        return buf;
 }
-
-/* Used by e.g. rpm which gives us a fd without filename,
- * thus we can't guess the format from filename's extension.
- */
-#if ZIPPED
-void FAST_FUNC setup_unzip_on_fd(int fd /*, int fail_if_not_detected*/)
-{
-       const int fail_if_not_detected = 1;
-       union {
-               uint8_t b[4];
-               uint16_t b16[2];
-               uint32_t b32[1];
-       } magic;
-       int offset = -2;
-# if BB_MMU
-       IF_DESKTOP(long long) int FAST_FUNC (*xformer)(int src_fd, int dst_fd);
-       enum { xformer_prog = 0 };
-# else
-       enum { xformer = 0 };
-       const char *xformer_prog;
-# endif
-
-       /* .gz and .bz2 both have 2-byte signature, and their
-        * unpack_XXX_stream wants this header skipped. */
-       xread(fd, magic.b16, sizeof(magic.b16[0]));
-       if (ENABLE_FEATURE_SEAMLESS_GZ
-        && magic.b16[0] == GZIP_MAGIC
-       ) {
-# if BB_MMU
-               xformer = unpack_gz_stream;
-# else
-               xformer_prog = "gunzip";
-# endif
-               goto found_magic;
-       }
-       if (ENABLE_FEATURE_SEAMLESS_BZ2
-        && magic.b16[0] == BZIP2_MAGIC
-       ) {
-# if BB_MMU
-               xformer = unpack_bz2_stream;
-# else
-               xformer_prog = "bunzip2";
-# endif
-               goto found_magic;
-       }
-       if (ENABLE_FEATURE_SEAMLESS_XZ
-        && magic.b16[0] == XZ_MAGIC1
-       ) {
-               offset = -6;
-               xread(fd, magic.b32, sizeof(magic.b32[0]));
-               if (magic.b32[0] == XZ_MAGIC2) {
-# if BB_MMU
-                       xformer = unpack_xz_stream;
-                       /* unpack_xz_stream wants fd at position 6, no need to seek */
-                       //xlseek(fd, offset, SEEK_CUR);
-# else
-                       xformer_prog = "unxz";
-# endif
-                       goto found_magic;
-               }
-       }
-
-       /* No known magic seen */
-       if (fail_if_not_detected)
-               bb_error_msg_and_die("no gzip"
-                       IF_FEATURE_SEAMLESS_BZ2("/bzip2")
-                       IF_FEATURE_SEAMLESS_XZ("/xz")
-                       " magic");
-       xlseek(fd, offset, SEEK_CUR);
-       return;
-
- found_magic:
-# if !BB_MMU
-       /* NOMMU version of open_transformer execs
-        * an external unzipper that wants
-        * file position at the start of the file */
-       xlseek(fd, offset, SEEK_CUR);
-# endif
-       open_transformer(fd, xformer, xformer_prog);
-}
-#endif /* ZIPPED */
-
-int FAST_FUNC open_zipped(const char *fname)
-{
-#if !ZIPPED
-       return open(fname, O_RDONLY);
-#else
-       char *sfx;
-       int fd;
-
-       fd = open(fname, O_RDONLY);
-       if (fd < 0)
-               return fd;
-
-       sfx = strrchr(fname, '.');
-       if (sfx) {
-               sfx++;
-               if (ENABLE_FEATURE_SEAMLESS_LZMA && strcmp(sfx, "lzma") == 0)
-                       /* .lzma has no header/signature, just trust it */
-                       open_transformer(fd, unpack_lzma_stream, "unlzma");
-               else
-               if ((ENABLE_FEATURE_SEAMLESS_GZ && strcmp(sfx, "gz") == 0)
-                || (ENABLE_FEATURE_SEAMLESS_BZ2 && strcmp(sfx, "bz2") == 0)
-                || (ENABLE_FEATURE_SEAMLESS_XZ && strcmp(sfx, "xz") == 0)
-               ) {
-                       setup_unzip_on_fd(fd /*, fail_if_not_detected: 1*/);
-               }
-       }
-
-       return fd;
-#endif
-}
-
-void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p)
-{
-       int fd;
-       char *image;
-
-       fd = open_zipped(fname);
-       if (fd < 0)
-               return NULL;
-
-       image = xmalloc_read(fd, maxsz_p);
-       if (!image)
-               bb_perror_msg("read error from '%s'", fname);
-       close(fd);
-
-       return image;
-}
index 57262cd..b5cf7c0 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index da14885..5b75f7f 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -33,7 +33,7 @@ int FAST_FUNC remove_file(const char *path, int flags)
                int status = 0;
 
                if (!(flags & FILEUTILS_RECUR)) {
-                       bb_error_msg("%s: is a directory", path);
+                       bb_error_msg("'%s' is a directory", path);
                        return -1;
                }
 
index fcd6c64..97455e8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Common RTC functions
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -22,13 +22,6 @@ int FAST_FUNC rtc_adjtime_is_utc(void)
                char buffer[128];
 
                while (fgets(buffer, sizeof(buffer), f)) {
-                       int len = strlen(buffer);
-
-                       while (len && isspace(buffer[len - 1]))
-                               len--;
-
-                       buffer[len] = 0;
-
                        if (strncmp(buffer, "UTC", 3) == 0) {
                                utc = 1;
                                break;
index 05e0954..cac99ae 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2008 Tito Ragusa <farmatito@tiscali.it>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*
@@ -50,25 +50,3 @@ char* FAST_FUNC safe_gethostname(void)
        uname(&uts);
        return xstrndup(!uts.nodename[0] ? "?" : uts.nodename, sizeof(uts.nodename));
 }
-
-/*
- * On success return the current malloced and NUL terminated domainname.
- * On error return malloced and NUL terminated string "?".
- * This is an illegal first character for a domainname.
- * The returned malloced string must be freed by the caller.
- */
-char* FAST_FUNC safe_getdomainname(void)
-{
-#if defined(__linux__)
-/* The field domainname of struct utsname is Linux specific. */
-       struct utsname uts;
-       uname(&uts);
-       return xstrndup(!uts.domainname[0] ? "?" : uts.domainname, sizeof(uts.domainname));
-#else
-       /* We really don't care about people with domain names wider than most screens */
-       char buf[256];
-       int r = getdomainname(buf, sizeof(buf));
-       buf[sizeof(buf)-1] = '\0';
-       return xstrdup(r < 0 ? "?" : buf);
-#endif
-}
index 58c7bda..b492a81 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2007 by Denys Vlasenko <vda.linux@googlemail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index 4acd976..5eb0db0 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -20,8 +20,13 @@ char* FAST_FUNC safe_strncpy(char *dst, const char *src, size_t size)
 /* Like strcpy but can copy overlapping strings. */
 void FAST_FUNC overlapping_strcpy(char *dst, const char *src)
 {
-       while ((*dst = *src) != '\0') {
-               dst++;
-               src++;
+       /* Cheap optimization for dst == src case -
+        * better to have it here than in many callers.
+        */
+       if (dst != src) {
+               while ((*dst = *src) != '\0') {
+                       dst++;
+                       src++;
+               }
        }
 }
index e3561f3..8f76280 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index 7b56967..c258555 100644 (file)
@@ -4,13 +4,13 @@
  *
  * Copyright 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 #include <selinux/context.h>
 
 context_t FAST_FUNC set_security_context_component(security_context_t cur_context,
-                                        char *user, char *role, char *type, char *range)
+                       char *user, char *role, char *type, char *range)
 {
        context_t con = context_new(cur_context);
        if (!con)
@@ -53,4 +53,3 @@ void FAST_FUNC selinux_preserve_fcontext(int fdesc)
        setfscreatecon_or_die(context);
        freecon(context);
 }
-
index a95fbc5..4258656 100644 (file)
 
 void FAST_FUNC setup_environment(const char *shell, int flags, const struct passwd *pw)
 {
+       if (!shell || !shell[0])
+               shell = DEFAULT_SHELL;
+
        /* Change the current working directory to be the home directory
         * of the user */
-       if (chdir(pw->pw_dir)) {
-               xchdir((flags & SETUP_ENV_TO_TMP) ? "/tmp" : "/");
-               bb_error_msg("can't chdir to home directory '%s'", pw->pw_dir);
+       if (!(flags & SETUP_ENV_NO_CHDIR)) {
+               if (chdir(pw->pw_dir) != 0) {
+                       bb_error_msg("can't change directory to '%s'", pw->pw_dir);
+                       xchdir((flags & SETUP_ENV_TO_TMP) ? "/tmp" : "/");
+               }
        }
 
        if (flags & SETUP_ENV_CLEARENV) {
diff --git a/libbb/sha1.c b/libbb/sha1.c
deleted file mode 100644 (file)
index 5f42532..0000000
+++ /dev/null
@@ -1,480 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * Based on shasum from http://www.netsw.org/crypto/hash/
- * Majorly hacked up to use Dr Brian Gladman's sha1 code
- *
- * Copyright (C) 2002 Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
- * Copyright (C) 2003 Glenn L. McGrath
- * Copyright (C) 2003 Erik Andersen
- *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
- *
- * ---------------------------------------------------------------------------
- * Issue Date: 10/11/2002
- *
- * This is a byte oriented version of SHA1 that operates on arrays of bytes
- * stored in memory. It runs at 22 cycles per byte on a Pentium P4 processor
- *
- * ---------------------------------------------------------------------------
- *
- * SHA256 and SHA512 parts are:
- * Released into the Public Domain by Ulrich Drepper <drepper@redhat.com>.
- * Shrank by Denys Vlasenko.
- *
- * ---------------------------------------------------------------------------
- *
- * The best way to test random blocksizes is to go to coreutils/md5_sha1_sum.c
- * and replace "4096" with something like "2000 + time(NULL) % 2097",
- * then rebuild and compare "shaNNNsum bigfile" results.
- */
-
-#include "libbb.h"
-
-#define rotl32(x,n) (((x) << (n)) | ((x) >> (32 - (n))))
-#define rotr32(x,n) (((x) >> (n)) | ((x) << (32 - (n))))
-/* for sha512: */
-#define rotr64(x,n) (((x) >> (n)) | ((x) << (64 - (n))))
-#if BB_LITTLE_ENDIAN
-static inline uint64_t hton64(uint64_t v)
-{
-       return (((uint64_t)htonl(v)) << 32) | htonl(v >> 32);
-}
-#else
-#define hton64(v) (v)
-#endif
-#define ntoh64(v) hton64(v)
-
-/* To check alignment gcc has an appropriate operator.  Other
-   compilers don't.  */
-#if defined(__GNUC__) && __GNUC__ >= 2
-# define UNALIGNED_P(p,type) (((uintptr_t) p) % __alignof__(type) != 0)
-#else
-# define UNALIGNED_P(p,type) (((uintptr_t) p) % sizeof(type) != 0)
-#endif
-
-
-/* Some arch headers have conflicting defines */
-#undef ch
-#undef parity
-#undef maj
-#undef rnd
-
-static void FAST_FUNC sha1_process_block64(sha1_ctx_t *ctx)
-{
-       unsigned t;
-       uint32_t W[80], a, b, c, d, e;
-       const uint32_t *words = (uint32_t*) ctx->wbuffer;
-
-       for (t = 0; t < 16; ++t) {
-               W[t] = ntohl(*words);
-               words++;
-       }
-
-       for (/*t = 16*/; t < 80; ++t) {
-               uint32_t T = W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16];
-               W[t] = rotl32(T, 1);
-       }
-
-       a = ctx->hash[0];
-       b = ctx->hash[1];
-       c = ctx->hash[2];
-       d = ctx->hash[3];
-       e = ctx->hash[4];
-
-/* Reverse byte order in 32-bit words   */
-#define ch(x,y,z)        ((z) ^ ((x) & ((y) ^ (z))))
-#define parity(x,y,z)    ((x) ^ (y) ^ (z))
-#define maj(x,y,z)       (((x) & (y)) | ((z) & ((x) | (y))))
-/* A normal version as set out in the FIPS. This version uses   */
-/* partial loop unrolling and is optimised for the Pentium 4    */
-#define rnd(f,k) \
-       do { \
-               uint32_t T = a; \
-               a = rotl32(a, 5) + f(b, c, d) + e + k + W[t]; \
-               e = d; \
-               d = c; \
-               c = rotl32(b, 30); \
-               b = T; \
-       } while (0)
-
-       for (t = 0; t < 20; ++t)
-               rnd(ch, 0x5a827999);
-
-       for (/*t = 20*/; t < 40; ++t)
-               rnd(parity, 0x6ed9eba1);
-
-       for (/*t = 40*/; t < 60; ++t)
-               rnd(maj, 0x8f1bbcdc);
-
-       for (/*t = 60*/; t < 80; ++t)
-               rnd(parity, 0xca62c1d6);
-#undef ch
-#undef parity
-#undef maj
-#undef rnd
-
-       ctx->hash[0] += a;
-       ctx->hash[1] += b;
-       ctx->hash[2] += c;
-       ctx->hash[3] += d;
-       ctx->hash[4] += e;
-}
-
-/* Constants for SHA512 from FIPS 180-2:4.2.3.
- * SHA256 constants from FIPS 180-2:4.2.2
- * are the most significant half of first 64 elements
- * of the same array.
- */
-static const uint64_t sha_K[80] = {
-       0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
-       0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
-       0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
-       0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
-       0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
-       0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
-       0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
-       0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
-       0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
-       0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
-       0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
-       0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
-       0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
-       0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
-       0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
-       0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
-       0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
-       0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
-       0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
-       0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
-       0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
-       0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
-       0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
-       0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
-       0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
-       0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
-       0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
-       0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
-       0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
-       0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
-       0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
-       0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
-       0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, /* [64]+ are used for sha512 only */
-       0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
-       0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
-       0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
-       0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
-       0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
-       0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
-       0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
-};
-
-#undef Ch
-#undef Maj
-#undef S0
-#undef S1
-#undef R0
-#undef R1
-
-static void FAST_FUNC sha256_process_block64(sha256_ctx_t *ctx)
-{
-       unsigned t;
-       uint32_t W[64], a, b, c, d, e, f, g, h;
-       const uint32_t *words = (uint32_t*) ctx->wbuffer;
-
-       /* Operators defined in FIPS 180-2:4.1.2.  */
-#define Ch(x, y, z) ((x & y) ^ (~x & z))
-#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
-#define S0(x) (rotr32(x, 2) ^ rotr32(x, 13) ^ rotr32(x, 22))
-#define S1(x) (rotr32(x, 6) ^ rotr32(x, 11) ^ rotr32(x, 25))
-#define R0(x) (rotr32(x, 7) ^ rotr32(x, 18) ^ (x >> 3))
-#define R1(x) (rotr32(x, 17) ^ rotr32(x, 19) ^ (x >> 10))
-
-       /* Compute the message schedule according to FIPS 180-2:6.2.2 step 2.  */
-       for (t = 0; t < 16; ++t) {
-               W[t] = ntohl(*words);
-               words++;
-       }
-
-       for (/*t = 16*/; t < 64; ++t)
-               W[t] = R1(W[t - 2]) + W[t - 7] + R0(W[t - 15]) + W[t - 16];
-
-       a = ctx->hash[0];
-       b = ctx->hash[1];
-       c = ctx->hash[2];
-       d = ctx->hash[3];
-       e = ctx->hash[4];
-       f = ctx->hash[5];
-       g = ctx->hash[6];
-       h = ctx->hash[7];
-
-       /* The actual computation according to FIPS 180-2:6.2.2 step 3.  */
-       for (t = 0; t < 64; ++t) {
-               /* Need to fetch upper half of sha_K[t]
-                * (I hope compiler is clever enough to just fetch
-                * upper half)
-                */
-               uint32_t K_t = sha_K[t] >> 32;
-               uint32_t T1 = h + S1(e) + Ch(e, f, g) + K_t + W[t];
-               uint32_t T2 = S0(a) + Maj(a, b, c);
-               h = g;
-               g = f;
-               f = e;
-               e = d + T1;
-               d = c;
-               c = b;
-               b = a;
-               a = T1 + T2;
-       }
-#undef Ch
-#undef Maj
-#undef S0
-#undef S1
-#undef R0
-#undef R1
-       /* Add the starting values of the context according to FIPS 180-2:6.2.2
-          step 4.  */
-       ctx->hash[0] += a;
-       ctx->hash[1] += b;
-       ctx->hash[2] += c;
-       ctx->hash[3] += d;
-       ctx->hash[4] += e;
-       ctx->hash[5] += f;
-       ctx->hash[6] += g;
-       ctx->hash[7] += h;
-}
-
-static void FAST_FUNC sha512_process_block128(sha512_ctx_t *ctx)
-{
-       unsigned t;
-       uint64_t W[80];
-       /* On i386, having assignments here (not later as sha256 does)
-        * produces 99 bytes smaller code with gcc 4.3.1
-        */
-       uint64_t a = ctx->hash[0];
-       uint64_t b = ctx->hash[1];
-       uint64_t c = ctx->hash[2];
-       uint64_t d = ctx->hash[3];
-       uint64_t e = ctx->hash[4];
-       uint64_t f = ctx->hash[5];
-       uint64_t g = ctx->hash[6];
-       uint64_t h = ctx->hash[7];
-       const uint64_t *words = (uint64_t*) ctx->wbuffer;
-
-       /* Operators defined in FIPS 180-2:4.1.2.  */
-#define Ch(x, y, z) ((x & y) ^ (~x & z))
-#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
-#define S0(x) (rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39))
-#define S1(x) (rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41))
-#define R0(x) (rotr64(x, 1) ^ rotr64(x, 8) ^ (x >> 7))
-#define R1(x) (rotr64(x, 19) ^ rotr64(x, 61) ^ (x >> 6))
-
-       /* Compute the message schedule according to FIPS 180-2:6.3.2 step 2.  */
-       for (t = 0; t < 16; ++t) {
-               W[t] = ntoh64(*words);
-               words++;
-       }
-       for (/*t = 16*/; t < 80; ++t)
-               W[t] = R1(W[t - 2]) + W[t - 7] + R0(W[t - 15]) + W[t - 16];
-
-       /* The actual computation according to FIPS 180-2:6.3.2 step 3.  */
-       for (t = 0; t < 80; ++t) {
-               uint64_t T1 = h + S1(e) + Ch(e, f, g) + sha_K[t] + W[t];
-               uint64_t T2 = S0(a) + Maj(a, b, c);
-               h = g;
-               g = f;
-               f = e;
-               e = d + T1;
-               d = c;
-               c = b;
-               b = a;
-               a = T1 + T2;
-       }
-#undef Ch
-#undef Maj
-#undef S0
-#undef S1
-#undef R0
-#undef R1
-       /* Add the starting values of the context according to FIPS 180-2:6.3.2
-          step 4.  */
-       ctx->hash[0] += a;
-       ctx->hash[1] += b;
-       ctx->hash[2] += c;
-       ctx->hash[3] += d;
-       ctx->hash[4] += e;
-       ctx->hash[5] += f;
-       ctx->hash[6] += g;
-       ctx->hash[7] += h;
-}
-
-
-void FAST_FUNC sha1_begin(sha1_ctx_t *ctx)
-{
-       ctx->hash[0] = 0x67452301;
-       ctx->hash[1] = 0xefcdab89;
-       ctx->hash[2] = 0x98badcfe;
-       ctx->hash[3] = 0x10325476;
-       ctx->hash[4] = 0xc3d2e1f0;
-       ctx->total64 = 0;
-       ctx->process_block = sha1_process_block64;
-}
-
-static const uint32_t init256[] = {
-       0x6a09e667,
-       0xbb67ae85,
-       0x3c6ef372,
-       0xa54ff53a,
-       0x510e527f,
-       0x9b05688c,
-       0x1f83d9ab,
-       0x5be0cd19
-};
-static const uint32_t init512_lo[] = {
-       0xf3bcc908,
-       0x84caa73b,
-       0xfe94f82b,
-       0x5f1d36f1,
-       0xade682d1,
-       0x2b3e6c1f,
-       0xfb41bd6b,
-       0x137e2179
-};
-
-/* Initialize structure containing state of computation.
-   (FIPS 180-2:5.3.2)  */
-void FAST_FUNC sha256_begin(sha256_ctx_t *ctx)
-{
-       memcpy(ctx->hash, init256, sizeof(init256));
-       ctx->total64 = 0;
-       ctx->process_block = sha256_process_block64;
-}
-
-/* Initialize structure containing state of computation.
-   (FIPS 180-2:5.3.3)  */
-void FAST_FUNC sha512_begin(sha512_ctx_t *ctx)
-{
-       int i;
-       for (i = 0; i < 8; i++)
-               ctx->hash[i] = ((uint64_t)(init256[i]) << 32) + init512_lo[i];
-       ctx->total64[0] = ctx->total64[1] = 0;
-}
-
-
-/* Used also for sha256 */
-void FAST_FUNC sha1_hash(const void *buffer, size_t len, sha1_ctx_t *ctx)
-{
-       unsigned in_buf = ctx->total64 & 63;
-       unsigned add = 64 - in_buf;
-
-       ctx->total64 += len;
-
-       while (len >= add) {    /* transfer whole blocks while possible  */
-               memcpy(ctx->wbuffer + in_buf, buffer, add);
-               buffer = (const char *)buffer + add;
-               len -= add;
-               add = 64;
-               in_buf = 0;
-               ctx->process_block(ctx);
-       }
-
-       memcpy(ctx->wbuffer + in_buf, buffer, len);
-}
-
-void FAST_FUNC sha512_hash(const void *buffer, size_t len, sha512_ctx_t *ctx)
-{
-       unsigned in_buf = ctx->total64[0] & 127;
-       unsigned add = 128 - in_buf;
-
-       /* First increment the byte count.  FIPS 180-2 specifies the possible
-          length of the file up to 2^128 _bits_.
-          We compute the number of _bytes_ and convert to bits later.  */
-       ctx->total64[0] += len;
-       if (ctx->total64[0] < len)
-               ctx->total64[1]++;
-
-       while (len >= add) {    /* transfer whole blocks while possible  */
-               memcpy(ctx->wbuffer + in_buf, buffer, add);
-               buffer = (const char *)buffer + add;
-               len -= add;
-               add = 128;
-               in_buf = 0;
-               sha512_process_block128(ctx);
-       }
-
-       memcpy(ctx->wbuffer + in_buf, buffer, len);
-}
-
-
-/* Used also for sha256 */
-void FAST_FUNC sha1_end(void *resbuf, sha1_ctx_t *ctx)
-{
-       unsigned pad, in_buf;
-
-       in_buf = ctx->total64 & 63;
-       /* Pad the buffer to the next 64-byte boundary with 0x80,0,0,0... */
-       ctx->wbuffer[in_buf++] = 0x80;
-
-       /* This loop iterates either once or twice, no more, no less */
-       while (1) {
-               pad = 64 - in_buf;
-               memset(ctx->wbuffer + in_buf, 0, pad);
-               in_buf = 0;
-               /* Do we have enough space for the length count? */
-               if (pad >= 8) {
-                       /* Store the 64-bit counter of bits in the buffer in BE format */
-                       uint64_t t = ctx->total64 << 3;
-                       t = hton64(t);
-                       /* wbuffer is suitably aligned for this */
-                       *(uint64_t *) (&ctx->wbuffer[64 - 8]) = t;
-               }
-               ctx->process_block(ctx);
-               if (pad >= 8)
-                       break;
-       }
-
-       in_buf = (ctx->process_block == sha1_process_block64) ? 5 : 8;
-       /* This way we do not impose alignment constraints on resbuf: */
-       if (BB_LITTLE_ENDIAN) {
-               unsigned i;
-               for (i = 0; i < in_buf; ++i)
-                       ctx->hash[i] = htonl(ctx->hash[i]);
-       }
-       memcpy(resbuf, ctx->hash, sizeof(ctx->hash[0]) * in_buf);
-}
-
-void FAST_FUNC sha512_end(void *resbuf, sha512_ctx_t *ctx)
-{
-       unsigned pad, in_buf;
-
-       in_buf = ctx->total64[0] & 127;
-       /* Pad the buffer to the next 128-byte boundary with 0x80,0,0,0...
-        * (FIPS 180-2:5.1.2)
-        */
-       ctx->wbuffer[in_buf++] = 0x80;
-
-       while (1) {
-               pad = 128 - in_buf;
-               memset(ctx->wbuffer + in_buf, 0, pad);
-               in_buf = 0;
-               if (pad >= 16) {
-                       /* Store the 128-bit counter of bits in the buffer in BE format */
-                       uint64_t t;
-                       t = ctx->total64[0] << 3;
-                       t = hton64(t);
-                       *(uint64_t *) (&ctx->wbuffer[128 - 8]) = t;
-                       t = (ctx->total64[1] << 3) | (ctx->total64[0] >> 61);
-                       t = hton64(t);
-                       *(uint64_t *) (&ctx->wbuffer[128 - 16]) = t;
-               }
-               sha512_process_block128(ctx);
-               if (pad >= 16)
-                       break;
-       }
-
-       if (BB_LITTLE_ENDIAN) {
-               unsigned i;
-               for (i = 0; i < ARRAY_SIZE(ctx->hash); ++i)
-                       ctx->hash[i] = hton64(ctx->hash[i]);
-       }
-       memcpy(resbuf, ctx->hash, sizeof(ctx->hash));
-}
index a528756..5651247 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (C) 2006 Rob Landley
  * Copyright (C) 2006 Denys Vlasenko
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -39,7 +39,7 @@ void FAST_FUNC bb_signals(int sigs, void (*f)(int))
 
        while (sigs) {
                if (sigs & bit) {
-                       sigs &= ~bit;
+                       sigs -= bit;
                        signal(sig_no, f);
                }
                sig_no++;
@@ -60,7 +60,7 @@ void FAST_FUNC bb_signals_recursive_norestart(int sigs, void (*f)(int))
 
        while (sigs) {
                if (sigs & bit) {
-                       sigs &= ~bit;
+                       sigs -= bit;
                        sigaction_set(sig_no, &sa);
                }
                sig_no++;
@@ -97,7 +97,7 @@ void FAST_FUNC kill_myself_with_sig(int sig)
        signal(sig, SIG_DFL);
        sig_unblock(sig);
        raise(sig);
-       _exit(EXIT_FAILURE); /* Should not reach it */
+       _exit(sig | 128); /* Should not reach it */
 }
 
 void FAST_FUNC signal_SA_RESTART_empty_mask(int sig, void (*handler)(int))
index f80e3e8..89dc5bd 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2001  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
@@ -15,17 +15,17 @@ char* FAST_FUNC bb_simplify_abs_path_inplace(char *start)
        p = s = start;
        do {
                if (*p == '/') {
-                       if (*s == '/') {        /* skip duplicate (or initial) slash */
+                       if (*s == '/') {  /* skip duplicate (or initial) slash */
                                continue;
                        }
                        if (*s == '.') {
-                               if (s[1] == '/' || !s[1]) {     /* remove extra '.' */
+                               if (s[1] == '/' || !s[1]) {  /* remove extra '.' */
                                        continue;
                                }
                                if ((s[1] == '.') && (s[2] == '/' || !s[2])) {
                                        ++s;
                                        if (p > start) {
-                                               while (*--p != '/')     /* omit previous dir */
+                                               while (*--p != '/')  /* omit previous dir */
                                                        continue;
                                        }
                                        continue;
@@ -35,8 +35,8 @@ char* FAST_FUNC bb_simplify_abs_path_inplace(char *start)
                *++p = *s;
        } while (*++s);
 
-       if ((p == start) || (*p != '/')) {      /* not a trailing slash */
-               ++p;                                    /* so keep last character */
+       if ((p == start) || (*p != '/')) {  /* not a trailing slash */
+               ++p;  /* so keep last character */
        }
        *p = '\0';
        return p;
index 6173c88..64844dd 100644 (file)
@@ -4,12 +4,14 @@
  *
  * Copyright (C) 2009 Denys Vlasenko
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
 char* FAST_FUNC single_argv(char **argv)
 {
+       if (argv[1] && strcmp(argv[1], "--") == 0)
+               argv++;
        if (!argv[1] || argv[2])
                bb_show_usage();
        return argv[1];
index f5a61a3..8c7b674 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index af676e1..45159f1 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -29,12 +29,12 @@ static const struct speed_map speeds[] = {
        {B2400, 2400},
        {B4800, 4800},
        {B9600, 9600},
-#ifdef B19200
+#ifdef B19200
        {B19200, 19200},
 #elif defined(EXTA)
        {EXTA, 19200},
 #endif
-#ifdef B38400
+#ifdef B38400
        {B38400, 38400/256 + 0x8000U},
 #elif defined(EXTB)
        {EXTB, 38400/256 + 0x8000U},
index f402e8e..c2d5637 100644 (file)
@@ -1,7 +1,7 @@
 /* vi set: sw=4 ts=4: */
 /* Convert string str to lowercase, return str.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
index a803dd9..d8823fc 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2008 Bernhard Reutner-Fischer
  *
- * Licensed under GPLv2 or later, see file License in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #ifdef __DO_STRRSTR_TEST
diff --git a/libbb/systemd_support.c b/libbb/systemd_support.c
new file mode 100644 (file)
index 0000000..542a3ef
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 Davide Cavalca <davide@geexbox.org>
+ *
+ * Based on http://cgit.freedesktop.org/systemd/tree/src/sd-daemon.c
+ * Copyright 2010 Lennart Poettering
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "libbb.h"
+
+//config:config FEATURE_SYSTEMD
+//config:      bool "Enable systemd support"
+//config:      default y
+//config:      help
+//config:        If you plan to use busybox daemons on a system where daemons
+//config:        are controlled by systemd, enable this option.
+//config:        If you don't use systemd, it is still safe to enable it,
+//config:        but the downside is increased code size.
+
+//kbuild:lib-$(CONFIG_FEATURE_SYSTEMD) += systemd_support.o
+
+int sd_listen_fds(void)
+{
+       const char *e;
+       int n;
+       int fd;
+
+       e = getenv("LISTEN_PID");
+       if (!e)
+               return 0;
+       n = xatoi_positive(e);
+       /* Is this for us? */
+       if (getpid() != (pid_t) n)
+               return 0;
+
+       e = getenv("LISTEN_FDS");
+       if (!e)
+               return 0;
+       n = xatoi_positive(e);
+       for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++)
+               close_on_exec_on(fd);
+
+       return n;
+}
index 8d176e5..ea2f72e 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2007 Denys Vlasenko
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
@@ -23,14 +23,16 @@ void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
                if (sscanf(date_str, "%u:%u%c",
                                        &ptm->tm_hour,
                                        &ptm->tm_min,
-                                       &end) >= 2) {
+                                       &end) >= 2
+               ) {
                        /* no adjustments needed */
                } else
                /* mm.dd-HH:MM */
                if (sscanf(date_str, "%u.%u-%u:%u%c",
                                        &ptm->tm_mon, &ptm->tm_mday,
                                        &ptm->tm_hour, &ptm->tm_min,
-                                       &end) >= 4) {
+                                       &end) >= 4
+               ) {
                        /* Adjust month from 1-12 to 0-11 */
                        ptm->tm_mon -= 1;
                } else
@@ -38,15 +40,13 @@ void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
                if (sscanf(date_str, "%u.%u.%u-%u:%u%c", &ptm->tm_year,
                                        &ptm->tm_mon, &ptm->tm_mday,
                                        &ptm->tm_hour, &ptm->tm_min,
-                                       &end) >= 5) {
-                       ptm->tm_year -= 1900; /* Adjust years */
-                       ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */
-               } else
+                                       &end) >= 5
                /* yyyy-mm-dd HH:MM */
-               if (sscanf(date_str, "%u-%u-%u %u:%u%c", &ptm->tm_year,
+                || sscanf(date_str, "%u-%u-%u %u:%u%c", &ptm->tm_year,
                                        &ptm->tm_mon, &ptm->tm_mday,
                                        &ptm->tm_hour, &ptm->tm_min,
-                                       &end) >= 5) {
+                                       &end) >= 5
+               ) {
                        ptm->tm_year -= 1900; /* Adjust years */
                        ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */
                } else
@@ -58,7 +58,6 @@ void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
                        return; /* don't fall through to end == ":" check */
                } else
 #endif
-//TODO: coreutils 6.9 also accepts "yyyy-mm-dd HH" (no minutes)
                {
                        bb_error_msg_and_die(bb_msg_invalid_date, date_str);
                }
@@ -68,7 +67,21 @@ void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
                                end = '\0';
                        /* else end != NUL and we error out */
                }
-       } else if (date_str[0] == '@') {
+       } else
+       /* yyyy-mm-dd HH */
+       if (sscanf(date_str, "%u-%u-%u %u%c", &ptm->tm_year,
+                               &ptm->tm_mon, &ptm->tm_mday,
+                               &ptm->tm_hour,
+                               &end) >= 4
+       /* yyyy-mm-dd */
+        || sscanf(date_str, "%u-%u-%u%c", &ptm->tm_year,
+                               &ptm->tm_mon, &ptm->tm_mday,
+                               &end) >= 3
+       ) {
+               ptm->tm_year -= 1900; /* Adjust years */
+               ptm->tm_mon -= 1; /* Adjust month from 1-12 to 0-11 */
+       } else
+       if (date_str[0] == '@') {
                time_t t = bb_strtol(date_str + 1, NULL, 10);
                if (!errno) {
                        struct tm *lt = localtime(&t);
@@ -91,8 +104,15 @@ void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
                 * .SS  Seconds, a number from 0 to 61 (with leap seconds)
                 * Everything but the minutes is optional
                 *
-                * This coincides with the format of "touch -t TIME"
+                * "touch -t DATETIME" format: [[[[[YY]YY]MM]DD]hh]mm[.ss]
+                * Some, but not all, Unix "date DATETIME" commands
+                * move [[YY]YY] past minutes mm field (!).
+                * Coreutils date does it, and SUS mandates it.
+                * (date -s DATETIME does not support this format. lovely!)
+                * In bbox, this format is special-cased in date applet
+                * (IOW: this function assumes "touch -t" format).
                 */
+               unsigned cur_year = ptm->tm_year;
                int len = strchrnul(date_str, '.') - date_str;
 
                /* MM[.SS] */
@@ -133,6 +153,17 @@ void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
                                        &end) >= 5) {
                        /* Adjust month from 1-12 to 0-11 */
                        ptm->tm_mon -= 1;
+                       if ((int)cur_year >= 50) { /* >= 1950 */
+                               /* Adjust year: */
+                               /* 1. Put it in the current century */
+                               ptm->tm_year += (cur_year / 100) * 100;
+                               /* 2. If too far in the past, +100 years */
+                               if (ptm->tm_year < cur_year - 50)
+                                       ptm->tm_year += 100;
+                               /* 3. If too far in the future, -100 years */
+                               if (ptm->tm_year > cur_year + 50)
+                                       ptm->tm_year -= 100;
+                       }
                } else
                /* ccyymmddHHMM[.SS] */
                if (len == 12 && sscanf(date_str, "%4u%2u%2u%2u%2u%c",
@@ -169,6 +200,27 @@ time_t FAST_FUNC validate_tm_time(const char *date_str, struct tm *ptm)
        return t;
 }
 
+static char* strftime_fmt(char *buf, unsigned len, time_t *tp, const char *fmt)
+{
+       time_t t;
+       if (!tp) {
+               tp = &t;
+               time(tp);
+       }
+       /* Returns pointer to NUL */
+       return buf + strftime(buf, len, fmt, localtime(tp));
+}
+
+char* FAST_FUNC strftime_HHMMSS(char *buf, unsigned len, time_t *tp)
+{
+       return strftime_fmt(buf, len, tp, "%H:%M:%S");
+}
+
+char* FAST_FUNC strftime_YYYYMMDDHHMMSS(char *buf, unsigned len, time_t *tp)
+{
+       return strftime_fmt(buf, len, tp, "%Y-%m-%d %H:%M:%S");
+}
+
 #if ENABLE_MONOTONIC_SYSCALL
 
 #include <sys/syscall.h>
index df00b84..16cb4fb 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) many different people.
  * If you wrote this, please acknowledge your work.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index 915eea5..8c78f5e 100644 (file)
@@ -4,9 +4,16 @@
  *
  * Copyright 2006 Rob Landley <rob@landley.net>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//config:config FEATURE_RTMINMAX
+//config:      bool "Support RTMIN[+n] and RTMAX[-n] signal names"
+//config:      default y
+//config:      help
+//config:        Support RTMIN[+n] and RTMAX[-n] signal names
+//config:        in kill, killall etc. This costs ~250 bytes.
+
 #include "libbb.h"
 
 /* Believe it or not, but some arches have more than 32 SIGs!
@@ -117,6 +124,16 @@ static const char signals[][7] = {
 #ifdef SIGSYS
        [SIGSYS   ] = "SYS",
 #endif
+#if ENABLE_FEATURE_RTMINMAX
+# ifdef __SIGRTMIN
+       [__SIGRTMIN] = "RTMIN",
+# endif
+// This makes array about x2 bigger.
+// More compact approach is to special-case SIGRTMAX in print_signames()
+//# ifdef __SIGRTMAX
+//     [__SIGRTMAX] = "RTMAX",
+//# endif
+#endif
 };
 
 // Convert signal name to number.
@@ -134,20 +151,54 @@ int FAST_FUNC get_signum(const char *name)
                if (strcasecmp(name, signals[i]) == 0)
                        return i;
 
-#if ENABLE_DESKTOP && (defined(SIGIOT) || defined(SIGIO))
+#if ENABLE_DESKTOP
+# if defined(SIGIOT) || defined(SIGIO)
        /* SIGIO[T] are aliased to other names,
         * thus cannot be stored in the signals[] array.
         * Need special code to recognize them */
        if ((name[0] | 0x20) == 'i' && (name[1] | 0x20) == 'o') {
-#ifdef SIGIO
+#  ifdef SIGIO
                if (!name[2])
                        return SIGIO;
-#endif
-#ifdef SIGIOT
+#  endif
+#  ifdef SIGIOT
                if ((name[2] | 0x20) == 't' && !name[3])
                        return SIGIOT;
-#endif
+#  endif
        }
+# endif
+#endif
+
+#if ENABLE_FEATURE_RTMINMAX
+# if defined(SIGRTMIN) && defined(SIGRTMAX)
+/* libc may use some rt sigs for pthreads and therefore "remap" SIGRTMIN/MAX,
+ * but we want to use "raw" SIGRTMIN/MAX. Underscored names, if exist, provide
+ * them. If they don't exist, fall back to non-underscored ones: */
+#  if !defined(__SIGRTMIN)
+#   define __SIGRTMIN SIGRTMIN
+#  endif
+#  if !defined(__SIGRTMAX)
+#   define __SIGRTMAX SIGRTMAX
+#  endif
+       if (strncasecmp(name, "RTMIN", 5) == 0) {
+               if (!name[5])
+                       return __SIGRTMIN;
+               if (name[5] == '+') {
+                       i = bb_strtou(name + 6, NULL, 10);
+                       if (!errno && i <= __SIGRTMAX - __SIGRTMIN)
+                               return __SIGRTMIN + i;
+               }
+       }
+       else if (strncasecmp(name, "RTMAX", 5) == 0) {
+               if (!name[5])
+                       return __SIGRTMAX;
+               if (name[5] == '-') {
+                       i = bb_strtou(name + 6, NULL, 10);
+                       if (!errno && i <= __SIGRTMAX - __SIGRTMIN)
+                               return __SIGRTMAX - i;
+               }
+       }
+# endif
 #endif
 
        return -1;
@@ -175,6 +226,11 @@ void FAST_FUNC print_signames(void)
        for (signo = 1; signo < ARRAY_SIZE(signals); signo++) {
                const char *name = signals[signo];
                if (name[0])
-                       puts(name);
+                       printf("%2u) %s\n", signo, name);
        }
+#if ENABLE_FEATURE_RTMINMAX
+# ifdef __SIGRTMAX
+       printf("%2u) %s\n", __SIGRTMAX, "RTMAX");
+# endif
+#endif
 }
index 24237be..7985a97 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2007 Denys Vlasenko
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
@@ -13,7 +13,7 @@
  * We don't check for errors here. Not supported == won't be used
  */
 void FAST_FUNC
-socket_want_pktinfo(int fd)
+socket_want_pktinfo(int fd UNUSED_PARAM)
 {
 #ifdef IP_PKTINFO
        setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &const_int_1, sizeof(int));
index d6fcf7a..9c4da50 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2009 Denys Vlasenko
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 #include "unicode.h"
@@ -23,37 +23,66 @@ uint8_t unicode_status;
 
 /* Unicode support using libc locale support. */
 
-void FAST_FUNC init_unicode(void)
+void FAST_FUNC reinit_unicode(const char *LANG)
 {
        static const char unicode_0x394[] = { 0xce, 0x94, 0 };
        size_t width;
 
-       if (unicode_status != UNICODE_UNKNOWN)
-               return;
+       /* We pass "" instead of "C" because some libc's have
+        * non-ASCII default locale for setlocale("") call
+        * (this allows users of such libc to have Unicoded
+        * system without having to mess with env).
+        *
+        * We set LC_CTYPE because (a) we may be called with $LC_CTYPE
+        * value in LANG, not with $LC_ALL, (b) internationalized
+        * LC_NUMERIC and LC_TIME are more PITA than benefit
+        * (for one, some utilities have hard time with comma
+        * used as a fractional separator).
+        */
+//TODO: avoid repeated calls by caching last string?
+       setlocale(LC_CTYPE, LANG ? LANG : "");
+
        /* In unicode, this is a one character string */
-// can use unicode_strlen(string) too, but otherwise unicode_strlen() is unused
-       width = mbstowcs(NULL, unicode_0x394, INT_MAX);
+       width = unicode_strlen(unicode_0x394);
        unicode_status = (width == 1 ? UNICODE_ON : UNICODE_OFF);
 }
 
+void FAST_FUNC init_unicode(void)
+{
+       /* Some people set only $LC_CTYPE, not $LC_ALL, because they want
+        * only Unicode to be activated on their system, not the whole
+        * shebang of wrong decimal points, strange date formats and so on.
+        */
+       if (unicode_status == UNICODE_UNKNOWN) {
+               char *s = getenv("LC_ALL");
+               if (!s) s = getenv("LC_CTYPE");
+               if (!s) s = getenv("LANG");
+               reinit_unicode(s);
+       }
+}
+
 #else
 
 /* Homegrown Unicode support. It knows only C and Unicode locales. */
 
 # if ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
-void FAST_FUNC init_unicode(void)
+void FAST_FUNC reinit_unicode(const char *LANG)
 {
-       char *lang;
-
-       if (unicode_status != UNICODE_UNKNOWN)
-               return;
-
        unicode_status = UNICODE_OFF;
-       lang = getenv("LANG");
-       if (!lang || !(strstr(lang, ".utf") || strstr(lang, ".UTF")))
+       if (!LANG || !(strstr(LANG, ".utf") || strstr(LANG, ".UTF")))
                return;
        unicode_status = UNICODE_ON;
 }
+
+void FAST_FUNC init_unicode(void)
+{
+       if (unicode_status == UNICODE_UNKNOWN) {
+               char *s = getenv("LC_ALL");
+               if (!s) s = getenv("LC_CTYPE");
+               if (!s) s = getenv("LANG");
+               reinit_unicode(s);
+       }
+}
 # endif
 
 static size_t wcrtomb_internal(char *s, wchar_t wc)
@@ -131,7 +160,7 @@ size_t FAST_FUNC wcstombs(char *dest, const wchar_t *src, size_t n)
                size_t len = wcrtomb_internal(tbuf, wc);
 
                if (len > n)
-                       len = n;
+                       break;
                memcpy(dest, tbuf, len);
                if (wc == L'\0')
                        return org_n - n;
@@ -956,7 +985,6 @@ int FAST_FUNC unicode_bidi_is_neutral_wchar(wint_t wc)
 
 /* The rest is mostly same for libc and for "homegrown" support */
 
-#if 0 // UNUSED
 size_t FAST_FUNC unicode_strlen(const char *string)
 {
        size_t width = mbstowcs(NULL, string, INT_MAX);
@@ -964,7 +992,6 @@ size_t FAST_FUNC unicode_strlen(const char *string)
                return strlen(string);
        return width;
 }
-#endif
 
 size_t FAST_FUNC unicode_strwidth(const char *string)
 {
@@ -1005,8 +1032,11 @@ static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char
                                d++;
                        }
                }
-               if (stats)
-                       stats->byte_count = stats->unicode_count = (d - dst);
+               if (stats) {
+                       stats->byte_count = (d - dst);
+                       stats->unicode_count = (d - dst);
+                       stats->unicode_width = (d - dst);
+               }
                return dst;
        }
 
@@ -1104,16 +1134,17 @@ char* FAST_FUNC unicode_conv_to_printable(uni_stat_t *stats, const char *src)
 {
        return unicode_conv_to_printable2(stats, src, INT_MAX, 0);
 }
-char* FAST_FUNC unicode_conv_to_printable_maxwidth(uni_stat_t *stats, const char *src, unsigned maxwidth)
+char* FAST_FUNC unicode_conv_to_printable_fixedwidth(/*uni_stat_t *stats,*/ const char *src, unsigned width)
 {
-       return unicode_conv_to_printable2(stats, src, maxwidth, 0);
+       return unicode_conv_to_printable2(/*stats:*/ NULL, src, width, UNI_FLAG_PAD);
 }
-char* FAST_FUNC unicode_conv_to_printable_fixedwidth(uni_stat_t *stats, const char *src, unsigned width)
+
+#ifdef UNUSED
+char* FAST_FUNC unicode_conv_to_printable_maxwidth(uni_stat_t *stats, const char *src, unsigned maxwidth)
 {
-       return unicode_conv_to_printable2(stats, src, width, UNI_FLAG_PAD);
+       return unicode_conv_to_printable2(stats, src, maxwidth, 0);
 }
 
-#ifdef UNUSED
 unsigned FAST_FUNC unicode_padding_to_width(unsigned width, const char *src)
 {
        if (unicode_status != UNICODE_ON) {
index f5ce1f9..a30af6f 100644 (file)
@@ -11,7 +11,7 @@
  * Modified to be able to add or delete users, groups and users to/from groups
  * by Tito Ragusa <farmatito@tiscali.it>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
@@ -22,7 +22,7 @@ static void check_selinux_update_passwd(const char *username)
        char *seuser;
 
        if (getuid() != (uid_t)0 || is_selinux_enabled() == 0)
-               return;         /* No need to check */
+               return;  /* No need to check */
 
        if (getprevcon_raw(&context) < 0)
                bb_perror_msg_and_die("getprevcon failed");
@@ -58,7 +58,7 @@ static void check_selinux_update_passwd(const char *username)
  6) delete a user from a group: update_passwd(FILE, GROUP, NULL, MEMBER)
     only if CONFIG_FEATURE_DEL_USER_FROM_GROUP=y and member != NULL
 
- 7) change user's passord: update_passwd(FILE, USER, NEW_PASSWD, NULL)
+ 7) change user's password: update_passwd(FILE, USER, NEW_PASSWD, NULL)
     only if CONFIG_PASSWD=y and applet_name[0] == 'p' like in passwd
     or if CONFIG_CHPASSWD=y and applet_name[0] == 'c' like in chpasswd
 
@@ -133,7 +133,7 @@ int FAST_FUNC update_passwd(const char *filename,
        goto close_old_fp;
 
  created:
-       if (!fstat(old_fd, &sb)) {
+       if (fstat(old_fd, &sb) == 0) {
                fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */
                fchown(new_fd, sb.st_uid, sb.st_gid);
        }
index 68c358e..09443fb 100644 (file)
@@ -4,10 +4,9 @@
  *
  * Copyright (C) 2010 Denys Vlasenko
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
-#include <utmp.h>
 
 static void touch(const char *filename)
 {
index 67d98d5..f7b2484 100644 (file)
@@ -1,14 +1,16 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Copyright 2006 Rob Landley <rob@landley.net>
+ * Copyright 2003, Glenn McGrath
+ * Copyright 2006, Rob Landley <rob@landley.net>
+ * Copyright 2010, Denys Vlasenko
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 
 /* Conversion table.  for base 64 */
-const char bb_uuenc_tbl_base64[65 + 2] ALIGN1 = {
+const char bb_uuenc_tbl_base64[65 + 1] ALIGN1 = {
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
        'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
        'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
@@ -18,7 +20,7 @@ const char bb_uuenc_tbl_base64[65 + 2] ALIGN1 = {
        'w', 'x', 'y', 'z', '0', '1', '2', '3',
        '4', '5', '6', '7', '8', '9', '+', '/',
        '=' /* termination character */,
-       '\n', '\0' /* needed for uudecode.c */
+       '\0' /* needed for uudecode.c only */
 };
 
 const char bb_uuenc_tbl_std[65] ALIGN1 = {
@@ -69,3 +71,154 @@ void FAST_FUNC bb_uuencode(char *p, const void *src, int length, const char *tbl
                length++;
        }
 }
+
+/*
+ * Decode base64 encoded string. Stops on '\0'.
+ *
+ * Returns: pointer to the undecoded part of source.
+ * If points to '\0', then the source was fully decoded.
+ * (*pp_dst): advanced past the last written byte.
+ */
+const char* FAST_FUNC decode_base64(char **pp_dst, const char *src)
+{
+       char *dst = *pp_dst;
+       const char *src_tail;
+
+       while (1) {
+               unsigned char six_bit[4];
+               int count = 0;
+
+               /* Fetch up to four 6-bit values */
+               src_tail = src;
+               while (count < 4) {
+                       char *table_ptr;
+                       int ch;
+
+                       /* Get next _valid_ character.
+                        * bb_uuenc_tbl_base64[] contains this string:
+                        *  0         1         2         3         4         5         6
+                        *  01234567890123456789012345678901234567890123456789012345678901234
+                        * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
+                        */
+                       do {
+                               ch = *src;
+                               if (ch == '\0') {
+                                       if (count == 0) {
+                                               /* Example:
+                                                * If we decode "QUJD <NUL>", we want
+                                                * to return ptr to NUL, not to ' ',
+                                                * because we did fully decode
+                                                * the string (to "ABC").
+                                                */
+                                               src_tail = src;
+                                       }
+                                       goto ret;
+                               }
+                               src++;
+                               table_ptr = strchr(bb_uuenc_tbl_base64, ch);
+//TODO: add BASE64_FLAG_foo to die on bad char?
+                       } while (!table_ptr);
+
+                       /* Convert encoded character to decimal */
+                       ch = table_ptr - bb_uuenc_tbl_base64;
+
+                       /* ch is 64 if char was '=', otherwise 0..63 */
+                       if (ch == 64)
+                               break;
+                       six_bit[count] = ch;
+                       count++;
+               }
+
+               /* Transform 6-bit values to 8-bit ones.
+                * count can be < 4 when we decode the tail:
+                * "eQ==" -> "y", not "y NUL NUL".
+                * Note that (count > 1) is always true,
+                * "x===" encoding is not valid:
+                * even a single zero byte encodes as "AA==".
+                * However, with current logic we come here with count == 1
+                * when we decode "==" tail.
+                */
+               if (count > 1)
+                       *dst++ = six_bit[0] << 2 | six_bit[1] >> 4;
+               if (count > 2)
+                       *dst++ = six_bit[1] << 4 | six_bit[2] >> 2;
+               if (count > 3)
+                       *dst++ = six_bit[2] << 6 | six_bit[3];
+               /* Note that if we decode "AA==" and ate first '=',
+                * we just decoded one char (count == 2) and now we'll
+                * do the loop once more to decode second '='.
+                */
+       } /* while (1) */
+ ret:
+       *pp_dst = dst;
+       return src_tail;
+}
+
+/*
+ * Decode base64 encoded stream.
+ * Can stop on EOF, specified char, or on uuencode-style "====" line:
+ * flags argument controls it.
+ */
+void FAST_FUNC read_base64(FILE *src_stream, FILE *dst_stream, int flags)
+{
+/* Note that EOF _can_ be passed as exit_char too */
+#define exit_char    ((int)(signed char)flags)
+#define uu_style_end (flags & BASE64_FLAG_UU_STOP)
+
+       /* uuencoded files have 61 byte lines. Use 64 byte buffer
+        * to process line at a time.
+        */
+       enum { BUFFER_SIZE = 64 };
+
+       char in_buf[BUFFER_SIZE + 2];
+       char out_buf[BUFFER_SIZE / 4 * 3 + 2];
+       char *out_tail;
+       const char *in_tail;
+       int term_seen = 0;
+       int in_count = 0;
+
+       while (1) {
+               while (in_count < BUFFER_SIZE) {
+                       int ch = fgetc(src_stream);
+                       if (ch == exit_char) {
+                               if (in_count == 0)
+                                       return;
+                               term_seen = 1;
+                               break;
+                       }
+                       if (ch == EOF) {
+                               term_seen = 1;
+                               break;
+                       }
+                       /* Prevent "====" line to be split: stop if we see '\n'.
+                        * We can also skip other whitespace and skirt the problem
+                        * of files with NULs by stopping on any control char or space:
+                        */
+                       if (ch <= ' ')
+                               break;
+                       in_buf[in_count++] = ch;
+               }
+               in_buf[in_count] = '\0';
+
+               /* Did we encounter "====" line? */
+               if (uu_style_end && strcmp(in_buf, "====") == 0)
+                       return;
+
+               out_tail = out_buf;
+               in_tail = decode_base64(&out_tail, in_buf);
+
+               fwrite(out_buf, (out_tail - out_buf), 1, dst_stream);
+
+               if (term_seen) {
+                       /* Did we consume ALL characters? */
+                       if (*in_tail == '\0')
+                               return;
+                       /* No */
+                       bb_error_msg_and_die("truncated base64 input");
+               }
+
+               /* It was partial decode */
+               in_count = strlen(in_tail);
+               memmove(in_buf, in_tail, in_count);
+       }
+}
index 09fffbc..0542687 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 #if defined(__GLIBC__) && __GLIBC__ < 2
 int FAST_FUNC vdprintf(int d, const char *format, va_list ap)
 {
-       char buf[BUF_SIZE];
+       char buf[8 * 1024];
        int len;
 
-       len = vsnprintf(buf, BUF_SIZE, format, ap);
+       len = vsnprintf(buf, sizeof(buf), format, ap);
        return write(d, buf, len);
 }
 #endif
index c5fbc38..ee95be3 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 #if ENABLE_FEATURE_SYSLOG
index 5c2c529..ed1f86f 100644 (file)
@@ -12,7 +12,7 @@
  *
  * Modified for uClibc by Erik Andersen <andersee@debian.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "busybox.h" /* uses applet tables */
@@ -52,6 +52,7 @@ pid_t FAST_FUNC spawn(char **argv)
         * Interested party can wait on pid and learn exit code.
         * If 111 - then it (most probably) failed to exec */
        if (failed) {
+               safe_waitpid(pid, NULL, 0); /* prevent zombie */
                errno = failed;
                return -1;
        }
@@ -68,17 +69,22 @@ pid_t FAST_FUNC xspawn(char **argv)
 }
 
 #if ENABLE_FEATURE_PREFER_APPLETS
-void FAST_FUNC save_nofork_data(struct nofork_save_area *save)
+struct nofork_save_area {
+       jmp_buf die_jmp;
+       const char *applet_name;
+       uint32_t option_mask32;
+       int die_sleep;
+       uint8_t xfunc_error_retval;
+};
+static void save_nofork_data(struct nofork_save_area *save)
 {
        memcpy(&save->die_jmp, &die_jmp, sizeof(die_jmp));
        save->applet_name = applet_name;
        save->xfunc_error_retval = xfunc_error_retval;
        save->option_mask32 = option_mask32;
        save->die_sleep = die_sleep;
-       save->saved = 1;
 }
-
-void FAST_FUNC restore_nofork_data(struct nofork_save_area *save)
+static void restore_nofork_data(struct nofork_save_area *save)
 {
        memcpy(&die_jmp, &save->die_jmp, sizeof(die_jmp));
        applet_name = save->applet_name;
@@ -87,19 +93,17 @@ void FAST_FUNC restore_nofork_data(struct nofork_save_area *save)
        die_sleep = save->die_sleep;
 }
 
-int FAST_FUNC run_nofork_applet_prime(struct nofork_save_area *old, int applet_no, char **argv)
+int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
 {
        int rc, argc;
+       struct nofork_save_area old;
+
+       save_nofork_data(&old);
 
        applet_name = APPLET_NAME(applet_no);
 
        xfunc_error_retval = EXIT_FAILURE;
 
-       /* Special flag for xfunc_die(). If xfunc will "die"
-        * in NOFORK applet, xfunc_die() sees negative
-        * die_sleep and longjmp here instead. */
-       die_sleep = -1;
-
        /* In case getopt() or getopt32() was already called:
         * reset the libc getopt() function, which keeps internal state.
         *
@@ -129,6 +133,11 @@ int FAST_FUNC run_nofork_applet_prime(struct nofork_save_area *old, int applet_n
        while (argv[argc])
                argc++;
 
+       /* Special flag for xfunc_die(). If xfunc will "die"
+        * in NOFORK applet, xfunc_die() sees negative
+        * die_sleep and longjmp here instead. */
+       die_sleep = -1;
+
        rc = setjmp(die_jmp);
        if (!rc) {
                /* Some callers (xargs)
@@ -137,15 +146,6 @@ int FAST_FUNC run_nofork_applet_prime(struct nofork_save_area *old, int applet_n
                memcpy(tmp_argv, argv, (argc+1) * sizeof(tmp_argv[0]));
                /* Finally we can call NOFORK applet's main() */
                rc = applet_main[applet_no](argc, tmp_argv);
-
-       /* The whole reason behind nofork_save_area is that <applet>_main
-        * may exit non-locally! For example, in hush Ctrl-Z tries
-        * (modulo bugs) to dynamically create a child (backgrounded task)
-        * if it detects that Ctrl-Z was pressed when a NOFORK was running.
-        * Testcase: interactive "rm -i".
-        * Don't fool yourself into thinking "and <applet>_main() returns
-        * quickly here" and removing "useless" nofork_save_area code. */
-
        } else { /* xfunc died in NOFORK applet */
                /* in case they meant to return 0... */
                if (rc == -2222)
@@ -153,7 +153,7 @@ int FAST_FUNC run_nofork_applet_prime(struct nofork_save_area *old, int applet_n
        }
 
        /* Restoring some globals */
-       restore_nofork_data(old);
+       restore_nofork_data(&old);
 
        /* Other globals can be simply reset to defaults */
 #ifdef __GLIBC__
@@ -164,15 +164,6 @@ int FAST_FUNC run_nofork_applet_prime(struct nofork_save_area *old, int applet_n
 
        return rc & 0xff; /* don't confuse people with "exitcodes" >255 */
 }
-
-int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
-{
-       struct nofork_save_area old;
-
-       /* Saving globals */
-       save_nofork_data(&old);
-       return run_nofork_applet_prime(&old, applet_no, argv);
-}
 #endif /* FEATURE_PREFER_APPLETS */
 
 int FAST_FUNC spawn_and_wait(char **argv)
@@ -182,17 +173,17 @@ int FAST_FUNC spawn_and_wait(char **argv)
        int a = find_applet_by_name(argv[0]);
 
        if (a >= 0 && (APPLET_IS_NOFORK(a)
-#if BB_MMU
+# if BB_MMU
                        || APPLET_IS_NOEXEC(a) /* NOEXEC trick needs fork() */
-#endif
+# endif
        )) {
-#if BB_MMU
+# if BB_MMU
                if (APPLET_IS_NOFORK(a))
-#endif
+# endif
                {
                        return run_nofork_applet(a, argv);
                }
-#if BB_MMU
+# if BB_MMU
                /* MMU only */
                /* a->noexec is true */
                rc = fork();
@@ -201,7 +192,7 @@ int FAST_FUNC spawn_and_wait(char **argv)
                /* child */
                xfunc_error_retval = EXIT_FAILURE;
                run_applet_no_and_exit(a, argv);
-#endif
+# endif
        }
 #endif /* FEATURE_PREFER_APPLETS */
        rc = spawn(argv);
@@ -262,11 +253,19 @@ void FAST_FUNC bb_daemonize_or_rexec(int flags, char **argv)
        if (!(flags & DAEMON_ONLY_SANITIZE)) {
                if (fork_or_rexec(argv))
                        exit(EXIT_SUCCESS); /* parent */
-               /* if daemonizing, make sure we detach from stdio & ctty */
+               /* if daemonizing, detach from stdio & ctty */
                setsid();
                dup2(fd, 0);
                dup2(fd, 1);
                dup2(fd, 2);
+               if (flags & DAEMON_DOUBLE_FORK) {
+                       /* On Linux, session leader can acquire ctty
+                        * unknowingly, by opening a tty.
+                        * Prevent this: stop being a session leader.
+                        */
+                       if (fork_or_rexec(argv))
+                               exit(EXIT_SUCCESS); /* parent */
+               }
        }
        while (fd > 2) {
                close(fd--);
index fa33c25..3f3025c 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
index deec79a..76dc8b8 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index 46ff7a6..d8b1c4a 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* A number of applets need to open a file for reading, where the filename
@@ -31,7 +31,7 @@ FILE* FAST_FUNC xfopen_stdin(const char *filename)
        FILE *fp = fopen_or_warn_stdin(filename);
        if (fp)
                return fp;
-       xfunc_die();    /* We already output an error message. */
+       xfunc_die();  /* We already output an error message. */
 }
 
 int FAST_FUNC open_or_warn_stdin(const char *filename)
@@ -46,3 +46,11 @@ int FAST_FUNC open_or_warn_stdin(const char *filename)
 
        return fd;
 }
+
+int FAST_FUNC xopen_stdin(const char *filename)
+{
+       int fd = open_or_warn_stdin(filename);
+       if (fd >= 0)
+               return fd;
+       xfunc_die();  /* We already output an error message. */
+}
index 116e4d1..2d67a72 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2008 Bernhard Reutner-Fischer
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index 3cdf634..6f4e023 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -59,7 +59,7 @@ unsigned bb_strtoui(const char *str, char **end, int b)
 
 /* A few special cases */
 
-int FAST_FUNC xatoi_u(const char *numstr)
+int FAST_FUNC xatoi_positive(const char *numstr)
 {
        return xatou_range(numstr, 0, INT_MAX);
 }
@@ -68,3 +68,10 @@ uint16_t FAST_FUNC xatou16(const char *numstr)
 {
        return xatou_range(numstr, 0, 0xffff);
 }
+
+const struct suffix_mult bkm_suffixes[] = {
+       { "b", 512 },
+       { "k", 1024 },
+       { "m", 1024*1024 },
+       { "", 0 }
+};
index c97a4b7..e047198 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 /*
 You need to define the following (example):
@@ -41,7 +41,7 @@ unsigned type FAST_FUNC xstrtou(_range_sfx)(const char *numstr, int base,
        if (errno || numstr == e)
                goto inval; /* error / no digits / illegal trailing chars */
 
-       errno = old_errno;      /* Ok.  So restore errno. */
+       errno = old_errno;  /* Ok.  So restore errno. */
 
        /* Do optional suffix parsing.  Allow 'empty' suffix tables.
         * Note that we also allow nul suffixes with associated multipliers,
@@ -59,7 +59,7 @@ unsigned type FAST_FUNC xstrtou(_range_sfx)(const char *numstr, int base,
        }
 
        /* Note: trailing space is an error.
-          It would be easy enough to allow though if desired. */
+        * It would be easy enough to allow though if desired. */
        if (*e)
                goto inval;
  chk_range:
index 2de6de7..1c8bb2b 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Connect to host at port using address resolution from getaddrinfo
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include <sys/types.h>
@@ -134,16 +134,18 @@ int FAST_FUNC get_nport(const struct sockaddr *sa)
        return -1;
 }
 
-void FAST_FUNC set_nport(len_and_sockaddr *lsa, unsigned port)
+void FAST_FUNC set_nport(struct sockaddr *sa, unsigned port)
 {
 #if ENABLE_FEATURE_IPV6
-       if (lsa->u.sa.sa_family == AF_INET6) {
-               lsa->u.sin6.sin6_port = port;
+       if (sa->sa_family == AF_INET6) {
+               struct sockaddr_in6 *sin6 = (void*) sa;
+               sin6->sin6_port = port;
                return;
        }
 #endif
-       if (lsa->u.sa.sa_family == AF_INET) {
-               lsa->u.sin.sin_port = port;
+       if (sa->sa_family == AF_INET) {
+               struct sockaddr_in *sin = (void*) sa;
+               sin->sin_port = port;
                return;
        }
        /* What? UNIX socket? IPX?? :) */
@@ -255,7 +257,7 @@ IF_NOT_FEATURE_IPV6(sa_family_t af = AF_INET;)
 
        memset(&hint, 0 , sizeof(hint));
        hint.ai_family = af;
-       /* Needed. Or else we will get each address thrice (or more)
+       /* Need SOCK_STREAM, or else we get each address thrice (or more)
         * for each possible socket type (tcp,udp,raw...): */
        hint.ai_socktype = SOCK_STREAM;
        hint.ai_flags = ai_flags & ~DIE_ON_ERROR;
@@ -283,9 +285,10 @@ IF_NOT_FEATURE_IPV6(sa_family_t af = AF_INET;)
        memcpy(&r->u.sa, used_res->ai_addr, used_res->ai_addrlen);
 
  set_port:
-       set_nport(r, htons(port));
+       set_nport(&r->u.sa, htons(port));
  ret:
-       freeaddrinfo(result);
+       if (result)
+               freeaddrinfo(result);
        return r;
 }
 #if !ENABLE_FEATURE_IPV6
@@ -319,26 +322,28 @@ len_and_sockaddr* FAST_FUNC xdotted2sockaddr(const char *host, int port)
        return str2sockaddr(host, port, AF_UNSPEC, AI_NUMERICHOST | DIE_ON_ERROR);
 }
 
-#undef xsocket_type
-int FAST_FUNC xsocket_type(len_and_sockaddr **lsap, IF_FEATURE_IPV6(int family,) int sock_type)
+int FAST_FUNC xsocket_type(len_and_sockaddr **lsap, int family, int sock_type)
 {
-       IF_NOT_FEATURE_IPV6(enum { family = AF_INET };)
        len_and_sockaddr *lsa;
        int fd;
        int len;
 
-#if ENABLE_FEATURE_IPV6
        if (family == AF_UNSPEC) {
+#if ENABLE_FEATURE_IPV6
                fd = socket(AF_INET6, sock_type, 0);
                if (fd >= 0) {
                        family = AF_INET6;
                        goto done;
                }
+#endif
                family = AF_INET;
        }
-#endif
+
        fd = xsocket(family, sock_type, 0);
+
        len = sizeof(struct sockaddr_in);
+       if (family == AF_UNIX)
+               len = sizeof(struct sockaddr_un);
 #if ENABLE_FEATURE_IPV6
        if (family == AF_INET6) {
  done:
@@ -354,7 +359,7 @@ int FAST_FUNC xsocket_type(len_and_sockaddr **lsap, IF_FEATURE_IPV6(int family,)
 
 int FAST_FUNC xsocket_stream(len_and_sockaddr **lsap)
 {
-       return xsocket_type(lsap, IF_FEATURE_IPV6(AF_UNSPEC,) SOCK_STREAM);
+       return xsocket_type(lsap, AF_UNSPEC, SOCK_STREAM);
 }
 
 static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type)
@@ -367,8 +372,8 @@ static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type)
                /* user specified bind addr dictates family */
                fd = xsocket(lsa->u.sa.sa_family, sock_type, 0);
        } else {
-               fd = xsocket_type(&lsa, IF_FEATURE_IPV6(AF_UNSPEC,) sock_type);
-               set_nport(lsa, htons(port));
+               fd = xsocket_type(&lsa, AF_UNSPEC, sock_type);
+               set_nport(&lsa->u.sa, htons(port));
        }
        setsockopt_reuseaddr(fd);
        xbind(fd, &lsa->u.sa, lsa->len);
index ba9fe93..204e5e4 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2008 by Denys Vlasenko <vda.linux@googlemail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /* Keeping it separate allows to NOT suck in stdio for VERY small applets.
index 275dd4b..23f2751 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (C) 2006 Rob Landley
  * Copyright (C) 2006 Denys Vlasenko
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /* We need to have separate xfuncs.c and xfuncs_printf.c because
 #include "libbb.h"
 
 /* Turn on nonblocking I/O on a fd */
-int FAST_FUNC ndelay_on(int fd)
+void FAST_FUNC ndelay_on(int fd)
 {
-       return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+       int flags = fcntl(fd, F_GETFL);
+       if (flags & O_NONBLOCK)
+               return;
+       fcntl(fd, F_SETFL, flags | O_NONBLOCK);
 }
 
-int FAST_FUNC ndelay_off(int fd)
+void FAST_FUNC ndelay_off(int fd)
 {
-       return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);
+       int flags = fcntl(fd, F_GETFL);
+       if (!(flags & O_NONBLOCK))
+               return;
+       fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
 }
 
-int FAST_FUNC close_on_exec_on(int fd)
+void FAST_FUNC close_on_exec_on(int fd)
 {
-       return fcntl(fd, F_SETFD, FD_CLOEXEC);
+       fcntl(fd, F_SETFD, FD_CLOEXEC);
 }
 
 char* FAST_FUNC strncpy_IFNAMSIZ(char *dst, const char *src)
@@ -234,7 +240,7 @@ static int wh_helper(int value, int def_val, const char *env_name, int *err)
                char *s = getenv(env_name);
                if (s) {
                        value = atoi(s);
-                       /* If LINES/COLUMNS are set, pretent that there is
+                       /* If LINES/COLUMNS are set, pretend that there is
                         * no error getting w/h, this prevents some ugly
                         * cursor tricks by our callers */
                        *err = 0;
index 7069a7c..e4ac6a0 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (C) 2006 Rob Landley
  * Copyright (C) 2006 Denys Vlasenko
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /* We need to have separate xfuncs.c and xfuncs_printf.c because
@@ -134,21 +134,12 @@ int FAST_FUNC xopen3(const char *pathname, int flags, int mode)
        return ret;
 }
 
-// Die if we can't open an existing file and return a fd.
+// Die if we can't open a file and return a fd.
 int FAST_FUNC xopen(const char *pathname, int flags)
 {
        return xopen3(pathname, flags, 0666);
 }
 
-/* Die if we can't open an existing file readonly with O_NONBLOCK
- * and return the fd.
- * Note that for ioctl O_RDONLY is sufficient.
- */
-int FAST_FUNC xopen_nonblocking(const char *pathname)
-{
-       return xopen(pathname, O_RDONLY | O_NONBLOCK);
-}
-
 // Warn if we can't open a file and return a fd.
 int FAST_FUNC open3_or_warn(const char *pathname, int flags, int mode)
 {
@@ -167,6 +158,32 @@ int FAST_FUNC open_or_warn(const char *pathname, int flags)
        return open3_or_warn(pathname, flags, 0666);
 }
 
+/* Die if we can't open an existing file readonly with O_NONBLOCK
+ * and return the fd.
+ * Note that for ioctl O_RDONLY is sufficient.
+ */
+int FAST_FUNC xopen_nonblocking(const char *pathname)
+{
+       return xopen(pathname, O_RDONLY | O_NONBLOCK);
+}
+
+int FAST_FUNC xopen_as_uid_gid(const char *pathname, int flags, uid_t u, gid_t g)
+{
+       int fd;
+       uid_t old_euid = geteuid();
+       gid_t old_egid = getegid();
+
+       xsetegid(g);
+       xseteuid(u);
+
+       fd = xopen(pathname, flags);
+
+       xseteuid(old_euid);
+       xsetegid(old_egid);
+
+       return fd;
+}
+
 void FAST_FUNC xunlink(const char *pathname)
 {
        if (unlink(pathname))
@@ -240,6 +257,14 @@ off_t FAST_FUNC xlseek(int fd, off_t offset, int whence)
        return off;
 }
 
+int FAST_FUNC xmkstemp(char *template)
+{
+       int fd = mkstemp(template);
+       if (fd < 0)
+               bb_perror_msg_and_die("can't create temp file '%s'", template);
+       return fd;
+}
+
 // Die with supplied filename if this FILE* has ferror set.
 void FAST_FUNC die_if_ferror(FILE *fp, const char *fn)
 {
@@ -343,17 +368,28 @@ void FAST_FUNC xsetuid(uid_t uid)
        if (setuid(uid)) bb_perror_msg_and_die("setuid");
 }
 
+void FAST_FUNC xsetegid(gid_t egid)
+{
+       if (setegid(egid)) bb_perror_msg_and_die("setegid");
+}
+
+void FAST_FUNC xseteuid(uid_t euid)
+{
+       if (seteuid(euid)) bb_perror_msg_and_die("seteuid");
+}
+
 // Die if we can't chdir to a new path.
 void FAST_FUNC xchdir(const char *path)
 {
        if (chdir(path))
-               bb_perror_msg_and_die("chdir(%s)", path);
+               bb_perror_msg_and_die("can't change directory to '%s'", path);
 }
 
 void FAST_FUNC xchroot(const char *path)
 {
        if (chroot(path))
-               bb_perror_msg_and_die("can't change root directory to %s", path);
+               bb_perror_msg_and_die("can't change root directory to '%s'", path);
+       xchdir("/");
 }
 
 // Print a warning message if opendir() fails, but don't die.
@@ -387,8 +423,12 @@ int FAST_FUNC xsocket(int domain, int type, int protocol)
                /* Hijack vaguely related config option */
 #if ENABLE_VERBOSE_RESOLUTION_ERRORS
                const char *s = "INET";
+# ifdef AF_PACKET
                if (domain == AF_PACKET) s = "PACKET";
+# endif
+# ifdef AF_NETLINK
                if (domain == AF_NETLINK) s = "NETLINK";
+# endif
 IF_FEATURE_IPV6(if (domain == AF_INET6) s = "INET6";)
                bb_perror_msg_and_die("socket(AF_%s,%d,%d)", s, type, protocol);
 #else
@@ -432,6 +472,16 @@ void FAST_FUNC xstat(const char *name, struct stat *stat_buf)
                bb_perror_msg_and_die("can't stat '%s'", name);
 }
 
+void FAST_FUNC xfstat(int fd, struct stat *stat_buf, const char *errmsg)
+{
+       /* errmsg is usually a file name, but not always:
+        * xfstat may be called in a spot where file name is no longer
+        * available, and caller may give e.g. "can't stat input file" string.
+        */
+       if (fstat(fd, stat_buf))
+               bb_simple_perror_msg_and_die(errmsg);
+}
+
 // selinux_or_die() - die if SELinux is disabled.
 void FAST_FUNC selinux_or_die(void)
 {
@@ -518,13 +568,11 @@ int FAST_FUNC bb_xioctl(int fd, unsigned request, void *argp)
 
 char* FAST_FUNC xmalloc_ttyname(int fd)
 {
-       char *buf = xzalloc(128);
-       int r = ttyname_r(fd, buf, 127);
-       if (r) {
-               free(buf);
-               buf = NULL;
-       }
-       return buf;
+       char buf[128];
+       int r = ttyname_r(fd, buf, sizeof(buf) - 1);
+       if (r)
+               return NULL;
+       return xstrdup(buf);
 }
 
 void FAST_FUNC generate_uuid(uint8_t *buf)
index 10febe3..71720d3 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Special function for busybox written by Vladimir Oleynik <dzo@simtreas.ru>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -24,7 +24,7 @@ xrealloc_getcwd_or_warn(char *cwd)
        char *ret;
        unsigned path_max;
 
-       path_max = 128; /* 128 + 64 should be enough for 99% of cases */
+       path_max = 128; /* 128 + 64 should be enough for 99% of cases */
 
        while (1) {
                path_max += PATH_INCR;
index 7afa9b6..89d0329 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
index faa0e16..bb63da0 100644 (file)
@@ -3,11 +3,17 @@
  * xreadlink.c - safe implementation of readlink.
  * Returns a NULL on failure...
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 
+/* some systems (eg Hurd) does not have MAXSYMLINKS definition,
+ * set it to some reasonable value if it isn't defined */
+#ifndef MAXSYMLINKS
+# define MAXSYMLINKS 20
+#endif
+
 /*
  * NOTE: This function returns a malloced char* that you will have to free
  * yourself.
@@ -102,7 +108,8 @@ char* FAST_FUNC xmalloc_readlink_or_warn(const char *path)
 
 char* FAST_FUNC xmalloc_realpath(const char *path)
 {
-#if defined(__GLIBC__) && !defined(__UCLIBC__)
+#if defined(__GLIBC__) || \
+    (defined(__UCLIBC__) && UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 31))
        /* glibc provides a non-standard extension */
        /* new: POSIX.1-2008 specifies this behavior as well */
        return realpath(path, NULL);
index 98fa967..e8d31b7 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2008 Denys Vlasenko
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index 61efb5b..344028f 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) many different people.
  * If you wrote this, please acknowledge your work.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index f9f1ddb..d15e3a2 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y := uidgid_get.o
 
index 34690a7..2060d78 100644 (file)
@@ -1,41 +1,28 @@
 /* vi: set sw=4 ts=4: */
-/*  Copyright (C) 2003     Manuel Novoa III
+/* Copyright (C) 2003     Manuel Novoa III
  *
- *  Licensed under GPL v2, or later.  See file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
-/*  Nov 6, 2003  Initial version.
+/* Nov 6, 2003  Initial version.
  *
- *  NOTE: This implementation is quite strict about requiring all
+ * NOTE: This implementation is quite strict about requiring all
  *    field seperators.  It also does not allow leading whitespace
  *    except when processing the numeric fields.  glibc is more
  *    lenient.  See the various glibc difference comments below.
  *
- *  TODO:
+ * TODO:
  *    Move to dynamic allocation of (currently statically allocated)
  *      buffers; especially for the group-related functions since
  *      large group member lists will cause error returns.
- *
  */
 
 #include "libbb.h"
 #include <assert.h>
 
-#ifndef _PATH_SHADOW
-#define        _PATH_SHADOW    "/etc/shadow"
-#endif
-#ifndef _PATH_PASSWD
-#define        _PATH_PASSWD    "/etc/passwd"
-#endif
-#ifndef _PATH_GROUP
-#define        _PATH_GROUP     "/etc/group"
-#endif
-
 /**********************************************************************/
 /* Sizes for statically allocated buffers. */
 
-/* If you change these values, also change _SC_GETPW_R_SIZE_MAX and
- * _SC_GETGR_R_SIZE_MAX in libc/unistd/sysconf.c to match */
 #define PWD_BUFFER_SIZE 256
 #define GRP_BUFFER_SIZE 256
 
@@ -60,46 +47,24 @@ static int FAST_FUNC bb__parsespent(void *sp, char *line);
 
 struct statics {
        /* Smaller things first */
-       struct passwd getpwuid_resultbuf;
-       struct group getgrgid_resultbuf;
-       struct passwd getpwnam_resultbuf;
-       struct group getgrnam_resultbuf;
-
-       char getpwuid_buffer[PWD_BUFFER_SIZE];
-       char getgrgid_buffer[GRP_BUFFER_SIZE];
-       char getpwnam_buffer[PWD_BUFFER_SIZE];
-       char getgrnam_buffer[GRP_BUFFER_SIZE];
-#if 0
-       struct passwd fgetpwent_resultbuf;
-       struct group fgetgrent_resultbuf;
-       struct spwd fgetspent_resultbuf;
-       char fgetpwent_buffer[PWD_BUFFER_SIZE];
-       char fgetgrent_buffer[GRP_BUFFER_SIZE];
-       char fgetspent_buffer[PWD_BUFFER_SIZE];
-#endif
+       /* It's ok to use one buffer for getpwuid and getpwnam. Manpage says:
+        * "The return value may point to a static area, and may be overwritten
+        * by subsequent calls to getpwent(), getpwnam(), or getpwuid()."
+        */
+       struct passwd getpw_resultbuf;
+       struct group getgr_resultbuf;
+
+       char getpw_buffer[PWD_BUFFER_SIZE];
+       char getgr_buffer[GRP_BUFFER_SIZE];
 #if 0 //ENABLE_USE_BB_SHADOW
-       struct spwd getspuid_resultbuf;
-       struct spwd getspnam_resultbuf;
-       char getspuid_buffer[PWD_BUFFER_SIZE];
-       char getspnam_buffer[PWD_BUFFER_SIZE];
+       struct spwd getsp_resultbuf;
+       char getsp_buffer[PWD_BUFFER_SIZE];
 #endif
 // Not converted - too small to bother
 //pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
 //FILE *pwf /*= NULL*/;
 //FILE *grf /*= NULL*/;
 //FILE *spf /*= NULL*/;
-#if 0
-       struct passwd getpwent_pwd;
-       struct group getgrent_gr;
-       char getpwent_line_buff[PWD_BUFFER_SIZE];
-       char getgrent_line_buff[GRP_BUFFER_SIZE];
-#endif
-#if 0 //ENABLE_USE_BB_SHADOW
-       struct spwd getspent_spwd;
-       struct spwd sgetspent_spwd;
-       char getspent_line_buff[PWD_BUFFER_SIZE];
-       char sgetspent_line_buff[PWD_BUFFER_SIZE];
-#endif
 };
 
 static struct statics *ptr_to_statics;
@@ -193,22 +158,22 @@ int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf,
 struct passwd *fgetpwent(FILE *stream)
 {
        struct statics *S;
-       struct passwd *resultbuf = RESULTBUF(fgetpwent);
-       char *buffer = BUFFER(fgetpwent);
+       struct passwd *resultbuf = RESULTBUF(getpw);
+       char *buffer = BUFFER(getpw);
        struct passwd *result;
 
-       fgetpwent_r(stream, resultbuf, buffer, sizeof(BUFFER(fgetpwent)), &result);
+       fgetpwent_r(stream, resultbuf, buffer, sizeof(BUFFER(getpw)), &result);
        return result;
 }
 
 struct group *fgetgrent(FILE *stream)
 {
        struct statics *S;
-       struct group *resultbuf = RESULTBUF(fgetgrent);
-       char *buffer = BUFFER(fgetgrent);
+       struct group *resultbuf = RESULTBUF(getgr);
+       char *buffer = BUFFER(getgr);
        struct group *result;
 
-       fgetgrent_r(stream, resultbuf, buffer, sizeof(BUFFER(fgetgrent)), &result);
+       fgetgrent_r(stream, resultbuf, buffer, sizeof(BUFFER(getgr)), &result);
        return result;
 }
 #endif
@@ -218,11 +183,11 @@ struct group *fgetgrent(FILE *stream)
 struct spwd *fgetspent(FILE *stream)
 {
        struct statics *S;
-       struct spwd *resultbuf = RESULTBUF(fgetspent);
-       char *buffer = BUFFER(fgetspent);
+       struct spwd *resultbuf = RESULTBUF(getsp);
+       char *buffer = BUFFER(getsp);
        struct spwd *result;
 
-       fgetspent_r(stream, resultbuf, buffer, sizeof(BUFFER(fgetspent)), &result);
+       fgetspent_r(stream, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
        return result;
 }
 #endif
@@ -310,11 +275,11 @@ int sgetspent_r(const char *string, struct spwd *result_buf,
 struct passwd *getpwuid(uid_t uid)
 {
        struct statics *S;
-       struct passwd *resultbuf = RESULTBUF(getpwuid);
-       char *buffer = BUFFER(getpwuid);
+       struct passwd *resultbuf = RESULTBUF(getpw);
+       char *buffer = BUFFER(getpw);
        struct passwd *result;
 
-       getpwuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getpwuid)), &result);
+       getpwuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getpw)), &result);
        return result;
 }
 
@@ -322,11 +287,11 @@ struct passwd *getpwuid(uid_t uid)
 struct group *getgrgid(gid_t gid)
 {
        struct statics *S;
-       struct group *resultbuf = RESULTBUF(getgrgid);
-       char *buffer = BUFFER(getgrgid);
+       struct group *resultbuf = RESULTBUF(getgr);
+       char *buffer = BUFFER(getgr);
        struct group *result;
 
-       getgrgid_r(gid, resultbuf, buffer, sizeof(BUFFER(getgrgid)), &result);
+       getgrgid_r(gid, resultbuf, buffer, sizeof(BUFFER(getgr)), &result);
        return result;
 }
 
@@ -335,8 +300,8 @@ struct group *getgrgid(gid_t gid)
  * to have been created as a reentrant version of the non-standard
  * functions getspuid.  Why getspuid was added, I do not know. */
 int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf,
-                      char *__restrict buffer, size_t buflen,
-                      struct spwd **__restrict result)
+                       char *__restrict buffer, size_t buflen,
+                       struct spwd **__restrict result)
 {
        int rv;
        struct passwd *pp;
@@ -357,11 +322,11 @@ int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf,
 struct spwd *getspuid(uid_t uid)
 {
        struct statics *S;
-       struct spwd *resultbuf = RESULTBUF(getspuid);
-       char *buffer = BUFFER(getspuid);
+       struct spwd *resultbuf = RESULTBUF(getsp);
+       char *buffer = BUFFER(getsp);
        struct spwd *result;
 
-       getspuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getspuid)), &result);
+       getspuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
        return result;
 }
 #endif
@@ -370,11 +335,11 @@ struct spwd *getspuid(uid_t uid)
 struct passwd *getpwnam(const char *name)
 {
        struct statics *S;
-       struct passwd *resultbuf = RESULTBUF(getpwnam);
-       char *buffer = BUFFER(getpwnam);
+       struct passwd *resultbuf = RESULTBUF(getpw);
+       char *buffer = BUFFER(getpw);
        struct passwd *result;
 
-       getpwnam_r(name, resultbuf, buffer, sizeof(BUFFER(getpwnam)), &result);
+       getpwnam_r(name, resultbuf, buffer, sizeof(BUFFER(getpw)), &result);
        return result;
 }
 
@@ -382,11 +347,11 @@ struct passwd *getpwnam(const char *name)
 struct group *getgrnam(const char *name)
 {
        struct statics *S;
-       struct group *resultbuf = RESULTBUF(getgrnam);
-       char *buffer = BUFFER(getgrnam);
+       struct group *resultbuf = RESULTBUF(getgr);
+       char *buffer = BUFFER(getgr);
        struct group *result;
 
-       getgrnam_r(name, resultbuf, buffer, sizeof(BUFFER(getgrnam)), &result);
+       getgrnam_r(name, resultbuf, buffer, sizeof(BUFFER(getgr)), &result);
        return result;
 }
 
@@ -394,11 +359,11 @@ struct group *getgrnam(const char *name)
 struct spwd *getspnam(const char *name)
 {
        struct statics *S;
-       struct spwd *resultbuf = RESULTBUF(getspnam);
-       char *buffer = BUFFER(getspnam);
+       struct spwd *resultbuf = RESULTBUF(getsp);
+       char *buffer = BUFFER(getsp);
        struct spwd *result;
 
-       getspnam_r(name, resultbuf, buffer, sizeof(BUFFER(getspnam)), &result);
+       getspnam_r(name, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
        return result;
 }
 #endif
@@ -438,8 +403,8 @@ void endpwent(void)
 
 
 int getpwent_r(struct passwd *__restrict resultbuf,
-                          char *__restrict buffer, size_t buflen,
-                          struct passwd **__restrict result)
+                       char *__restrict buffer, size_t buflen,
+                       struct passwd **__restrict result)
 {
        int rv;
 
@@ -452,6 +417,7 @@ int getpwent_r(struct passwd *__restrict resultbuf,
                        rv = errno;
                        goto ERR;
                }
+               close_on_exec_on(fileno(pwf));
        }
 
        rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, pwf);
@@ -485,8 +451,8 @@ void endgrent(void)
 }
 
 int getgrent_r(struct group *__restrict resultbuf,
-                          char *__restrict buffer, size_t buflen,
-                          struct group **__restrict result)
+                       char *__restrict buffer, size_t buflen,
+                       struct group **__restrict result)
 {
        int rv;
 
@@ -499,6 +465,7 @@ int getgrent_r(struct group *__restrict resultbuf,
                        rv = errno;
                        goto ERR;
                }
+               close_on_exec_on(fileno(grf));
        }
 
        rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, grf);
@@ -534,7 +501,7 @@ void endspent(void)
 }
 
 int getspent_r(struct spwd *resultbuf, char *buffer,
-                          size_t buflen, struct spwd **result)
+                       size_t buflen, struct spwd **result)
 {
        int rv;
 
@@ -547,6 +514,7 @@ int getspent_r(struct spwd *resultbuf, char *buffer,
                        rv = errno;
                        goto ERR;
                }
+               close_on_exec_on(fileno(spf));
        }
 
        rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, spf);
index ffdc85e..d6483be 100644 (file)
@@ -1,21 +1,20 @@
 /* vi: set sw=4 ts=4: */
-/*  Copyright (C) 2003     Manuel Novoa III
+/* Copyright (C) 2003     Manuel Novoa III
  *
- *  Licensed under GPL v2, or later.  See file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
-/*  Nov 6, 2003  Initial version.
+/* Nov 6, 2003  Initial version.
  *
- *  NOTE: This implementation is quite strict about requiring all
+ * NOTE: This implementation is quite strict about requiring all
  *    field seperators.  It also does not allow leading whitespace
  *    except when processing the numeric fields.  glibc is more
  *    lenient.  See the various glibc difference comments below.
  *
- *  TODO:
+ * TODO:
  *    Move to dynamic allocation of (currently statically allocated)
  *      buffers; especially for the group-related functions since
  *      large group member lists will cause error returns.
- *
  */
 
 #ifndef GETXXKEY_R_FUNC
@@ -38,12 +37,12 @@ int GETXXKEY_R_FUNC(GETXXKEY_R_KEYTYPE key,
        while (1) {
                rv = bb__pgsreader(GETXXKEY_R_PARSER, resultbuf, buffer, buflen, stream);
                if (!rv) {
-                       if (GETXXKEY_R_TEST(resultbuf)) { /* Found key? */
+                       if (GETXXKEY_R_TEST(resultbuf)) { /* found key? */
                                *result = resultbuf;
                                break;
                        }
                } else {
-                       if (rv == ENOENT) {     /* end-of-file encountered. */
+                       if (rv == ENOENT) {  /* EOF encountered */
                                rv = 0;
                        }
                        break;
index 92290bf..8388be0 100644 (file)
@@ -71,7 +71,8 @@ int FAST_FUNC get_uidgid(struct bb_uidgid_t *u, const char *ug, int numeric_ok)
                        }
                }
                gr = getgrnam(group);
-               if (!gr) return 0;
+               if (!gr)
+                       return 0;
                u->gid = gr->gr_gid;
        }
        return 1;
index 5d497c4..9bf79af 100644 (file)
@@ -93,41 +93,18 @@ config USE_BB_CRYPT_SHA
          With this option off, login will fail password check for any
          user which has password encrypted with these algorithms.
 
-config ADDGROUP
-       bool "addgroup"
+config ADDUSER
+       bool "adduser"
        default y
        help
-         Utility for creating a new group account.
+         Utility for creating a new user account.
 
-config FEATURE_ADDGROUP_LONG_OPTIONS
+config FEATURE_ADDUSER_LONG_OPTIONS
        bool "Enable long options"
        default y
-       depends on ADDGROUP && LONG_OPTS
-       help
-         Support long options for the addgroup applet.
-
-config FEATURE_ADDUSER_TO_GROUP
-       bool "Support for adding users to groups"
-       default y
-       depends on ADDGROUP
-       help
-         If  called  with two non-option arguments,
-         addgroup will add an existing user to an
-         existing group.
-
-config DELGROUP
-       bool "delgroup"
-       default y
-       help
-         Utility for deleting a group account.
-
-config FEATURE_DEL_USER_FROM_GROUP
-       bool "Support for removing users from groups"
-       default y
-       depends on DELGROUP
+       depends on ADDUSER && LONG_OPTS
        help
-         If called with two non-option arguments, deluser
-         or delgroup will remove an user from a specified group.
+         Support long options for the adduser applet.
 
 config FEATURE_CHECK_NAMES
        bool "Enable sanity check on user/group names in adduser and addgroup"
@@ -141,19 +118,6 @@ config FEATURE_CHECK_NAMES
          For compatibility with Samba machine accounts "$" is also supported
          at the end of the user or group name.
 
-config ADDUSER
-       bool "adduser"
-       default y
-       help
-         Utility for creating a new user account.
-
-config FEATURE_ADDUSER_LONG_OPTIONS
-       bool "Enable long options"
-       default y
-       depends on ADDUSER && LONG_OPTS
-       help
-         Support long options for the adduser applet.
-
 config FIRST_SYSTEM_ID
        int "First valid system uid or gid for adduser and addgroup"
        depends on ADDUSER || ADDGROUP
@@ -170,23 +134,70 @@ config LAST_SYSTEM_ID
        help
          Last valid system uid or gid for adduser and addgroup
 
+config ADDGROUP
+       bool "addgroup"
+       default y
+       help
+         Utility for creating a new group account.
+
+config FEATURE_ADDGROUP_LONG_OPTIONS
+       bool "Enable long options"
+       default y
+       depends on ADDGROUP && LONG_OPTS
+       help
+         Support long options for the addgroup applet.
+
+config FEATURE_ADDUSER_TO_GROUP
+       bool "Support for adding users to groups"
+       default y
+       depends on ADDGROUP
+       help
+         If  called  with two non-option arguments,
+         addgroup will add an existing user to an
+         existing group.
+
 config DELUSER
        bool "deluser"
        default y
        help
          Utility for deleting a user account.
 
+config DELGROUP
+       bool "delgroup"
+       default y
+       help
+         Utility for deleting a group account.
+
+config FEATURE_DEL_USER_FROM_GROUP
+       bool "Support for removing users from groups"
+       default y
+       depends on DELGROUP
+       help
+         If called with two non-option arguments, deluser
+         or delgroup will remove an user from a specified group.
+
 config GETTY
        bool "getty"
        default y
        select FEATURE_SYSLOG
        help
-         getty lets you log in on a tty, it is normally invoked by init.
+         getty lets you log in on a tty. It is normally invoked by init.
+
+         Note that you can save a few bytes by disabling it and
+         using login applet directly.
+         If you need to reset tty attributes before calling login,
+         this script approximates getty:
+
+         exec </dev/$1 >/dev/$1 2>&1 || exit 1
+         reset
+         stty sane; stty ispeed 38400; stty ospeed 38400
+         printf "%s login: " "`hostname`"
+         read -r login
+         exec /bin/login "$login"
 
 config LOGIN
        bool "login"
        default y
-       select FEATURE_SUID
        select FEATURE_SYSLOG
        help
          login is used when signing onto a system.
@@ -194,6 +205,17 @@ config LOGIN
          Note that Busybox binary must be setuid root for this applet to
          work properly.
 
+config LOGIN_SESSION_AS_CHILD
+       bool "Run logged in session in a child process"
+       default y if PAM
+       depends on LOGIN
+       help
+         Run the logged in session in a child process.  This allows
+         login to clean up things such as utmp entries or PAM sessions
+         when the login session is complete.  If you use PAM, you
+         almost always would want this to be set to Y, else PAM session
+         will not be cleaned up.
+
 config PAM
        bool "Support for PAM (Pluggable Authentication Modules)"
        default n
@@ -229,7 +251,6 @@ config FEATURE_SECURETTY
 config PASSWD
        bool "passwd"
        default y
-       select FEATURE_SUID
        select FEATURE_SYSLOG
        help
          passwd changes passwords for user and group accounts. A normal user
@@ -262,10 +283,16 @@ config CHPASSWD
          Reads a file of user name and password pairs from standard input
          and uses this information to update a group of existing users.
 
+config FEATURE_DEFAULT_PASSWD_ALGO
+       string "Default password encryption method (passwd -a, cryptpw -m parameter)"
+       default "des"
+       depends on PASSWD || CRYPTPW
+       help
+         Possible choices are "d[es]", "m[d5]", "s[ha256]" or "sha512".
+
 config SU
        bool "su"
        default y
-       select FEATURE_SUID
        select FEATURE_SYSLOG
        help
          su is used to become another user during a login session.
@@ -295,7 +322,6 @@ config SULOGIN
 config VLOCK
        bool "vlock"
        default y
-       select FEATURE_SUID
        help
          Build the "vlock" applet which allows you to lock (virtual) terminals.
 
index fd1ea06..ef416a7 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
diff --git a/loginutils/README b/loginutils/README
new file mode 100644 (file)
index 0000000..ce88510
--- /dev/null
@@ -0,0 +1,70 @@
+       Getty
+
+??? Should getty open tty with or without O_NONBLOCK?
+For serial lines, it means "should getty wait for Carrier Detect pin?"
+I checked other getties:
+
+- agetty always uses O_NONBLOCK
+- mgetty uses O_NONBLOCK unless run with -b, or as "getty"
+
+??? If we decided to use O_NONBLOCK (perhaps optionally with -b),
+when getty should send -I INITSTR data to tty? After open succeeds?
+What if we also want to initialize *modem* with some AT commands?
+
+??? Should we check/create /var/lock/LCK..ttyPFX lockfiles?
+
+??? mgetty opens tty but does NOT lock it, then waits for input via
+select/poll, and when input is available, it checks lock file.
+If it exists, mgetty exits (it assumes someone else uses the line).
+If no, it creates the file (lock the tty). Sounds like a good algorithm
+to use if we are called with -w...
+
+Getty should establish a new session and process group, and ensure
+that tty is a ctty.
+
+??? Should getty ensure that other processes which might have opened
+fds to this tty be dusconnected? agetty has a -R option which makes
+agetty call vhangup() after tty is opened. (Then agetty opens it again,
+since it probably vhangup'ed its own fd too).
+
+Getty should leave the tty in approximately the same state as "stty sane"
+before it execs login program. Minor things we do conditionally are:
+       c_iflag |= ICRNL; // if '\r' was used to end username
+
+??? mgetty uses per-tty file to ignore connects, /etc/nologin.ttyxx -
+is it useful?
+
+It should be possible to run "getty 0 -" from a shell prompt.
+[This currently doesn't work from interactive shell since setsid()
+fails in process group leader. The workaround is to run it as a child
+of something. sh -c 'getty - 0; true' usually works. Should we fix this?]
+It should leave tty in a sane state when it exits (Ctrl-D, -t SEC timeout):
+echo should be on, speed, control chars properly set, etc.
+(However, it can't restore ctty. The symptom is that "</dev/tty"
+fails in the parent shell after getty exits: /dev/tty can't be opened).
+
+Getty should write LOGIN_PROCESS utmp record before it starts waiting
+for username to be entered.
+
+       Login
+
+Login should not try to set up tty parameters - apart from switching echo
+off while entering password, and switching it back on after.
+
+Login should not leave "echo off" state when it times out reading password
+or otherwise terminates (Ctrl-C, Ctrl-D etc).
+
+??? Should login establish a new session and/or process group, and ensure
+that tty is a ctty? Without this, running login directly (not via getty)
+from e.g. initscript will usually result with a login session without
+ctty and without session/pgrp properly created...
+
+It should be possible to run "login [USER]" from a shell prompt,
+and it should work (not block/die/error out).
+Similarly to getty, it should leave tty in the sane state when it exits.
+
+??? Should login write LOGIN_PROCESS utmp record before it starts waiting
+for username/password to be entered?
+
+Login should write USER_PROCESS utmp record just before it is about
+to exec user's shell.
diff --git a/loginutils/add-remove-shell.c b/loginutils/add-remove-shell.c
new file mode 100644 (file)
index 0000000..e492b6e
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * add-shell and remove-shell implementation for busybox
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ * Written by Alexander Shishkin <virtuoso@slind.org>
+ *
+ * Licensed under GPLv2 or later, see the LICENSE file in this source tree
+ * for details.
+ */
+
+//applet:IF_ADD_SHELL(   APPLET_ODDNAME(add-shell   , add_remove_shell, BB_DIR_USR_SBIN, BB_SUID_DROP, add_shell   ))
+//applet:IF_REMOVE_SHELL(APPLET_ODDNAME(remove-shell, add_remove_shell, BB_DIR_USR_SBIN, BB_SUID_DROP, remove_shell))
+
+//kbuild:lib-$(CONFIG_ADD_SHELL)    += add-remove-shell.o
+//kbuild:lib-$(CONFIG_REMOVE_SHELL) += add-remove-shell.o
+
+//config:config ADD_SHELL
+//config:       bool "add-shell"
+//config:       default y if DESKTOP
+//config:       help
+//config:         Add shells to /etc/shells.
+//config:
+//config:config REMOVE_SHELL
+//config:       bool "remove-shell"
+//config:       default y if DESKTOP
+//config:       help
+//config:         Remove shells from /etc/shells.
+
+//usage:#define add_shell_trivial_usage
+//usage:       "SHELL..."
+//usage:#define add_shell_full_usage "\n\n"
+//usage:       "Add SHELLs to /etc/shells"
+
+//usage:#define remove_shell_trivial_usage
+//usage:       "SHELL..."
+//usage:#define remove_shell_full_usage "\n\n"
+//usage:       "Remove SHELLs from /etc/shells"
+
+#include "libbb.h"
+
+#define SHELLS_FILE "/etc/shells"
+
+#define REMOVE_SHELL (ENABLE_REMOVE_SHELL && (!ENABLE_ADD_SHELL || applet_name[0] == 'r'))
+#define ADD_SHELL    (ENABLE_ADD_SHELL && (!ENABLE_REMOVE_SHELL || applet_name[0] == 'a'))
+
+/* NB: we use the _address_, not the value, of this string
+ * as a "special value of pointer" in the code.
+ */
+static const char dont_add[] ALIGN1 = "\n";
+
+int add_remove_shell_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int add_remove_shell_main(int argc UNUSED_PARAM, char **argv)
+{
+       FILE *orig_fp;
+       char *orig_fn;
+       char *new_fn;
+
+       argv++;
+
+       orig_fn = xmalloc_follow_symlinks(SHELLS_FILE);
+       if (!orig_fn)
+               return EXIT_FAILURE;
+       orig_fp = fopen_for_read(orig_fn);
+
+       new_fn = xasprintf("%s.tmp", orig_fn);
+       /*
+        * O_TRUNC or O_EXCL? At the first glance, O_EXCL looks better,
+        * since it prevents races. But: (1) it requires a retry loop,
+        * (2) if /etc/shells.tmp is *stale*, then retry loop
+        * with O_EXCL will never succeed - it should have a timeout,
+        * after which it should revert to O_TRUNC.
+        * For now, I settle for O_TRUNC instead.
+        */
+       xmove_fd(xopen(new_fn, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO);
+
+       /* TODO:
+       struct stat sb;
+       xfstat(fileno(orig_fp), &sb);
+       xfchown(STDOUT_FILENO, sb.st_uid, sb.st_gid);
+       xfchmod(STDOUT_FILENO, sb.st_mode);
+       */
+
+       if (orig_fp) {
+               /* Copy old file, possibly skipping removed shell names */
+               char *line;
+               while ((line = xmalloc_fgetline(orig_fp)) != NULL) {
+                       char **cpp = argv;
+                       while (*cpp) {
+                               if (strcmp(*cpp, line) == 0) {
+                                       /* Old file has this shell name */
+                                       if (REMOVE_SHELL) {
+                                               /* we are remove-shell */
+                                               /* delete this name by not copying it */
+                                               goto next_line;
+                                       }
+                                       /* we are add-shell */
+                                       /* mark this name as "do not add" */
+                                       *cpp = (char*)dont_add;
+                               }
+                               cpp++;
+                       }
+                       /* copy shell name from old to new file */
+                       printf("%s\n", line);
+ next_line:
+                       free(line);
+               }
+               if (ENABLE_FEATURE_CLEAN_UP)
+                       fclose(orig_fp);
+       }
+
+       if (ADD_SHELL) {
+               char **cpp = argv;
+               while (*cpp) {
+                       if (*cpp != dont_add)
+                               printf("%s\n", *cpp);
+                       cpp++;
+               }
+       }
+
+       /* Ensure we wrote out everything */
+       if (fclose(stdout) != 0) {
+               xunlink(new_fn);
+               bb_perror_msg_and_die("%s: write error", new_fn);
+       }
+
+       /* Small hole: if rename fails, /etc/shells.tmp is not removed */
+       xrename(new_fn, orig_fn);
+
+       if (ENABLE_FEATURE_CLEAN_UP) {
+               free(orig_fn);
+               free(new_fn);
+       }
+
+       return EXIT_SUCCESS;
+}
index 215e4a9..b37270f 100644 (file)
@@ -6,9 +6,17 @@
  * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
  * Copyright (C) 2007 by Tito Ragusa <farmatito@tiscali.it>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  */
+
+//usage:#define addgroup_trivial_usage
+//usage:       "[-g GID] " IF_FEATURE_ADDUSER_TO_GROUP("[USER] ") "GROUP"
+//usage:#define addgroup_full_usage "\n\n"
+//usage:       "Add a group " IF_FEATURE_ADDUSER_TO_GROUP("or add a user to a group") "\n"
+//usage:     "\n       -g GID  Group id"
+//usage:     "\n       -S      Create a system group"
+
 #include "libbb.h"
 
 #if CONFIG_LAST_SYSTEM_ID < CONFIG_FIRST_SYSTEM_ID
@@ -168,7 +176,6 @@ int addgroup_main(int argc UNUSED_PARAM, char **argv)
        {
                die_if_bad_username(argv[0]);
                new_group(argv[0], gid);
-
        }
        /* Reached only on success */
        return EXIT_SUCCESS;
index f5dca92..ef390ad 100644 (file)
@@ -5,8 +5,22 @@
  * Copyright (C) 1999 by Lineo, inc. and John Beppu
  * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define adduser_trivial_usage
+//usage:       "[OPTIONS] USER [GROUP]"
+//usage:#define adduser_full_usage "\n\n"
+//usage:       "Create new user, or add USER to GROUP\n"
+//usage:     "\n       -h DIR          Home directory"
+//usage:     "\n       -g GECOS        GECOS field"
+//usage:     "\n       -s SHELL        Login shell"
+//usage:     "\n       -G GRP          Add user to existing group"
+//usage:     "\n       -S              Create a system user"
+//usage:     "\n       -D              Don't assign a password"
+//usage:     "\n       -H              Don't create home directory"
+//usage:     "\n       -u UID          User id"
+
 #include "libbb.h"
 
 #if CONFIG_LAST_SYSTEM_ID < CONFIG_FIRST_SYSTEM_ID
@@ -52,6 +66,7 @@ static void passwd_study(struct passwd *p)
                }
                if (p->pw_uid == max) {
                        bb_error_msg_and_die("no %cids left", 'u');
+                       /* this format string is reused in adduser and addgroup */
                }
                p->pw_uid++;
        }
@@ -65,24 +80,44 @@ static void passwd_study(struct passwd *p)
        }
 }
 
-static void addgroup_wrapper(struct passwd *p, const char *group_name)
+static int addgroup_wrapper(struct passwd *p, const char *group_name)
 {
-       char *cmd;
-
-       if (group_name) /* Add user to existing group */
-               cmd = xasprintf("addgroup '%s' '%s'", p->pw_name, group_name);
-       else    /* Add user to his own group with the first free gid found in passwd_study */
-               cmd = xasprintf("addgroup -g %u '%s'", (unsigned)p->pw_gid, p->pw_name);
-       /* Warning: to be compatible with external addgroup programs we should use --gid instead */
-       system(cmd);
-       free(cmd);
+       char *argv[6];
+
+       argv[0] = (char*)"addgroup";
+       if (group_name) {
+               /* Add user to existing group */
+               argv[1] = (char*)"--";
+               argv[2] = p->pw_name;
+               argv[3] = (char*)group_name;
+               argv[4] = NULL;
+       } else {
+               /* Add user to his own group with the first free gid
+                * found in passwd_study.
+                */
+#if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS || !ENABLE_ADDGROUP
+               /* We try to use --gid, not -g, because "standard" addgroup
+                * has no short option -g, it has only long --gid.
+                */
+               argv[1] = (char*)"--gid";
+#else
+               /* Breaks if system in fact does NOT use busybox addgroup */
+               argv[1] = (char*)"-g";
+#endif
+               argv[2] = utoa(p->pw_gid);
+               argv[3] = (char*)"--";
+               argv[4] = p->pw_name;
+               argv[5] = NULL;
+       }
+
+       return spawn_and_wait(argv);
 }
 
-static void passwd_wrapper(const char *login) NORETURN;
+static void passwd_wrapper(const char *login_name) NORETURN;
 
-static void passwd_wrapper(const char *login)
+static void passwd_wrapper(const char *login_name)
 {
-       BB_EXECLP("passwd", "passwd", login, NULL);
+       BB_EXECLP("passwd", "passwd", "--", login_name, NULL);
        bb_error_msg_and_die("can't execute passwd, you must set password manually");
 }
 
@@ -123,12 +158,13 @@ int adduser_main(int argc UNUSED_PARAM, char **argv)
        }
 
        pw.pw_gecos = (char *)"Linux User,,,";
-       pw.pw_shell = (char *)DEFAULT_SHELL;
+       /* We assume that newly created users "inherit" root's shell setting */
+       pw.pw_shell = (char *)get_shell_name();
        pw.pw_dir = NULL;
 
-       /* exactly one non-option arg */
+       /* at least one and at most two non-option args */
        /* disable interactive passwd for system accounts */
-       opt_complementary = "=1:SD:u+";
+       opt_complementary = "-1:?2:SD:u+";
        if (sizeof(pw.pw_uid) == sizeof(int)) {
                opts = getopt32(argv, "h:g:s:G:DSHu:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &pw.pw_uid);
        } else {
@@ -139,9 +175,16 @@ int adduser_main(int argc UNUSED_PARAM, char **argv)
                }
        }
        argv += optind;
+       pw.pw_name = argv[0];
+
+       if (!opts && argv[1]) {
+               /* if called with two non-option arguments, adduser
+                * will add an existing user to an existing group.
+                */
+               return addgroup_wrapper(&pw, argv[1]);
+       }
 
        /* fill in the passwd struct */
-       pw.pw_name = argv[0];
        die_if_bad_username(pw.pw_name);
        if (!pw.pw_dir) {
                /* create string for $HOME if not specified already */
@@ -169,7 +212,6 @@ int adduser_main(int argc UNUSED_PARAM, char **argv)
        }
        if (ENABLE_FEATURE_CLEAN_UP)
                free(p);
-
 #if ENABLE_FEATURE_SHADOWPASSWDS
        /* /etc/shadow fields:
         * 1. username
@@ -203,9 +245,11 @@ int adduser_main(int argc UNUSED_PARAM, char **argv)
                if (mkdir_err == 0) {
                        /* New home. Copy /etc/skel to it */
                        const char *args[] = {
-                               "chown", "-R",
+                               "chown",
+                               "-R",
                                xasprintf("%u:%u", (int)pw.pw_uid, (int)pw.pw_gid),
-                               pw.pw_dir, NULL
+                               pw.pw_dir,
+                               NULL
                        };
                        /* Be silent on any errors (like: no /etc/skel) */
                        logmode = LOGMODE_NONE;
index 1817231..54ed737 100644 (file)
@@ -3,10 +3,25 @@
  * chpasswd.c
  *
  * Written for SLIND (from passwd.c) by Alexander Shishkin <virtuoso@slind.org>
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
+//usage:#define chpasswd_trivial_usage
+//usage:       IF_LONG_OPTS("[--md5|--encrypted]") IF_NOT_LONG_OPTS("[-m|-e]")
+//usage:#define chpasswd_full_usage "\n\n"
+//usage:       "Read user:password from stdin and update /etc/passwd\n"
+//usage:       IF_LONG_OPTS(
+//usage:     "\n       -e,--encrypted  Supplied passwords are in encrypted form"
+//usage:     "\n       -m,--md5        Use MD5 encryption instead of DES"
+//usage:       )
+//usage:       IF_NOT_LONG_OPTS(
+//usage:     "\n       -e      Supplied passwords are in encrypted form"
+//usage:     "\n       -m      Use MD5 encryption instead of DES"
+//usage:       )
+
+//TODO: implement -c ALGO
+
 #if ENABLE_LONG_OPTS
 static const char chpasswd_longopts[] ALIGN1 =
        "encrypted\0" No_argument "e"
@@ -14,18 +29,16 @@ static const char chpasswd_longopts[] ALIGN1 =
        ;
 #endif
 
-#define OPT_ENC                1
-#define OPT_MD5                2
+#define OPT_ENC  1
+#define OPT_MD5  2
 
 int chpasswd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int chpasswd_main(int argc UNUSED_PARAM, char **argv)
 {
-       char *name, *pass;
-       char salt[sizeof("$N$XXXXXXXX")];
-       int opt, rc;
-       int rnd = rnd; /* we *want* it to be non-initialized! */
+       char *name;
+       int opt;
 
-       if (getuid())
+       if (getuid() != 0)
                bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
 
        opt_complementary = "m--e:e--m";
@@ -33,6 +46,10 @@ int chpasswd_main(int argc UNUSED_PARAM, char **argv)
        opt = getopt32(argv, "em");
 
        while ((name = xmalloc_fgetline(stdin)) != NULL) {
+               char *free_me;
+               char *pass;
+               int rc;
+
                pass = strchr(name, ':');
                if (!pass)
                        bb_error_msg_and_die("missing new password");
@@ -40,20 +57,28 @@ int chpasswd_main(int argc UNUSED_PARAM, char **argv)
 
                xuname2uid(name); /* dies if there is no such user */
 
+               free_me = NULL;
                if (!(opt & OPT_ENC)) {
-                       rnd = crypt_make_salt(salt, 1, rnd);
+                       char salt[sizeof("$N$XXXXXXXX")];
+
+                       crypt_make_salt(salt, 1);
                        if (opt & OPT_MD5) {
-                               strcpy(salt, "$1$");
-                               rnd = crypt_make_salt(salt + 3, 4, rnd);
+                               salt[0] = '$';
+                               salt[1] = '1';
+                               salt[2] = '$';
+                               crypt_make_salt(salt + 3, 4);
                        }
-                       pass = pw_encrypt(pass, salt, 0);
+                       free_me = pass = pw_encrypt(pass, salt, 0);
                }
 
                /* This is rather complex: if user is not found in /etc/shadow,
                 * we try to find & change his passwd in /etc/passwd */
 #if ENABLE_FEATURE_SHADOWPASSWDS
                rc = update_passwd(bb_path_shadow_file, name, pass, NULL);
-               if (rc == 0) /* no lines updated, no errors detected */
+               if (rc > 0) /* password in /etc/shadow was updated */
+                       pass = (char*)"x";
+               if (rc >= 0)
+                       /* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */
 #endif
                        rc = update_passwd(bb_path_passwd_file, name, pass, NULL);
                /* LOGMODE_BOTH logs to syslog also */
@@ -64,8 +89,7 @@ int chpasswd_main(int argc UNUSED_PARAM, char **argv)
                        bb_info_msg("Password for '%s' changed", name);
                logmode = LOGMODE_STDIO;
                free(name);
-               if (!(opt & OPT_ENC))
-                       free(pass);
+               free(free_me);
        }
        return EXIT_SUCCESS;
 }
index b44993f..29f0fbe 100644 (file)
@@ -2,14 +2,51 @@
 /*
  * cryptpw.c - output a crypt(3)ed password to stdout.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Cooked from passwd.c by Thomas Lundquist <thomasez@zelow.no>
  * mkpasswd compatible options added by Bernhard Reutner-Fischer
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#define cryptpw_trivial_usage
+//usage:       "[OPTIONS] [PASSWORD] [SALT]"
+/* We do support -s, we just don't mention it */
+//usage:#define cryptpw_full_usage "\n\n"
+//usage:       "Crypt PASSWORD using crypt(3)\n"
+//usage:       IF_LONG_OPTS(
+//usage:     "\n       -P,--password-fd=N      Read password from fd N"
+/* //usage:  "\n       -s,--stdin              Use stdin; like -P0" */
+//usage:     "\n       -m,--method=TYPE        Encryption method"
+//usage:     "\n       -S,--salt=SALT"
+//usage:       )
+//usage:       IF_NOT_LONG_OPTS(
+//usage:     "\n       -P N    Read password from fd N"
+/* //usage:  "\n       -s      Use stdin; like -P0" */
+//usage:     "\n       -m TYPE Encryption method TYPE"
+//usage:     "\n       -S SALT"
+//usage:       )
+
+/* mkpasswd is an alias to cryptpw */
+//usage:#define mkpasswd_trivial_usage
+//usage:       "[OPTIONS] [PASSWORD] [SALT]"
+/* We do support -s, we just don't mention it */
+//usage:#define mkpasswd_full_usage "\n\n"
+//usage:       "Crypt PASSWORD using crypt(3)\n"
+//usage:       IF_LONG_OPTS(
+//usage:     "\n       -P,--password-fd=N      Read password from fd N"
+/* //usage:  "\n       -s,--stdin              Use stdin; like -P0" */
+//usage:     "\n       -m,--method=TYPE        Encryption method"
+//usage:     "\n       -S,--salt=SALT"
+//usage:       )
+//usage:       IF_NOT_LONG_OPTS(
+//usage:     "\n       -P N    Read password from fd N"
+/* //usage:  "\n       -s      Use stdin; like -P0" */
+//usage:     "\n       -m TYPE Encryption method TYPE"
+//usage:     "\n       -S SALT"
+//usage:       )
+
 #include "libbb.h"
 
 /* Debian has 'mkpasswd' utility, manpage says:
@@ -53,11 +90,10 @@ to cryptpw. -a option (alias for -m) came from cryptpw.
 int cryptpw_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int cryptpw_main(int argc UNUSED_PARAM, char **argv)
 {
-       /* $N$ + sha_salt_16_bytes + NUL */
-       char salt[3 + 16 + 1];
+       char salt[MAX_PW_SALT_LEN];
        char *salt_ptr;
+       char *password;
        const char *opt_m, *opt_S;
-       int len;
        int fd;
 
 #if ENABLE_LONG_OPTS
@@ -70,7 +106,7 @@ int cryptpw_main(int argc UNUSED_PARAM, char **argv)
        applet_long_options = mkpasswd_longopts;
 #endif
        fd = STDIN_FILENO;
-       opt_m = "d";
+       opt_m = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO;
        opt_S = NULL;
        /* at most two non-option arguments; -P NUM */
        opt_complementary = "?2:P+";
@@ -82,36 +118,25 @@ int cryptpw_main(int argc UNUSED_PARAM, char **argv)
        if (argv[0] && !opt_S)
                opt_S = argv[1];
 
-       len = 2/2;
-       salt_ptr = salt;
-       if (opt_m[0] != 'd') { /* not des */
-               len = 8/2; /* so far assuming md5 */
-               *salt_ptr++ = '$';
-               *salt_ptr++ = '1';
-               *salt_ptr++ = '$';
-#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA
-               if (opt_m[0] == 's') { /* sha */
-                       salt[1] = '5' + (strcmp(opt_m, "sha512") == 0);
-                       len = 16/2;
-               }
-#endif
-       }
+       salt_ptr = crypt_make_pw_salt(salt, opt_m);
        if (opt_S)
-               safe_strncpy(salt_ptr, opt_S, sizeof(salt) - 3);
-       else
-               crypt_make_salt(salt_ptr, len, 0);
+               safe_strncpy(salt_ptr, opt_S, sizeof(salt) - (sizeof("$N$")-1));
 
        xmove_fd(fd, STDIN_FILENO);
 
-       puts(pw_encrypt(
-               argv[0] ? argv[0] : (
-                       /* Only mkpasswd, and only from tty, prompts.
-                        * Otherwise it is a plain read. */
-                       (isatty(STDIN_FILENO) && applet_name[0] == 'm')
+       password = argv[0];
+       if (!password) {
+               /* Only mkpasswd, and only from tty, prompts.
+                * Otherwise it is a plain read. */
+               password = (isatty(STDIN_FILENO) && applet_name[0] == 'm')
                        ? bb_ask_stdin("Password: ")
                        : xmalloc_fgetline(stdin)
-               ),
-               salt, 1));
+               ;
+               /* may still be NULL on EOF/error */
+       }
+
+       if (password)
+               puts(pw_encrypt(password, salt, 1));
 
        return EXIT_SUCCESS;
 }
index 293e324..e39ac55 100644 (file)
  * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
  * Copyright (C) 2007 by Tito Ragusa <farmatito@tiscali.it>
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  *
  */
-#include "libbb.h"
 
-static int del_line_matching(char **args, const char *filename)
-{
-       if (ENABLE_FEATURE_DEL_USER_FROM_GROUP && args[2]) {
-               return update_passwd(filename, args[2], NULL, args[1]);
-       }
-       return update_passwd(filename, args[1], NULL, NULL);
-}
+//usage:#define deluser_trivial_usage
+//usage:       "USER"
+//usage:#define deluser_full_usage "\n\n"
+//usage:       "Delete USER from the system"
+
+//usage:#define delgroup_trivial_usage
+//usage:       IF_FEATURE_DEL_USER_FROM_GROUP("[USER] ")"GROUP"
+//usage:#define delgroup_full_usage "\n\n"
+//usage:       "Delete group GROUP from the system"
+//usage:       IF_FEATURE_DEL_USER_FROM_GROUP(" or user USER from group GROUP")
+
+#include "libbb.h"
 
 int deluser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int deluser_main(int argc, char **argv)
 {
-       if (argc != 2
-        && (!ENABLE_FEATURE_DEL_USER_FROM_GROUP
-           || (applet_name[3] != 'g' || argc != 3))
-       ) {
-               bb_show_usage();
-       }
+       /* User or group name */
+       char *name;
+       /* Username (non-NULL only in "delgroup USER GROUP" case) */
+       char *member;
+       /* Name of passwd or group file */
+       const char *pfile;
+       /* Name of shadow or gshadow file */
+       const char *sfile;
+       /* Are we deluser or delgroup? */
+       int do_deluser = (ENABLE_DELUSER && (!ENABLE_DELGROUP || applet_name[3] == 'u'));
 
-       if (geteuid())
+       if (geteuid() != 0)
                bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
 
-       if ((ENABLE_FEATURE_DEL_USER_FROM_GROUP && argc != 3)
-        || ENABLE_DELUSER
-        || (ENABLE_DELGROUP && ENABLE_DESKTOP)
-       ) {
-               if (ENABLE_DELUSER
-                && (!ENABLE_DELGROUP || applet_name[3] == 'u')
-               ) {
-                       if (del_line_matching(argv, bb_path_passwd_file) < 0)
+       name = argv[1];
+       member = NULL;
+
+       switch (argc) {
+       case 3:
+               if (!ENABLE_FEATURE_DEL_USER_FROM_GROUP || do_deluser)
+                       break;
+               /* It's "delgroup USER GROUP" */
+               member = name;
+               name = argv[2];
+               /* Fallthrough */
+
+       case 2:
+               if (do_deluser) {
+                       /* "deluser USER" */
+                       xgetpwnam(name); /* bail out if USER is wrong */
+                       pfile = bb_path_passwd_file;
+                       if (ENABLE_FEATURE_SHADOWPASSWDS)
+                               sfile = bb_path_shadow_file;
+               } else {
+                       struct group *gr;
+ do_delgroup:
+                       /* "delgroup GROUP" or "delgroup USER GROUP" */
+                       if (do_deluser < 0) { /* delgroup after deluser? */
+                               gr = getgrnam(name);
+                               if (!gr)
+                                       return EXIT_SUCCESS;
+                       } else {
+                               gr = xgetgrnam(name); /* bail out if GROUP is wrong */
+                       }
+                       if (!member) {
+                               /* "delgroup GROUP" */
+                               struct passwd *pw;
+                               struct passwd pwent;
+                               /* Check if the group is in use */
+#define passwd_buf bb_common_bufsiz1
+                               while (!getpwent_r(&pwent, passwd_buf, sizeof(passwd_buf), &pw)) {
+                                       if (pwent.pw_gid == gr->gr_gid)
+                                               bb_error_msg_and_die("'%s' still has '%s' as their primary group!", pwent.pw_name, name);
+                               }
+                               //endpwent();
+                       }
+                       pfile = bb_path_group_file;
+                       if (ENABLE_FEATURE_SHADOWPASSWDS)
+                               sfile = bb_path_gshadow_file;
+               }
+
+               /* Modify pfile, then sfile */
+               do {
+                       if (update_passwd(pfile, name, NULL, member) == -1)
                                return EXIT_FAILURE;
                        if (ENABLE_FEATURE_SHADOWPASSWDS) {
-                               del_line_matching(argv, bb_path_shadow_file);
+                               pfile = sfile;
+                               sfile = NULL;
                        }
-               } else if (ENABLE_DESKTOP && ENABLE_DELGROUP && getpwnam(argv[1]))
-                       bb_error_msg_and_die("can't remove primary group of user %s", argv[1]);
-       }
-       if (del_line_matching(argv, bb_path_group_file) < 0)
-               return EXIT_FAILURE;
-       if (ENABLE_FEATURE_SHADOWPASSWDS) {
-               del_line_matching(argv, bb_path_gshadow_file);
+               } while (ENABLE_FEATURE_SHADOWPASSWDS && pfile);
+
+               if (ENABLE_DELGROUP && do_deluser > 0) {
+                       /* "deluser USER" also should try to delete
+                        * same-named group. IOW: do "delgroup USER"
+                        */
+// On debian deluser is a perl script that calls userdel.
+// From man userdel:
+//  If USERGROUPS_ENAB is defined to yes in /etc/login.defs, userdel will
+//  delete the group with the same name as the user.
+                       do_deluser = -1;
+                       goto do_delgroup;
+               }
+               return EXIT_SUCCESS;
        }
-       return EXIT_SUCCESS;
+       /* Reached only if number of command line args is wrong */
+       bb_show_usage();
 }
index 40ec971..0f060ae 100644 (file)
@@ -1,43 +1,50 @@
 /* vi: set sw=4 ts=4: */
-/* agetty.c - another getty program for Linux. By W. Z. Venema 1989
+/*
+ * Based on agetty - another getty program for Linux. By W. Z. Venema 1989
  * Ported to Linux by Peter Orbaek <poe@daimi.aau.dk>
- * This program is freely distributable. The entire man-page used to
- * be here. Now read the real man-page agetty.8 instead.
+ * This program is freely distributable.
  *
  * option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95
  *
  * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
- * - added Native Language Support
+ * - Added Native Language Support
  *
  * 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>
- * - enable hardware flow control before displaying /etc/issue
+ * - Enabled hardware flow control before displaying /etc/issue
+ *
+ * 2011-01 Venys Vlasenko
+ * - Removed parity detection code. It can't work reliably:
+ * if all chars received have bit 7 cleared and odd (or even) parity,
+ * it is impossible to determine whether other side is 8-bit,no-parity
+ * or 7-bit,odd(even)-parity. It also interferes with non-ASCII usernames.
+ * - From now on, we assume that parity is correctly set.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 #include <syslog.h>
+#ifndef IUCLC
+# define IUCLC 0
+#endif
 
-#if ENABLE_FEATURE_UTMP
-# include <utmp.h> /* LOGIN_PROCESS */
+#ifndef LOGIN_PROCESS
+# undef ENABLE_FEATURE_UTMP
+# undef ENABLE_FEATURE_WTMP
+# define ENABLE_FEATURE_UTMP 0
+# define ENABLE_FEATURE_WTMP 0
 #endif
 
-#ifndef IUCLC
-# define IUCLC 0
+
+/* The following is used for understandable diagnostics */
+#ifdef DEBUGGING
+static FILE *dbf;
+# define DEBUGTERM "/dev/ttyp0"
+# define debug(...) do { fprintf(dbf, __VA_ARGS__); fflush(dbf); } while (0)
+#else
+# define debug(...) ((void)0)
 #endif
 
-/*
- * Some heuristics to find out what environment we are in: if it is not
- * System V, assume it is SunOS 4.
- */
-#ifdef LOGIN_PROCESS                    /* defined in System V utmp.h */
-#include <sys/utsname.h>
-#else /* if !sysV style, wtmp/utmp code is off */
-#undef ENABLE_FEATURE_UTMP
-#undef ENABLE_FEATURE_WTMP
-#define ENABLE_FEATURE_UTMP 0
-#define ENABLE_FEATURE_WTMP 0
-#endif  /* LOGIN_PROCESS */
 
 /*
  * Things you may want to modify.
  * below. Note, however, that DEL cannot be used for interrupt generation
  * and for line editing at the same time.
  */
-
-/* I doubt there are systems which still need this */
-#undef HANDLE_ALLCAPS
-#undef ANCIENT_BS_KILL_CHARS
-
+#undef  _PATH_LOGIN
 #define _PATH_LOGIN "/bin/login"
 
-/* If ISSUE is not defined, getty will never display the contents of the
+/* Displayed before the login prompt.
+ * If ISSUE is not defined, getty will never display the contents of the
  * /etc/issue file. You will not want to spit out large "issue" files at the
  * wrong baud rate.
  */
-#define ISSUE "/etc/issue"              /* displayed before the login prompt */
-
-/* Some shorthands for control characters. */
-#define CTL(x)          ((x) ^ 0100)    /* Assumes ASCII dialect */
-#define CR              CTL('M')        /* carriage return */
-#define NL              CTL('J')        /* line feed */
-#define BS              CTL('H')        /* back space */
-#define DEL             CTL('?')        /* delete */
-
-/* Defaults for line-editing etc. characters; you may want to change this. */
-#define DEF_ERASE       DEL             /* default erase character */
-#define DEF_INTR        CTL('C')        /* default interrupt character */
-#define DEF_QUIT        CTL('\\')       /* default quit char */
-#define DEF_KILL        CTL('U')        /* default kill char */
-#define DEF_EOF         CTL('D')        /* default EOF char */
-#define DEF_EOL         '\n'
-#define DEF_SWITCH      0               /* default switch char */
+#define ISSUE "/etc/issue"
+
+/* Macro to build Ctrl-LETTER. Assumes ASCII dialect */
+#define CTL(x)          ((x) ^ 0100)
 
 /*
- * When multiple baud rates are specified on the command line, the first one
- * we will try is the first one specified.
+ * When multiple baud rates are specified on the command line,
+ * the first one we will try is the first one specified.
  */
 #define MAX_SPEED       10              /* max. nr. of baud rates */
 
-/* Storage for command-line options. */
-struct options {
-       int flags;                      /* toggle switches, see below */
-       unsigned timeout;               /* time-out period */
+struct globals {
+       unsigned timeout;
        const char *login;              /* login program */
-       const char *tty;                /* name of tty */
-       const char *initstring;         /* modem init string */
+       const char *fakehost;
+       const char *tty_name;
+       char *initstring;               /* modem init string */
        const char *issue;              /* alternative issue file */
        int numspeed;                   /* number of baud rates to try */
        int speeds[MAX_SPEED];          /* baud rates to be tried */
+       unsigned char eol;              /* end-of-line char seen (CR or NL) */
+       struct termios tty_attrs;
+       char line_buf[128];
 };
 
-/* Storage for things detected while the login name was read. */
-struct chardata {
-       unsigned char erase;    /* erase character */
-       unsigned char kill;     /* kill character */
-       unsigned char eol;      /* end-of-line character */
-       unsigned char parity;   /* what parity did we see */
-       /* (parity & 1): saw odd parity char with 7th bit set */
-       /* (parity & 2): saw even parity char with 7th bit set */
-       /* parity == 0: probably 7-bit, space parity? */
-       /* parity == 1: probably 7-bit, odd parity? */
-       /* parity == 2: probably 7-bit, even parity? */
-       /* parity == 3: definitely 8 bit, no parity! */
-       /* Hmm... with any value of "parity" 8 bit, no parity is possible */
-#ifdef HANDLE_ALLCAPS
-       unsigned char capslock; /* upper case without lower case */
-#endif
-};
-
-
-/* Initial values for the above. */
-static const struct chardata init_chardata = {
-       DEF_ERASE,                              /* default erase character */
-       DEF_KILL,                               /* default kill character */
-       13,                                     /* default eol char */
-       0,                                      /* space parity */
-#ifdef HANDLE_ALLCAPS
-       0,                                      /* no capslock */
-#endif
-};
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+       SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+//usage:#define getty_trivial_usage
+//usage:       "[OPTIONS] BAUD_RATE[,BAUD_RATE]... TTY [TERMTYPE]"
+//usage:#define getty_full_usage "\n\n"
+//usage:       "Open TTY, prompt for login name, then invoke /bin/login\n"
+//usage:     "\n       -h              Enable hardware RTS/CTS flow control"
+//usage:     "\n       -L              Set CLOCAL (ignore Carrier Detect state)"
+//usage:     "\n       -m              Get baud rate from modem's CONNECT status message"
+//usage:     "\n       -n              Don't prompt for login name"
+//usage:     "\n       -w              Wait for CR or LF before sending /etc/issue"
+//usage:     "\n       -i              Don't display /etc/issue"
+//usage:     "\n       -f ISSUE_FILE   Display ISSUE_FILE instead of /etc/issue"
+//usage:     "\n       -l LOGIN        Invoke LOGIN instead of /bin/login"
+//usage:     "\n       -t SEC          Terminate after SEC if no login name is read"
+//usage:     "\n       -I INITSTR      Send INITSTR before anything else"
+//usage:     "\n       -H HOST         Log HOST into the utmp file as the hostname"
+//usage:     "\n"
+//usage:     "\nBAUD_RATE of 0 leaves it unchanged"
 
 static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn";
-#define F_INITSTRING    (1 << 0)        /* -I initstring is set */
-#define F_LOCAL         (1 << 1)        /* -L force local */
-#define F_FAKEHOST      (1 << 2)        /* -H fake hostname */
-#define F_CUSTISSUE     (1 << 3)        /* -f give alternative issue file */
-#define F_RTSCTS        (1 << 4)        /* -h enable RTS/CTS flow control */
-#define F_ISSUE         (1 << 5)        /* -i display /etc/issue */
-#define F_LOGIN         (1 << 6)        /* -l non-default login program */
-#define F_PARSE         (1 << 7)        /* -m process modem status messages */
-#define F_TIMEOUT       (1 << 8)        /* -t time out */
-#define F_WAITCRLF      (1 << 9)        /* -w wait for CR or LF */
-#define F_NOPROMPT      (1 << 10)       /* -n don't ask for login name */
-
-
-#define line_buf bb_common_bufsiz1
-
-/* The following is used for understandable diagnostics. */
-#ifdef DEBUGGING
-static FILE *dbf;
-#define DEBUGTERM "/dev/ttyp0"
-#define debug(...) do { fprintf(dbf, __VA_ARGS__); fflush(dbf); } while (0)
-#else
-#define debug(...) ((void)0)
-#endif
-
-
-/* bcode - convert speed string to speed code; return <= 0 on failure */
+#define F_INITSTRING    (1 << 0)   /* -I */
+#define F_LOCAL         (1 << 1)   /* -L */
+#define F_FAKEHOST      (1 << 2)   /* -H */
+#define F_CUSTISSUE     (1 << 3)   /* -f */
+#define F_RTSCTS        (1 << 4)   /* -h */
+#define F_NOISSUE       (1 << 5)   /* -i */
+#define F_LOGIN         (1 << 6)   /* -l */
+#define F_PARSE         (1 << 7)   /* -m */
+#define F_TIMEOUT       (1 << 8)   /* -t */
+#define F_WAITCRLF      (1 << 9)   /* -w */
+#define F_NOPROMPT      (1 << 10)  /* -n */
+
+
+/* convert speed string to speed code; return <= 0 on failure */
 static int bcode(const char *s)
 {
        int value = bb_strtou(s, NULL, 10); /* yes, int is intended! */
@@ -158,67 +132,53 @@ static int bcode(const char *s)
        return tty_value_to_baud(value);
 }
 
-/* parse_speeds - parse alternate baud rates */
-static void parse_speeds(struct options *op, char *arg)
+/* parse alternate baud rates */
+static void parse_speeds(char *arg)
 {
        char *cp;
 
        /* NB: at least one iteration is always done */
        debug("entered parse_speeds\n");
        while ((cp = strsep(&arg, ",")) != NULL) {
-               op->speeds[op->numspeed] = bcode(cp);
-               if (op->speeds[op->numspeed] < 0)
+               G.speeds[G.numspeed] = bcode(cp);
+               if (G.speeds[G.numspeed] < 0)
                        bb_error_msg_and_die("bad speed: %s", cp);
                /* note: arg "0" turns into speed B0 */
-               op->numspeed++;
-               if (op->numspeed > MAX_SPEED)
+               G.numspeed++;
+               if (G.numspeed > MAX_SPEED)
                        bb_error_msg_and_die("too many alternate speeds");
        }
        debug("exiting parse_speeds\n");
 }
 
-/* parse_args - parse command-line arguments */
-static void parse_args(char **argv, struct options *op, char **fakehost_p)
+/* parse command-line arguments */
+static void parse_args(char **argv)
 {
        char *ts;
+       int flags;
 
        opt_complementary = "-2:t+"; /* at least 2 args; -t N */
-       op->flags = getopt32(argv, opt_string,
-               &(op->initstring), fakehost_p, &(op->issue),
-               &(op->login), &op->timeout);
-       argv += optind;
-       if (op->flags & F_INITSTRING) {
-               const char *p = op->initstring;
-               char *q;
-
-               op->initstring = q = xstrdup(p);
-               /* copy optarg into op->initstring decoding \ddd
-                  octal codes into chars */
-               while (*p) {
-                       if (*p == '\\') {
-                               p++;
-                               *q++ = bb_process_escape_sequence(&p);
-                       } else {
-                               *q++ = *p++;
-                       }
-               }
-               *q = '\0';
+       flags = getopt32(argv, opt_string,
+               &G.initstring, &G.fakehost, &G.issue,
+               &G.login, &G.timeout
+       );
+       if (flags & F_INITSTRING) {
+               G.initstring = xstrdup(G.initstring);
+               /* decode \ddd octal codes into chars */
+               strcpy_and_process_escape_sequences(G.initstring, G.initstring);
        }
-       op->flags ^= F_ISSUE;           /* invert flag "show /etc/issue" */
+       argv += optind;
        debug("after getopt\n");
 
-       /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
-       op->tty = argv[0];      /* tty name */
-       ts = argv[1];           /* baud rate(s) */
+       /* We loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
+       G.tty_name = argv[0];
+       ts = argv[1];            /* baud rate(s) */
        if (isdigit(argv[0][0])) {
-               /* a number first, assume it's a speed (BSD style) */
-               op->tty = ts;   /* tty name is in argv[1] */
-               ts = argv[0];   /* baud rate(s) */
+               /* A number first, assume it's a speed (BSD style) */
+               G.tty_name = ts; /* tty name is in argv[1] */
+               ts = argv[0];    /* baud rate(s) */
        }
-       parse_speeds(op, ts);
-
-// TODO: if applet_name is set to "getty: TTY", bb_error_msg's get simpler!
-// grep for "%s:"
+       parse_speeds(ts);
 
        if (argv[2])
                xsetenv("TERM", argv[2]);
@@ -226,102 +186,208 @@ static void parse_args(char **argv, struct options *op, char **fakehost_p)
        debug("exiting parse_args\n");
 }
 
-/* open_tty - set up tty as standard { input, output, error } */
-static void open_tty(const char *tty)
+/* set up tty as standard input, output, error */
+static void open_tty(void)
 {
-       /* Set up new standard input, unless we are given an already opened port. */
-       if (NOT_LONE_DASH(tty)) {
-//             struct stat st;
-//             int cur_dir_fd;
-//             int fd;
-
-               /* Sanity checks... */
-//             cur_dir_fd = xopen(".", O_DIRECTORY | O_NONBLOCK);
-//             xchdir("/dev");
-//             xstat(tty, &st);
-//             if (!S_ISCHR(st.st_mode))
-//                     bb_error_msg_and_die("%s: not a character device", tty);
-
-               if (tty[0] != '/')
-                       tty = xasprintf("/dev/%s", tty); /* will leak it */
-
-               /* Open the tty as standard input. */
+       /* Set up new standard input, unless we are given an already opened port */
+       if (NOT_LONE_DASH(G.tty_name)) {
+               if (G.tty_name[0] != '/')
+                       G.tty_name = xasprintf("/dev/%s", G.tty_name); /* will leak it */
+
+               /* Open the tty as standard input */
                debug("open(2)\n");
                close(0);
-               /*fd =*/ xopen(tty, O_RDWR | O_NONBLOCK); /* uses fd 0 */
-
-//             /* Restore current directory */
-//             fchdir(cur_dir_fd);
-
-               /* Open the tty as standard input, continued */
-//             xmove_fd(fd, 0);
-//             /* fd is >= cur_dir_fd, and cur_dir_fd gets closed too here: */
-//             while (fd > 2)
-//                     close(fd--);
+               xopen(G.tty_name, O_RDWR | O_NONBLOCK); /* uses fd 0 */
 
-               /* Set proper protections and ownership. */
+               /* Set proper protections and ownership */
                fchown(0, 0, 0);        /* 0:0 */
                fchmod(0, 0620);        /* crw--w---- */
        } else {
+               char *n;
                /*
-                * Standard input should already be connected to an open port. Make
-                * sure it is open for read/write.
+                * Standard input should already be connected to an open port.
+                * Make sure it is open for read/write.
                 */
-               if ((fcntl(0, F_GETFL) & O_RDWR) != O_RDWR)
+               if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR)
                        bb_error_msg_and_die("stdin is not open for read/write");
+
+               /* Try to get real tty name instead of "-" */
+               n = xmalloc_ttyname(0);
+               if (n)
+                       G.tty_name = n;
        }
+       applet_name = xasprintf("getty: %s", skip_dev_pfx(G.tty_name));
 }
 
-/* termios_init - initialize termios settings */
-static void termios_init(struct termios *tp, int speed, struct options *op)
+static void set_tty_attrs(void)
 {
-       speed_t ispeed, ospeed;
-       /*
-        * Initial termios settings: 8-bit characters, raw-mode, blocking i/o.
+       if (tcsetattr_stdin_TCSANOW(&G.tty_attrs) < 0)
+               bb_perror_msg_and_die("tcsetattr");
+}
+
+/* We manipulate tty_attrs this way:
+ * - first, we read existing tty_attrs
+ * - init_tty_attrs modifies some parts and sets it
+ * - auto_baud and/or BREAK processing can set different speed and set tty attrs
+ * - finalize_tty_attrs again modifies some parts and sets tty attrs before
+ *   execing login
+ */
+static void init_tty_attrs(int speed)
+{
+       /* Try to drain output buffer, with 5 sec timeout.
+        * Added on request from users of ~600 baud serial interface
+        * with biggish buffer on a 90MHz CPU.
+        * They were losing hundreds of bytes of buffered output
+        * on tcflush.
+        */
+       signal_no_SA_RESTART_empty_mask(SIGALRM, record_signo);
+       alarm(5);
+       tcdrain(STDIN_FILENO);
+       alarm(0);
+
+       /* Flush input and output queues, important for modems! */
+       tcflush(STDIN_FILENO, TCIOFLUSH);
+
+       /* Set speed if it wasn't specified as "0" on command line */
+       if (speed != B0)
+               cfsetspeed(&G.tty_attrs, speed);
+
+       /* Initial settings: 8-bit characters, raw mode, blocking i/o.
         * Special characters are set after we have read the login name; all
-        * reads will be done in raw mode anyway. Errors will be dealt with
-        * later on.
+        * reads will be done in raw mode anyway.
         */
-#ifdef __linux__
-       /* flush input and output queues, important for modems! */
-       ioctl(0, TCFLSH, TCIOFLUSH); /* tcflush(0, TCIOFLUSH)? - same */
+       /* Clear all bits except: */
+       G.tty_attrs.c_cflag &= (0
+               /* 2 stop bits (1 otherwise)
+                * Enable parity bit (both on input and output)
+                * Odd parity (else even)
+                */
+               | CSTOPB | PARENB | PARODD
+#ifdef CMSPAR
+               | CMSPAR  /* mark or space parity */
+#endif
+#ifdef CBAUD
+               | CBAUD   /* (output) baud rate */
+#endif
+#ifdef CBAUDEX
+               | CBAUDEX /* (output) baud rate */
+#endif
+#ifdef CIBAUD
+               | CIBAUD   /* input baud rate */
 #endif
-       ispeed = ospeed = speed;
-       if (speed == B0) {
-               /* Speed was specified as "0" on command line.
-                * Just leave it unchanged */
-               ispeed = cfgetispeed(tp);
-               ospeed = cfgetospeed(tp);
+       );
+       /* Set: 8 bits; hang up (drop DTR) on last close; enable receive */
+       G.tty_attrs.c_cflag |= CS8 | HUPCL | CREAD;
+       if (option_mask32 & F_LOCAL) {
+               /* ignore Carrier Detect pin:
+                * opens don't block when CD is low,
+                * losing CD doesn't hang up processes whose ctty is this tty
+                */
+               G.tty_attrs.c_cflag |= CLOCAL;
        }
-       tp->c_cflag = CS8 | HUPCL | CREAD;
-       if (op->flags & F_LOCAL)
-               tp->c_cflag |= CLOCAL;
-       cfsetispeed(tp, ispeed);
-       cfsetospeed(tp, ospeed);
-
-       tp->c_iflag = tp->c_lflag = tp->c_line = 0;
-       tp->c_oflag = OPOST | ONLCR;
-       tp->c_cc[VMIN] = 1;
-       tp->c_cc[VTIME] = 0;
-
-       /* Optionally enable hardware flow control */
 #ifdef CRTSCTS
-       if (op->flags & F_RTSCTS)
-               tp->c_cflag |= CRTSCTS;
+       if (option_mask32 & F_RTSCTS)
+               G.tty_attrs.c_cflag |= CRTSCTS; /* flow control using RTS/CTS pins */
+#endif
+       G.tty_attrs.c_iflag = 0;
+       G.tty_attrs.c_lflag = 0;
+       /* non-raw output; add CR to each NL */
+       G.tty_attrs.c_oflag = OPOST | ONLCR;
+
+       /* reads would block only if < 1 char is available */
+       G.tty_attrs.c_cc[VMIN] = 1;
+       /* no timeout (reads block forever) */
+       G.tty_attrs.c_cc[VTIME] = 0;
+#ifdef __linux__
+       G.tty_attrs.c_line = 0;
 #endif
 
-       tcsetattr_stdin_TCSANOW(tp);
+       set_tty_attrs();
 
        debug("term_io 2\n");
 }
 
-/* auto_baud - extract baud rate from modem status message */
-static void auto_baud(char *buf, unsigned size_buf, struct termios *tp)
+static void finalize_tty_attrs(void)
+{
+       /* software flow control on output (stop sending if XOFF is recvd);
+        * and on input (send XOFF when buffer is full)
+        */
+       G.tty_attrs.c_iflag |= IXON | IXOFF;
+       if (G.eol == '\r') {
+               G.tty_attrs.c_iflag |= ICRNL; /* map CR on input to NL */
+       }
+       /* Other bits in c_iflag:
+        * IXANY   Any recvd char enables output (any char is also a XON)
+        * INPCK   Enable parity check
+        * IGNPAR  Ignore parity errors (drop bad bytes)
+        * PARMRK  Mark parity errors with 0xff, 0x00 prefix
+        *         (else bad byte is received as 0x00)
+        * ISTRIP  Strip parity bit
+        * IGNBRK  Ignore break condition
+        * BRKINT  Send SIGINT on break - maybe set this?
+        * INLCR   Map NL to CR
+        * IGNCR   Ignore CR
+        * ICRNL   Map CR to NL
+        * IUCLC   Map uppercase to lowercase
+        * IMAXBEL Echo BEL on input line too long
+        * IUTF8   Appears to affect tty's idea of char widths,
+        *         observed to improve backspacing through Unicode chars
+        */
+
+       /* line buffered input (NL or EOL or EOF chars end a line);
+        * recognize INT/QUIT/SUSP chars;
+        * echo input chars;
+        * echo BS-SP-BS on erase character;
+        * echo kill char specially, not as ^c (ECHOKE controls how exactly);
+        * erase all input via BS-SP-BS on kill char (else go to next line)
+        */
+       G.tty_attrs.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
+       /* Other bits in c_lflag:
+        * XCASE   Map uppercase to \lowercase [tried, doesn't work]
+        * ECHONL  Echo NL even if ECHO is not set
+        * ECHOCTL Echo ctrl chars as ^c (else don't echo) - maybe set this?
+        * ECHOPRT On erase, echo erased chars
+        *         [qwe<BS><BS><BS> input looks like "qwe\ewq/" on screen]
+        * NOFLSH  Don't flush input buffer after interrupt or quit chars
+        * IEXTEN  Enable extended functions (??)
+        *         [glibc says it enables c_cc[LNEXT] "enter literal char"
+        *         and c_cc[VDISCARD] "toggle discard buffered output" chars]
+        * FLUSHO  Output being flushed (c_cc[VDISCARD] is in effect)
+        * PENDIN  Retype pending input at next read or input char
+        *         (c_cc[VREPRINT] is being processed)
+        * TOSTOP  Send SIGTTOU for background output
+        *         (why "stty sane" unsets this bit?)
+        */
+
+       G.tty_attrs.c_cc[VINTR] = CTL('C');
+       G.tty_attrs.c_cc[VQUIT] = CTL('\\');
+       G.tty_attrs.c_cc[VEOF] = CTL('D');
+       G.tty_attrs.c_cc[VEOL] = '\n';
+#ifdef VSWTC
+       G.tty_attrs.c_cc[VSWTC] = 0;
+#endif
+#ifdef VSWTCH
+       G.tty_attrs.c_cc[VSWTCH] = 0;
+#endif
+       G.tty_attrs.c_cc[VKILL] = CTL('U');
+       /* Other control chars:
+        * VEOL2
+        * VERASE, VWERASE - (word) erase. we may set VERASE in get_logname
+        * VREPRINT - reprint current input buffer
+        * VLNEXT, VDISCARD, VSTATUS
+        * VSUSP, VDSUSP - send (delayed) SIGTSTP
+        * VSTART, VSTOP - chars used for IXON/IXOFF
+        */
+
+       set_tty_attrs();
+
+       /* Now the newline character should be properly written */
+       full_write(STDOUT_FILENO, "\n", 1);
+}
+
+/* extract baud rate from modem status message */
+static void auto_baud(void)
 {
-       int speed;
-       int vmin;
-       unsigned iflag;
-       char *bp;
        int nread;
 
        /*
@@ -339,278 +405,181 @@ static void auto_baud(char *buf, unsigned size_buf, struct termios *tp)
         * modem status messages is enabled.
         */
 
-       /*
-        * Use 7-bit characters, don't block if input queue is empty. Errors will
-        * be dealt with later on.
-        */
-       iflag = tp->c_iflag;
-       tp->c_iflag |= ISTRIP;          /* enable 8th-bit stripping */
-       vmin = tp->c_cc[VMIN];
-       tp->c_cc[VMIN] = 0;             /* don't block if queue empty */
-       tcsetattr_stdin_TCSANOW(tp);
+       G.tty_attrs.c_cc[VMIN] = 0; /* don't block reads (min read is 0 chars) */
+       set_tty_attrs();
 
        /*
         * Wait for a while, then read everything the modem has said so far and
         * try to extract the speed of the dial-in call.
         */
        sleep(1);
-       nread = safe_read(STDIN_FILENO, buf, size_buf - 1);
+       nread = safe_read(STDIN_FILENO, G.line_buf, sizeof(G.line_buf) - 1);
        if (nread > 0) {
-               buf[nread] = '\0';
-               for (bp = buf; bp < buf + nread; bp++) {
+               int speed;
+               char *bp;
+               G.line_buf[nread] = '\0';
+               for (bp = G.line_buf; bp < G.line_buf + nread; bp++) {
                        if (isdigit(*bp)) {
                                speed = bcode(bp);
-                               if (speed > 0) {
-                                       tp->c_cflag &= ~CBAUD;
-                                       tp->c_cflag |= speed;
-                               }
+                               if (speed > 0)
+                                       cfsetspeed(&G.tty_attrs, speed);
                                break;
                        }
                }
        }
 
-       /* Restore terminal settings. Errors will be dealt with later on. */
-       tp->c_iflag = iflag;
-       tp->c_cc[VMIN] = vmin;
-       tcsetattr_stdin_TCSANOW(tp);
-}
-
-/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
-static void do_prompt(struct options *op)
-{
-#ifdef ISSUE
-       print_login_issue(op->issue, op->tty);
-#endif
-       print_login_prompt();
-}
-
-#ifdef HANDLE_ALLCAPS
-/* all_is_upcase - string contains upper case without lower case */
-/* returns 1 if true, 0 if false */
-static int all_is_upcase(const char *s)
-{
-       while (*s)
-               if (islower(*s++))
-                       return 0;
-       return 1;
+       /* Restore terminal settings */
+       G.tty_attrs.c_cc[VMIN] = 1; /* restore to value set by init_tty_attrs */
+       set_tty_attrs();
 }
-#endif
 
-/* get_logname - get user name, establish parity, speed, erase, kill, eol;
- * return NULL on BREAK, logname on success */
-static char *get_logname(char *logname, unsigned size_logname,
-               struct options *op, struct chardata *cp)
+/* get user name, establish parity, speed, erase, kill, eol;
+ * return NULL on BREAK, logname on success
+ */
+static char *get_logname(void)
 {
        char *bp;
-       char c;                         /* input character, full eight bits */
-       char ascval;                    /* low 7 bits of input character */
-       int bits;                       /* # of "1" bits per character */
-       int mask;                       /* mask with 1 bit up */
-       static const char erase[][3] = {/* backspace-space-backspace */
-               "\010\040\010",                 /* space parity */
-               "\010\040\010",                 /* odd parity */
-               "\210\240\210",                 /* even parity */
-               "\010\040\010",                 /* 8 bit no parity */
-       };
-
-       /* NB: *cp is pre-initialized with init_chardata */
-
-       /* Flush pending input (esp. after parsing or switching the baud rate). */
-       sleep(1);
-       ioctl(0, TCFLSH, TCIFLUSH); /* tcflush(0, TCIOFLUSH)? - same */
+       char c;
 
-       /* Prompt for and read a login name. */
-       logname[0] = '\0';
-       while (!logname[0]) {
-               /* Write issue file and prompt, with "parity" bit == 0. */
-               do_prompt(op);
+       /* Flush pending input (esp. after parsing or switching the baud rate) */
+       usleep(100*1000); /* 0.1 sec */
+       tcflush(STDIN_FILENO, TCIFLUSH);
 
-               /* Read name, watch for break, parity, erase, kill, end-of-line. */
-               bp = logname;
-               cp->eol = '\0';
-               while (cp->eol == '\0') {
+       /* Prompt for and read a login name */
+       do {
+               /* Write issue file and prompt */
+#ifdef ISSUE
+               if (!(option_mask32 & F_NOISSUE))
+                       print_login_issue(G.issue, G.tty_name);
+#endif
+               print_login_prompt();
 
-                       /* Do not report trivial EINTR/EIO errors. */
+               /* Read name, watch for break, erase, kill, end-of-line */
+               bp = G.line_buf;
+               while (1) {
+                       /* Do not report trivial EINTR/EIO errors */
                        errno = EINTR; /* make read of 0 bytes be silent too */
                        if (read(STDIN_FILENO, &c, 1) < 1) {
+                               finalize_tty_attrs();
                                if (errno == EINTR || errno == EIO)
                                        exit(EXIT_SUCCESS);
-                               bb_perror_msg_and_die("%s: read", op->tty);
-                       }
-
-                       /* BREAK. If we have speeds to try,
-                        * return NULL (will switch speeds and return here) */
-                       if (c == '\0' && op->numspeed > 1)
-                               return NULL;
-
-                       /* Do parity bit handling. */
-                       if (!(op->flags & F_LOCAL) && (c & 0x80)) {       /* "parity" bit on? */
-                               bits = 1;
-                               mask = 1;
-                               while (mask & 0x7f) {
-                                       if (mask & c)
-                                               bits++; /* count "1" bits */
-                                       mask <<= 1;
-                               }
-                               /* ... |= 2 - even, 1 - odd */
-                               cp->parity |= 2 - (bits & 1);
+                               bb_perror_msg_and_die(bb_msg_read_error);
                        }
 
-                       /* Do erase, kill and end-of-line processing. */
-                       ascval = c & 0x7f;
-                       switch (ascval) {
-                       case CR:
-                       case NL:
-                               *bp = '\0';             /* terminate logname */
-                               cp->eol = ascval;       /* set end-of-line char */
-                               break;
-                       case BS:
-                       case DEL:
-#ifdef ANCIENT_BS_KILL_CHARS
-                       case '#':
-#endif
-                               cp->erase = ascval;     /* set erase character */
-                               if (bp > logname) {
-                                       full_write(STDOUT_FILENO, erase[cp->parity], 3);
+                       switch (c) {
+                       case '\r':
+                       case '\n':
+                               *bp = '\0';
+                               G.eol = c;
+                               goto got_logname;
+                       case CTL('H'):
+                       case 0x7f:
+                               G.tty_attrs.c_cc[VERASE] = c;
+                               if (bp > G.line_buf) {
+                                       full_write(STDOUT_FILENO, "\010 \010", 3);
                                        bp--;
                                }
                                break;
                        case CTL('U'):
-#ifdef ANCIENT_BS_KILL_CHARS
-                       case '@':
-#endif
-                               cp->kill = ascval;      /* set kill character */
-                               while (bp > logname) {
-                                       full_write(STDOUT_FILENO, erase[cp->parity], 3);
+                               while (bp > G.line_buf) {
+                                       full_write(STDOUT_FILENO, "\010 \010", 3);
                                        bp--;
                                }
                                break;
+                       case CTL('C'):
                        case CTL('D'):
+                               finalize_tty_attrs();
                                exit(EXIT_SUCCESS);
+                       case '\0':
+                               /* BREAK. If we have speeds to try,
+                                * return NULL (will switch speeds and return here) */
+                               if (G.numspeed > 1)
+                                       return NULL;
+                               /* fall through and ignore it */
                        default:
-                               if (ascval < ' ') {
+                               if ((unsigned char)c < ' ') {
                                        /* ignore garbage characters */
-                               } else if ((int)(bp - logname) >= size_logname - 1) {
-                                       bb_error_msg_and_die("%s: input overrun", op->tty);
-                               } else {
-                                       full_write(STDOUT_FILENO, &c, 1); /* echo the character */
-                                       *bp++ = ascval; /* and store it */
+                               } else if ((int)(bp - G.line_buf) < sizeof(G.line_buf) - 1) {
+                                       /* echo and store the character */
+                                       full_write(STDOUT_FILENO, &c, 1);
+                                       *bp++ = c;
                                }
                                break;
                        }
-               }
-       }
-       /* Handle names with upper case and no lower case. */
-
-#ifdef HANDLE_ALLCAPS
-       cp->capslock = all_is_upcase(logname);
-       if (cp->capslock) {
-               for (bp = logname; *bp; bp++)
-                       if (isupper(*bp))
-                               *bp = tolower(*bp);     /* map name to lower case */
-       }
-#endif
-       return logname;
+               } /* end of get char loop */
+ got_logname: ;
+       } while (G.line_buf[0] == '\0');  /* while logname is empty */
+
+       return G.line_buf;
 }
 
-/* termios_final - set the final tty mode bits */
-static void termios_final(struct options *op, struct termios *tp, struct chardata *cp)
+static void alarm_handler(int sig UNUSED_PARAM)
 {
-       /* General terminal-independent stuff. */
-//     tp->c_iflag |= IXON | IXOFF;    /* 2-way flow control */
-       tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
-       /* no longer| ECHOCTL | ECHOPRT */
-       tp->c_oflag |= OPOST;
-       /* tp->c_cflag = 0; */
-       tp->c_cc[VINTR] = DEF_INTR;     /* default interrupt */
-       tp->c_cc[VQUIT] = DEF_QUIT;     /* default quit */
-       tp->c_cc[VEOF] = DEF_EOF;       /* default EOF character */
-       tp->c_cc[VEOL] = DEF_EOL;
-       tp->c_cc[VSWTC] = DEF_SWITCH;   /* default switch character */
-
-       /* Account for special characters seen in input. */
-       if (cp->eol == CR) {
-               tp->c_iflag |= ICRNL;   /* map CR in input to NL */
-               tp->c_oflag |= ONLCR;   /* map NL in output to CR-NL */
-       }
-       tp->c_cc[VERASE] = cp->erase;   /* set erase character */
-       tp->c_cc[VKILL] = cp->kill;     /* set kill character */
-
-       /* Account for the presence or absence of parity bits in input. */
-       switch (cp->parity) {
-       case 0:                                 /* space (always 0) parity */
-// I bet most people go here - they use only 7-bit chars in usernames....
-               break;
-       case 1:                                 /* odd parity */
-               tp->c_cflag |= PARODD;
-               /* FALLTHROUGH */
-       case 2:                                 /* even parity */
-               tp->c_cflag |= PARENB;
-               tp->c_iflag |= INPCK | ISTRIP;
-               /* FALLTHROUGH */
-       case (1 | 2):                           /* no parity bit */
-               tp->c_cflag &= ~CSIZE;
-               tp->c_cflag |= CS7;
-// FIXME: wtf? case 3: we saw both even and odd 8-bit bytes -
-// it's probably some umlauts etc, but definitely NOT 7-bit!!!
-// Entire parity detection madness here just begs for deletion...
-               break;
-       }
-
-       /* Account for upper case without lower case. */
-#ifdef HANDLE_ALLCAPS
-       if (cp->capslock) {
-               tp->c_iflag |= IUCLC;
-               tp->c_lflag |= XCASE;
-               tp->c_oflag |= OLCUC;
-       }
-#endif
-       /* Optionally enable hardware flow control */
-#ifdef CRTSCTS
-       if (op->flags & F_RTSCTS)
-               tp->c_cflag |= CRTSCTS;
-#endif
-
-       /* Finally, make the new settings effective */
-       /* It's tcsetattr_stdin_TCSANOW() + error check */
-       ioctl_or_perror_and_die(0, TCSETS, tp, "%s: TCSETS", op->tty);
+       finalize_tty_attrs();
+       _exit(EXIT_SUCCESS);
 }
 
 int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int getty_main(int argc UNUSED_PARAM, char **argv)
 {
        int n;
-       pid_t pid;
-       char *fakehost = NULL;          /* Fake hostname for ut_host */
-       char *logname;                  /* login name, given to /bin/login */
-       /* Merging these into "struct local" may _seem_ to reduce
-        * parameter passing, but today's gcc will inline
-        * statics which are called once anyway, so don't do that */
-       struct chardata chardata;       /* set by get_logname() */
-       struct termios termios;         /* terminal mode bits */
-       struct options options;
-
-       chardata = init_chardata;
-
-       memset(&options, 0, sizeof(options));
-       options.login = _PATH_LOGIN;    /* default login program */
-       options.tty = "tty1";           /* default tty line */
-       options.initstring = "";        /* modem init string */
+       pid_t pid, tsid;
+       char *logname;
+
+       INIT_G();
+       G.login = _PATH_LOGIN;    /* default login program */
 #ifdef ISSUE
-       options.issue = ISSUE;          /* default issue file */
+       G.issue = ISSUE;          /* default issue file */
 #endif
+       G.eol = '\r';
+
+       /* Parse command-line arguments */
+       parse_args(argv);
+
+       /* Create new session and pgrp, lose controlling tty */
+       pid = setsid();  /* this also gives us our pid :) */
+       if (pid < 0) {
+               int fd;
+               /* :(
+                * docs/ctty.htm says:
+                * "This is allowed only when the current process
+                *  is not a process group leader".
+                * Thus, setsid() will fail if we _already_ are
+                * a session leader - which is quite possible for getty!
+                */
+               pid = getpid();
+               if (getsid(0) != pid) {
+                       //for debugging:
+                       //bb_perror_msg_and_die("setsid failed:"
+                       //      " pid %d ppid %d"
+                       //      " sid %d pgid %d",
+                       //      pid, getppid(),
+                       //      getsid(0), getpgid(0));
+                       bb_perror_msg_and_die("setsid");
+               }
+               /* Looks like we are already a session leader.
+                * In this case (setsid failed) we may still have ctty,
+                * and it may be different from tty we need to control!
+                * If we still have ctty, on Linux ioctl(TIOCSCTTY)
+                * (which we are going to use a bit later) always fails -
+                * even if we try to take ctty which is already ours!
+                * Try to drop old ctty now to prevent that.
+                * Use O_NONBLOCK: old ctty may be a serial line.
+                */
+               fd = open("/dev/tty", O_RDWR | O_NONBLOCK);
+               if (fd >= 0) {
+                       /* TIOCNOTTY sends SIGHUP to the foreground
+                        * process group - which may include us!
+                        * Make sure to not die on it:
+                        */
+                       sighandler_t old = signal(SIGHUP, SIG_IGN);
+                       ioctl(fd, TIOCNOTTY);
+                       close(fd);
+                       signal(SIGHUP, old);
+               }
+       }
 
-       /* Parse command-line arguments. */
-       parse_args(argv, &options, &fakehost);
-
-       logmode = LOGMODE_NONE;
-
-       /* Create new session, lose controlling tty, if any */
-       /* docs/ctty.htm says:
-        * "This is allowed only when the current process
-        *  is not a process group leader" - is this a problem? */
-       setsid();
-       /* close stdio, and stray descriptors, just in case */
+       /* Close stdio, and stray descriptors, just in case */
        n = xopen(bb_dev_null, O_RDWR);
        /* dup2(n, 0); - no, we need to handle "getty - 9600" too */
        xdup2(n, 1);
@@ -634,13 +603,25 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
 #endif
 
        /* Open the tty as standard input, if it is not "-" */
-       /* If it's not "-" and not taken yet, it will become our ctty */
        debug("calling open_tty\n");
-       open_tty(options.tty);
-       ndelay_off(0);
+       open_tty();
+       ndelay_off(STDIN_FILENO);
        debug("duping\n");
-       xdup2(0, 1);
-       xdup2(0, 2);
+       xdup2(STDIN_FILENO, 1);
+       xdup2(STDIN_FILENO, 2);
+
+       /* Steal ctty if we don't have it yet */
+       tsid = tcgetsid(STDIN_FILENO);
+       if (tsid < 0 || pid != tsid) {
+               if (ioctl(STDIN_FILENO, TIOCSCTTY, /*force:*/ (long)1) < 0)
+                       bb_perror_msg_and_die("TIOCSCTTY");
+       }
+
+#ifdef __linux__
+       /* Make ourself a foreground process group within our session */
+       if (tcsetpgrp(STDIN_FILENO, pid) < 0)
+               bb_perror_msg_and_die("tcsetpgrp");
+#endif
 
        /*
         * The following ioctl will fail if stdin is not a tty, but also when
@@ -650,87 +631,70 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
         * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
         * 5 seconds seems to be a good value.
         */
-       /* tcgetattr() + error check */
-       ioctl_or_perror_and_die(0, TCGETS, &termios, "%s: TCGETS", options.tty);
-
-       pid = getpid();
-#ifdef __linux__
-// FIXME: do we need this? Otherwise "-" case seems to be broken...
-       // /* Forcibly make fd 0 our controlling tty, even if another session
-       //  * has it as a ctty. (Another session loses ctty). */
-       // ioctl(0, TIOCSCTTY, (void*)1);
-       /* Make ourself a foreground process group within our session */
-       tcsetpgrp(0, pid);
-#endif
+       if (tcgetattr(STDIN_FILENO, &G.tty_attrs) < 0)
+               bb_perror_msg_and_die("tcgetattr");
 
        /* Update the utmp file. This tty is ours now! */
-       update_utmp(pid, LOGIN_PROCESS, options.tty, "LOGIN", fakehost);
+       update_utmp(pid, LOGIN_PROCESS, G.tty_name, "LOGIN", G.fakehost);
 
-       /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
-       debug("calling termios_init\n");
-       termios_init(&termios, options.speeds[0], &options);
+       /* Initialize tty attrs (raw mode, eight-bit, blocking i/o) */
+       debug("calling init_tty_attrs\n");
+       init_tty_attrs(G.speeds[0]);
 
        /* Write the modem init string and DON'T flush the buffers */
-       if (options.flags & F_INITSTRING) {
+       if (option_mask32 & F_INITSTRING) {
                debug("writing init string\n");
-               full_write1_str(options.initstring);
+               full_write1_str(G.initstring);
        }
 
        /* Optionally detect the baud rate from the modem status message */
        debug("before autobaud\n");
-       if (options.flags & F_PARSE)
-               auto_baud(line_buf, sizeof(line_buf), &termios);
+       if (option_mask32 & F_PARSE)
+               auto_baud();
 
        /* Set the optional timer */
-       alarm(options.timeout); /* if 0, alarm is not set */
+       signal(SIGALRM, alarm_handler);
+       alarm(G.timeout); /* if 0, alarm is not set */
 
        /* Optionally wait for CR or LF before writing /etc/issue */
-       if (options.flags & F_WAITCRLF) {
+       if (option_mask32 & F_WAITCRLF) {
                char ch;
-
                debug("waiting for cr-lf\n");
                while (safe_read(STDIN_FILENO, &ch, 1) == 1) {
                        debug("read %x\n", (unsigned char)ch);
-                       ch &= 0x7f;                     /* strip "parity bit" */
                        if (ch == '\n' || ch == '\r')
                                break;
                }
        }
 
        logname = NULL;
-       if (!(options.flags & F_NOPROMPT)) {
-               /* NB:termios_init already set line speed
-                * to options.speeds[0] */
+       if (!(option_mask32 & F_NOPROMPT)) {
+               /* NB: init_tty_attrs already set line speed
+                * to G.speeds[0] */
                int baud_index = 0;
 
                while (1) {
-                       /* Read the login name. */
+                       /* Read the login name */
                        debug("reading login name\n");
-                       logname = get_logname(line_buf, sizeof(line_buf),
-                                       &options, &chardata);
+                       logname = get_logname();
                        if (logname)
                                break;
-                       /* we are here only if options.numspeed > 1 */
-                       baud_index = (baud_index + 1) % options.numspeed;
-                       cfsetispeed(&termios, options.speeds[baud_index]);
-                       cfsetospeed(&termios, options.speeds[baud_index]);
-                       tcsetattr_stdin_TCSANOW(&termios);
+                       /* We are here only if G.numspeed > 1 */
+                       baud_index = (baud_index + 1) % G.numspeed;
+                       cfsetspeed(&G.tty_attrs, G.speeds[baud_index]);
+                       set_tty_attrs();
                }
        }
 
-       /* Disable timer. */
+       /* Disable timer */
        alarm(0);
 
-       /* Finalize the termios settings. */
-       termios_final(&options, &termios, &chardata);
-
-       /* Now the newline character should be properly written. */
-       full_write(STDOUT_FILENO, "\n", 1);
+       finalize_tty_attrs();
 
-       /* Let the login program take care of password validation. */
+       /* Let the login program take care of password validation */
        /* We use PATH because we trust that root doesn't set "bad" PATH,
-        * and getty is not suid-root applet. */
+        * and getty is not suid-root applet */
        /* With -n, logname == NULL, and login will ask for username instead */
-       BB_EXECLP(options.login, options.login, "--", logname, NULL);
-       bb_error_msg_and_die("%s: can't exec %s", options.tty, options.login);
+       BB_EXECLP(G.login, G.login, "--", logname, (char *)0);
+       bb_error_msg_and_die("can't execute '%s'", G.login);
 }
index 88ed0af..a4b19cc 100644 (file)
@@ -1,12 +1,18 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define login_trivial_usage
+//usage:       "[-p] [-h HOST] [[-f] USER]"
+//usage:#define login_full_usage "\n\n"
+//usage:       "Begin a new session on the system\n"
+//usage:     "\n       -f      Don't authenticate (user already authenticated)"
+//usage:     "\n       -h      Name of the remote host"
+//usage:     "\n       -p      Preserve environment"
+
 #include "libbb.h"
 #include <syslog.h>
-#if ENABLE_FEATURE_UTMP
-# include <utmp.h> /* USER_PROCESS */
-#endif
 #include <sys/resource.h>
 
 #if ENABLE_SELINUX
@@ -31,11 +37,17 @@ static const struct pam_conv conv = {
 enum {
        TIMEOUT = 60,
        EMPTY_USERNAME_COUNT = 10,
-       USERNAME_SIZE = 32,
+       /* Some users found 32 chars limit to be too low: */
+       USERNAME_SIZE = 64,
        TTYNAME_SIZE = 32,
 };
 
-static char* short_tty;
+struct globals {
+       struct termios tty_attrs;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { } while (0)
+
 
 #if ENABLE_FEATURE_NOLOGIN
 static void die_if_nologin(void)
@@ -68,7 +80,7 @@ static void die_if_nologin(void)
 #endif
 
 #if ENABLE_FEATURE_SECURETTY && !ENABLE_PAM
-static int check_securetty(void)
+static int check_securetty(const char *short_tty)
 {
        char *buf = (char*)"/etc/securetty"; /* any non-NULL is ok */
        parser_t *parser = config_open2("/etc/securetty", fopen_for_read);
@@ -83,7 +95,7 @@ static int check_securetty(void)
        return buf != NULL;
 }
 #else
-static ALWAYS_INLINE int check_securetty(void) { return 1; }
+static ALWAYS_INLINE int check_securetty(const char *short_tty UNUSED_PARAM) { return 1; }
 #endif
 
 #if ENABLE_SELINUX
@@ -124,7 +136,7 @@ static void run_login_script(struct passwd *pw, char *full_tty)
                xsetenv("LOGIN_UID", utoa(pw->pw_uid));
                xsetenv("LOGIN_GID", utoa(pw->pw_gid));
                xsetenv("LOGIN_SHELL", pw->pw_shell);
-               spawn_and_wait(t_argv); /* NOMMU-friendly */
+               spawn_and_wait(t_argv); /* NOMMU-friendly */
                unsetenv("LOGIN_TTY");
                unsetenv("LOGIN_USER");
                unsetenv("LOGIN_UID");
@@ -136,6 +148,29 @@ static void run_login_script(struct passwd *pw, char *full_tty)
 void run_login_script(struct passwd *pw, char *full_tty);
 #endif
 
+#if ENABLE_LOGIN_SESSION_AS_CHILD && ENABLE_PAM
+static void login_pam_end(pam_handle_t *pamh)
+{
+       int pamret;
+
+       pamret = pam_setcred(pamh, PAM_DELETE_CRED);
+       if (pamret != PAM_SUCCESS) {
+               bb_error_msg("pam_%s failed: %s (%d)", "setcred",
+                       pam_strerror(pamh, pamret), pamret);
+       }
+       pamret = pam_close_session(pamh, 0);
+       if (pamret != PAM_SUCCESS) {
+               bb_error_msg("pam_%s failed: %s (%d)", "close_session",
+                       pam_strerror(pamh, pamret), pamret);
+       }
+       pamret = pam_end(pamh, pamret);
+       if (pamret != PAM_SUCCESS) {
+               bb_error_msg("pam_%s failed: %s (%d)", "end",
+                       pam_strerror(pamh, pamret), pamret);
+       }
+}
+#endif /* ENABLE_PAM */
+
 static void get_username_or_die(char *buf, int size_buf)
 {
        int c, cntdown;
@@ -179,15 +214,21 @@ static void motd(void)
 
 static void alarm_handler(int sig UNUSED_PARAM)
 {
-       /* This is the escape hatch!  Poor serial line users and the like
+       /* This is the escape hatch! Poor serial line users and the like
         * arrive here when their connection is broken.
         * We don't want to block here */
-       ndelay_on(1);
-       printf("\r\nLogin timed out after %d seconds\r\n", TIMEOUT);
+       ndelay_on(STDOUT_FILENO);
+       /* Test for correct attr restoring:
+        * run "getty 0 -" from a shell, enter bogus username, stop at
+        * password prompt, let it time out. Without the tcsetattr below,
+        * when you are back at shell prompt, echo will be still off.
+        */
+       tcsetattr_stdin_TCSANOW(&G.tty_attrs);
+       printf("\r\nLogin timed out after %u seconds\r\n", TIMEOUT);
        fflush_all();
        /* unix API is brain damaged regarding O_NONBLOCK,
         * we should undo it, or else we can affect other processes */
-       ndelay_off(1);
+       ndelay_off(STDOUT_FILENO);
        _exit(EXIT_SUCCESS);
 }
 
@@ -201,7 +242,6 @@ int login_main(int argc UNUSED_PARAM, char **argv)
        };
        char *fromhost;
        char username[USERNAME_SIZE];
-       const char *shell;
        int run_by_root;
        unsigned opt;
        int count = 0;
@@ -209,6 +249,7 @@ int login_main(int argc UNUSED_PARAM, char **argv)
        char *opt_host = NULL;
        char *opt_user = opt_user; /* for compiler */
        char *full_tty;
+       char *short_tty;
        IF_SELINUX(security_context_t user_sid = NULL;)
 #if ENABLE_PAM
        int pamret;
@@ -217,11 +258,13 @@ int login_main(int argc UNUSED_PARAM, char **argv)
        const char *failed_msg;
        struct passwd pwdstruct;
        char pwdbuf[256];
+       char **pamenv;
+#endif
+#if ENABLE_LOGIN_SESSION_AS_CHILD
+       pid_t child_pid;
 #endif
 
-       username[0] = '\0';
-       signal(SIGALRM, alarm_handler);
-       alarm(TIMEOUT);
+       INIT_G();
 
        /* More of suid paranoia if called by non-root: */
        /* Clear dangerous stuff, set PATH */
@@ -233,6 +276,7 @@ int login_main(int argc UNUSED_PARAM, char **argv)
         * (The name of the function is misleading. Not daemonizing here.) */
        bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE | DAEMON_CLOSE_EXTRA_FDS, NULL);
 
+       username[0] = '\0';
        opt = getopt32(argv, "f:h:p", &opt_user, &opt_host);
        if (opt & LOGIN_OPT_f) {
                if (!run_by_root)
@@ -243,9 +287,19 @@ int login_main(int argc UNUSED_PARAM, char **argv)
        if (argv[0]) /* user from command line (getty) */
                safe_strncpy(username, argv[0], sizeof(username));
 
-       /* Let's find out and memorize our tty */
-       if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO))
-               return EXIT_FAILURE;            /* Must be a terminal */
+       /* Save tty attributes - and by doing it, check that it's indeed a tty */
+       if (tcgetattr(STDIN_FILENO, &G.tty_attrs) < 0
+        || !isatty(STDOUT_FILENO)
+        /*|| !isatty(STDERR_FILENO) - no, guess some people might want to redirect this */
+       ) {
+               return EXIT_FAILURE;  /* Must be a terminal */
+       }
+
+       /* We install timeout handler only _after_ we saved G.tty_attrs */
+       signal(SIGALRM, alarm_handler);
+       alarm(TIMEOUT);
+
+       /* Find out and memorize our tty name */
        full_tty = xmalloc_ttyname(STDIN_FILENO);
        if (!full_tty)
                full_tty = xstrdup("UNKNOWN");
@@ -264,7 +318,7 @@ int login_main(int argc UNUSED_PARAM, char **argv)
 
        while (1) {
                /* flush away any type-ahead (as getty does) */
-               ioctl(0, TCFLSH, TCIFLUSH);
+               tcflush(0, TCIFLUSH);
 
                if (!username[0])
                        get_username_or_die(username, sizeof(username));
@@ -281,14 +335,24 @@ int login_main(int argc UNUSED_PARAM, char **argv)
                        failed_msg = "set_item(TTY)";
                        goto pam_auth_failed;
                }
-               pamret = pam_authenticate(pamh, 0);
-               if (pamret != PAM_SUCCESS) {
-                       failed_msg = "authenticate";
-                       goto pam_auth_failed;
-                       /* TODO: or just "goto auth_failed"
-                        * since user seems to enter wrong password
-                        * (in this case pamret == 7)
-                        */
+               /* set RHOST */
+               if (opt_host) {
+                       pamret = pam_set_item(pamh, PAM_RHOST, opt_host);
+                       if (pamret != PAM_SUCCESS) {
+                               failed_msg = "set_item(RHOST)";
+                               goto pam_auth_failed;
+                       }
+               }
+               if (!(opt & LOGIN_OPT_f)) {
+                       pamret = pam_authenticate(pamh, 0);
+                       if (pamret != PAM_SUCCESS) {
+                               failed_msg = "authenticate";
+                               goto pam_auth_failed;
+                               /* TODO: or just "goto auth_failed"
+                                * since user seems to enter wrong password
+                                * (in this case pamret == 7)
+                                */
+                       }
                }
                /* check that the account is healthy */
                pamret = pam_acct_mgmt(pamh, 0);
@@ -345,25 +409,32 @@ int login_main(int argc UNUSED_PARAM, char **argv)
                if (opt & LOGIN_OPT_f)
                        break; /* -f USER: success without asking passwd */
 
-               if (pw->pw_uid == 0 && !check_securetty())
+               if (pw->pw_uid == 0 && !check_securetty(short_tty))
                        goto auth_failed;
 
                /* Don't check the password if password entry is empty (!) */
                if (!pw->pw_passwd[0])
                        break;
  fake_it:
-               /* authorization takes place here */
-               if (correct_password(pw))
+               /* Password reading and authorization takes place here.
+                * Note that reads (in no-echo mode) trash tty attributes.
+                * If we get interrupted by SIGALRM, we need to restore attrs.
+                */
+               if (ask_and_check_password(pw) > 0)
                        break;
 #endif /* ENABLE_PAM */
  auth_failed:
                opt &= ~LOGIN_OPT_f;
-               bb_do_delay(FAIL_DELAY);
+               bb_do_delay(LOGIN_FAIL_DELAY);
                /* TODO: doesn't sound like correct English phrase to me */
                puts("Login incorrect");
                if (++count == 3) {
                        syslog(LOG_WARNING, "invalid password for '%s'%s",
                                                username, fromhost);
+
+                       if (ENABLE_FEATURE_CLEAN_UP)
+                               free(fromhost);
+
                        return EXIT_FAILURE;
                }
                username[0] = '\0';
@@ -375,7 +446,22 @@ int login_main(int argc UNUSED_PARAM, char **argv)
        if (pw->pw_uid != 0)
                die_if_nologin();
 
-       IF_SELINUX(initselinux(username, full_tty, &user_sid));
+#if ENABLE_LOGIN_SESSION_AS_CHILD
+       child_pid = vfork();
+       if (child_pid != 0) {
+               if (child_pid < 0)
+                       bb_perror_msg("vfork");
+               else {
+                       if (safe_waitpid(child_pid, NULL, 0) == -1)
+                               bb_perror_msg("waitpid");
+                       update_utmp(child_pid, DEAD_PROCESS, NULL, NULL, NULL);
+               }
+               IF_PAM(login_pam_end(pamh);)
+               return 0;
+       }
+#endif
+
+       IF_SELINUX(initselinux(username, full_tty, &user_sid);)
 
        /* Try these, but don't complain if they fail.
         * _f_chown is safe wrt race t=ttyname(0);...;chown(t); */
@@ -389,18 +475,28 @@ int login_main(int argc UNUSED_PARAM, char **argv)
                run_login_script(pw, full_tty);
 
        change_identity(pw);
-       shell = pw->pw_shell;
-       if (!shell || !shell[0])
-               shell = DEFAULT_SHELL;
-       setup_environment(shell,
+       setup_environment(pw->pw_shell,
                        (!(opt & LOGIN_OPT_p) * SETUP_ENV_CLEARENV) + SETUP_ENV_CHANGEENV,
                        pw);
 
+#if ENABLE_PAM
+       /* Modules such as pam_env will setup the PAM environment,
+        * which should be copied into the new environment. */
+       pamenv = pam_getenvlist(pamh);
+       if (pamenv) while (*pamenv) {
+               putenv(*pamenv);
+               pamenv++;
+       }
+#endif
+
        motd();
 
        if (pw->pw_uid == 0)
                syslog(LOG_INFO, "root login%s", fromhost);
 
+       if (ENABLE_FEATURE_CLEAN_UP)
+               free(fromhost);
+
        /* well, a simple setexeccon() here would do the job as well,
         * but let's play the game for now */
        IF_SELINUX(set_current_security_context(user_sid);)
@@ -427,7 +523,7 @@ int login_main(int argc UNUSED_PARAM, char **argv)
        signal(SIGINT, SIG_DFL);
 
        /* Exec login shell with no additional parameters */
-       run_shell(shell, 1, NULL, NULL);
+       run_shell(pw->pw_shell, 1, NULL, NULL);
 
        /* return EXIT_FAILURE; - not reached */
 }
index b447af2..1509089 100644 (file)
@@ -1,24 +1,31 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define passwd_trivial_usage
+//usage:       "[OPTIONS] [USER]"
+//usage:#define passwd_full_usage "\n\n"
+//usage:       "Change USER's password (default: current user)"
+//usage:     "\n"
+//usage:     "\n       -a ALG  Encryption method"
+//usage:     "\n       -d      Set password to ''"
+//usage:     "\n       -l      Lock (disable) account"
+//usage:     "\n       -u      Unlock (enable) account"
+
 #include "libbb.h"
 #include <syslog.h>
+#include <sys/resource.h> /* setrlimit */
 
-static void nuke_str(char *str)
-{
-       if (str) memset(str, 0, strlen(str));
-}
-
-static char* new_password(const struct passwd *pw, uid_t myuid, int algo)
+static char* new_password(const struct passwd *pw, uid_t myuid, const char *algo)
 {
-       char salt[sizeof("$N$XXXXXXXX")]; /* "$N$XXXXXXXX" or "XX" */
+       char salt[MAX_PW_SALT_LEN];
        char *orig = (char*)"";
        char *newp = NULL;
        char *cp = NULL;
        char *ret = NULL; /* failure so far */
 
-       if (myuid && pw->pw_passwd[0]) {
+       if (myuid != 0 && pw->pw_passwd[0]) {
                char *encrypted;
 
                orig = bb_ask_stdin("Old password: "); /* returns ptr to static */
@@ -26,13 +33,13 @@ static char* new_password(const struct passwd *pw, uid_t myuid, int algo)
                        goto err_ret;
                encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */
                if (strcmp(encrypted, pw->pw_passwd) != 0) {
-                       syslog(LOG_WARNING, "incorrect password for %s",
-                               pw->pw_name);
-                       bb_do_delay(FAIL_DELAY);
+                       syslog(LOG_WARNING, "incorrect password for %s", pw->pw_name);
+                       bb_do_delay(LOGIN_FAIL_DELAY);
                        puts("Incorrect password");
                        goto err_ret;
                }
-               if (ENABLE_FEATURE_CLEAN_UP) free(encrypted);
+               if (ENABLE_FEATURE_CLEAN_UP)
+                       free(encrypted);
        }
        orig = xstrdup(orig); /* or else bb_ask_stdin() will destroy it */
        newp = bb_ask_stdin("New password: "); /* returns ptr to static */
@@ -40,22 +47,22 @@ static char* new_password(const struct passwd *pw, uid_t myuid, int algo)
                goto err_ret;
        newp = xstrdup(newp); /* we are going to bb_ask_stdin() again, so save it */
        if (ENABLE_FEATURE_PASSWD_WEAK_CHECK
-        && obscure(orig, newp, pw) && myuid)
+        && obscure(orig, newp, pw)
+        && myuid != 0
+       ) {
                goto err_ret; /* non-root is not allowed to have weak passwd */
+       }
 
        cp = bb_ask_stdin("Retype password: ");
        if (!cp)
                goto err_ret;
-       if (strcmp(cp, newp)) {
+       if (strcmp(cp, newp) != 0) {
                puts("Passwords don't match");
                goto err_ret;
        }
 
-       crypt_make_salt(salt, 1, 0); /* des */
-       if (algo) { /* MD5 */
-               strcpy(salt, "$1$");
-               crypt_make_salt(salt + 3, 4, 0);
-       }
+       crypt_make_pw_salt(salt, algo);
+
        /* pw_encrypt returns malloced str */
        ret = pw_encrypt(newp, salt, 1);
        /* whee, success! */
@@ -63,8 +70,10 @@ static char* new_password(const struct passwd *pw, uid_t myuid, int algo)
  err_ret:
        nuke_str(orig);
        if (ENABLE_FEATURE_CLEAN_UP) free(orig);
+
        nuke_str(newp);
        if (ENABLE_FEATURE_CLEAN_UP) free(newp);
+
        nuke_str(cp);
        return ret;
 }
@@ -73,17 +82,15 @@ int passwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int passwd_main(int argc UNUSED_PARAM, char **argv)
 {
        enum {
-               OPT_algo = 0x1, /* -a - password algorithm */
-               OPT_lock = 0x2, /* -l - lock account */
-               OPT_unlock = 0x4, /* -u - unlock account */
-               OPT_delete = 0x8, /* -d - delete password */
-               OPT_lud = 0xe,
-               STATE_ALGO_md5 = 0x10,
-               //STATE_ALGO_des = 0x20, not needed yet
+               OPT_algo   = (1 << 0), /* -a - password algorithm */
+               OPT_lock   = (1 << 1), /* -l - lock account */
+               OPT_unlock = (1 << 2), /* -u - unlock account */
+               OPT_delete = (1 << 3), /* -d - delete password */
+               OPT_lud    = OPT_lock | OPT_unlock | OPT_delete,
        };
        unsigned opt;
        int rc;
-       const char *opt_a = "";
+       const char *opt_a = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO;
        const char *filename;
        char *myname;
        char *name;
@@ -104,13 +111,9 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
        //argc -= optind;
        argv += optind;
 
-       if (strcasecmp(opt_a, "des") != 0) /* -a */
-               opt |= STATE_ALGO_md5;
-       //else
-       //      opt |= STATE_ALGO_des;
        myuid = getuid();
        /* -l, -u, -d require root priv and username argument */
-       if ((opt & OPT_lud) && (myuid || !argv[0]))
+       if ((opt & OPT_lud) && (myuid != 0 || !argv[0]))
                bb_show_usage();
 
        /* Will complain and die if username not found */
@@ -118,7 +121,7 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
        name = argv[0] ? argv[0] : myname;
 
        pw = xgetpwnam(name);
-       if (myuid && pw->pw_uid != myuid) {
+       if (myuid != 0 && pw->pw_uid != myuid) {
                /* LOGMODE_BOTH */
                bb_error_msg_and_die("%s can't change password for %s", myname, name);
        }
@@ -152,27 +155,28 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
        newp = NULL;
        c = pw->pw_passwd[0] - '!';
        if (!(opt & OPT_lud)) {
-               if (myuid && !c) { /* passwd starts with '!' */
+               if (myuid != 0 && !c) { /* passwd starts with '!' */
                        /* LOGMODE_BOTH */
                        bb_error_msg_and_die("can't change "
                                        "locked password for %s", name);
                }
                printf("Changing password for %s\n", name);
-               newp = new_password(pw, myuid, opt & STATE_ALGO_md5);
+               newp = new_password(pw, myuid, opt_a);
                if (!newp) {
                        logmode = LOGMODE_STDIO;
                        bb_error_msg_and_die("password for %s is unchanged", name);
                }
        } else if (opt & OPT_lock) {
-               if (!c) goto skip; /* passwd starts with '!' */
+               if (!c)
+                       goto skip; /* passwd starts with '!' */
                newp = xasprintf("!%s", pw->pw_passwd);
        } else if (opt & OPT_unlock) {
-               if (c) goto skip; /* not '!' */
+               if (c)
+                       goto skip; /* not '!' */
                /* pw->pw_passwd points to static storage,
                 * strdup'ing to avoid nasty surprizes */
                newp = xstrdup(&pw->pw_passwd[1]);
        } else if (opt & OPT_delete) {
-               //newp = xstrdup("");
                newp = (char*)"";
        }
 
@@ -189,7 +193,11 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
 #if ENABLE_FEATURE_SHADOWPASSWDS
        filename = bb_path_shadow_file;
        rc = update_passwd(bb_path_shadow_file, name, newp, NULL);
-       if (rc == 0) /* no lines updated, no errors detected */
+       if (rc > 0)
+               /* password in /etc/shadow was updated */
+               newp = (char*) "x";
+       if (rc >= 0)
+               /* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */
 #endif
        {
                filename = bb_path_passwd_file;
@@ -197,16 +205,17 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
        }
        /* LOGMODE_BOTH */
        if (rc < 0)
-               bb_error_msg_and_die("can't update password file %s",
-                               filename);
+               bb_error_msg_and_die("can't update password file %s", filename);
        bb_info_msg("Password for %s changed by %s", name, myname);
 
-       //if (ENABLE_FEATURE_CLEAN_UP) free(newp);
+       /*if (ENABLE_FEATURE_CLEAN_UP) free(newp); - can't, it may be non-malloced */
  skip:
        if (!newp) {
                bb_error_msg_and_die("password for %s is already %slocked",
                        name, (opt & OPT_unlock) ? "un" : "");
        }
-       if (ENABLE_FEATURE_CLEAN_UP) free(myname);
+
+       if (ENABLE_FEATURE_CLEAN_UP)
+               free(myname);
        return 0;
 }
index 9bae375..c51f26f 100644 (file)
@@ -1,13 +1,22 @@
 /* vi: set sw=4 ts=4: */
 /*
- *  Mini su implementation for busybox
+ * Mini su implementation for busybox
  *
- *  Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 #include <syslog.h>
 
+//usage:#define su_trivial_usage
+//usage:       "[OPTIONS] [-] [USER]"
+//usage:#define su_full_usage "\n\n"
+//usage:       "Run shell under USER (by default, root)\n"
+//usage:     "\n       -,-l    Clear environment, run shell as login shell"
+//usage:     "\n       -p,-m   Do not set new $HOME, $SHELL, $USER, $LOGNAME"
+//usage:     "\n       -c CMD  Command to pass to 'sh -c'"
+//usage:     "\n       -s SH   Shell to use instead of user's default"
+
 #if ENABLE_FEATURE_SU_CHECKS_SHELLS
 /* Return 1 if SHELL is a restricted shell (one not returned by
  * getusershell), else 0, meaning it is a standard shell.  */
@@ -42,7 +51,9 @@ int su_main(int argc UNUSED_PARAM, char **argv)
        struct passwd *pw;
        uid_t cur_uid = getuid();
        const char *tty;
+#if ENABLE_FEATURE_UTMP
        char user_buf[64];
+#endif
        const char *old_user;
 
        flags = getopt32(argv, "mplc:s:", &opt_command, &opt_shell);
@@ -82,7 +93,7 @@ int su_main(int argc UNUSED_PARAM, char **argv)
 
        pw = xgetpwnam(opt_username);
 
-       if (cur_uid == 0 || correct_password(pw)) {
+       if (cur_uid == 0 || ask_and_check_password(pw) > 0) {
                if (ENABLE_FEATURE_SU_SYSLOG)
                        syslog(LOG_NOTICE, "%c %s %s:%s",
                                '+', tty, old_user, opt_username);
@@ -102,20 +113,14 @@ int su_main(int argc UNUSED_PARAM, char **argv)
                opt_shell = getenv("SHELL");
        }
 
-       /* Make sure pw->pw_shell is non-NULL.  It may be NULL when NEW_USER
-        * is a username that is retrieved via NIS (YP), that doesn't have
-        * a default shell listed.  */
-       if (!pw->pw_shell || !pw->pw_shell[0])
-               pw->pw_shell = (char *)DEFAULT_SHELL;
-
 #if ENABLE_FEATURE_SU_CHECKS_SHELLS
-       if (opt_shell && cur_uid != 0 && restricted_shell(pw->pw_shell)) {
+       if (opt_shell && cur_uid != 0 && pw->pw_shell && restricted_shell(pw->pw_shell)) {
                /* The user being su'd to has a nonstandard shell, and so is
                 * probably a uucp account or has restricted access.  Don't
                 * compromise the account by allowing access with a standard
                 * shell.  */
                bb_error_msg("using restricted shell");
-               opt_shell = NULL;
+               opt_shell = NULL; /* ignore -s PROG */
        }
        /* else: user can run whatever he wants via "su -s PROG USER".
         * This is safe since PROG is run under user's uid/gid. */
@@ -126,7 +131,8 @@ int su_main(int argc UNUSED_PARAM, char **argv)
        change_identity(pw);
        setup_environment(opt_shell,
                        ((flags & SU_OPT_l) / SU_OPT_l * SETUP_ENV_CLEARENV)
-                       + (!(flags & SU_OPT_mp) * SETUP_ENV_CHANGEENV),
+                       + (!(flags & SU_OPT_mp) * SETUP_ENV_CHANGEENV)
+                       + (!(flags & SU_OPT_l) * SETUP_ENV_NO_CHDIR),
                        pw);
        IF_SELINUX(set_current_security_context(NULL);)
 
index 3516013..2a29099 100644 (file)
@@ -2,30 +2,24 @@
 /*
  * Mini sulogin implementation for busybox
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define sulogin_trivial_usage
+//usage:       "[-t N] [TTY]"
+//usage:#define sulogin_full_usage "\n\n"
+//usage:       "Single user login\n"
+//usage:     "\n       -t N    Timeout"
+
 #include "libbb.h"
 #include <syslog.h>
 
-//static void catchalarm(int UNUSED_PARAM junk)
-//{
-//     exit(EXIT_FAILURE);
-//}
-
-
 int sulogin_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int sulogin_main(int argc UNUSED_PARAM, char **argv)
 {
-       char *cp;
        int timeout = 0;
        struct passwd *pwd;
        const char *shell;
-#if ENABLE_FEATURE_SHADOWPASSWDS
-       /* Using _r function to avoid pulling in static buffers */
-       char buffer[256];
-       struct spwd spw;
-#endif
 
        logmode = LOGMODE_BOTH;
        openlog(applet_name, 0, LOG_AUTH);
@@ -56,43 +50,24 @@ int sulogin_main(int argc UNUSED_PARAM, char **argv)
                goto auth_error;
        }
 
-#if ENABLE_FEATURE_SHADOWPASSWDS
-       {
-               /* getspnam_r may return 0 yet set result to NULL.
-                * At least glibc 2.4 does this. Be extra paranoid here. */
-               struct spwd *result = NULL;
-               int r = getspnam_r(pwd->pw_name, &spw, buffer, sizeof(buffer), &result);
-               if (r || !result) {
-                       goto auth_error;
-               }
-               pwd->pw_passwd = result->sp_pwdp;
-       }
-#endif
-
        while (1) {
-               char *encrypted;
                int r;
 
-               /* cp points to a static buffer that is zeroed every time */
-               cp = bb_ask(STDIN_FILENO, timeout,
-                               "Give root password for system maintenance\n"
-                               "(or type Control-D for normal startup):");
-
-               if (!cp || !*cp) {
+               r = ask_and_check_password_extended(pwd, timeout,
+                       "Give root password for system maintenance\n"
+                       "(or type Control-D for normal startup):"
+               );
+               if (r < 0) {
+                       /* ^D, ^C, timeout, or read error */
                        bb_info_msg("Normal startup");
                        return 0;
                }
-               encrypted = pw_encrypt(cp, pwd->pw_passwd, 1);
-               r = strcmp(encrypted, pwd->pw_passwd);
-               free(encrypted);
-               if (r == 0) {
+               if (r > 0) {
                        break;
                }
-               bb_do_delay(FAIL_DELAY);
-               bb_error_msg("login incorrect");
+               bb_do_delay(LOGIN_FAIL_DELAY);
+               bb_info_msg("Login incorrect");
        }
-       memset(cp, 0, strlen(cp));
-//     signal(SIGALRM, SIG_DFL);
 
        bb_info_msg("System Maintenance Mode");
 
index 85f489c..44b14e6 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) 2000 by spoon <spoon@ix.netcom.com>
  * Written by spoon <spon@ix.netcom.com>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* Shoutz to Michael K. Johnson <johnsonm@redhat.com>, author of the
 /* Fixed by Erik Andersen to do passwords the tinylogin way...
  * It now works with md5, sha1, etc passwords. */
 
-#include <sys/vt.h>
+//usage:#define vlock_trivial_usage
+//usage:       "[-a]"
+//usage:#define vlock_full_usage "\n\n"
+//usage:       "Lock a virtual terminal. A password is required to unlock.\n"
+//usage:     "\n       -a      Lock all VTs"
+
 #include "libbb.h"
 
+#ifdef __linux__
+#include <sys/vt.h>
+
 static void release_vt(int signo UNUSED_PARAM)
 {
        /* If -a, param is 0, which means:
@@ -30,14 +38,17 @@ static void acquire_vt(int signo UNUSED_PARAM)
        /* ACK to kernel that switch to console is successful */
        ioctl(STDIN_FILENO, VT_RELDISP, VT_ACKACQ);
 }
+#endif
 
 int vlock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int vlock_main(int argc UNUSED_PARAM, char **argv)
 {
+#ifdef __linux__
        struct vt_mode vtm;
+       struct vt_mode ovtm;
+#endif
        struct termios term;
        struct termios oterm;
-       struct vt_mode ovtm;
        struct passwd *pw;
 
        pw = xgetpwuid(getuid());
@@ -55,6 +66,7 @@ int vlock_main(int argc UNUSED_PARAM, char **argv)
                + (1 << SIGINT )
                , SIG_IGN);
 
+#ifdef __linux__
        /* We will use SIGUSRx for console switch control: */
        /* 1: set handlers */
        signal_SA_RESTART_empty_mask(SIGUSR1, release_vt);
@@ -62,12 +74,14 @@ int vlock_main(int argc UNUSED_PARAM, char **argv)
        /* 2: unmask them */
        sig_unblock(SIGUSR1);
        sig_unblock(SIGUSR2);
+#endif
 
        /* Revert stdin/out to our controlling tty
         * (or die if we have none) */
        xmove_fd(xopen(CURRENT_TTY, O_RDWR), STDIN_FILENO);
        xdup2(STDIN_FILENO, STDOUT_FILENO);
 
+#ifdef __linux__
        xioctl(STDIN_FILENO, VT_GETMODE, &vtm);
        ovtm = vtm;
        /* "console switches are controlled by us, not kernel!" */
@@ -75,6 +89,7 @@ int vlock_main(int argc UNUSED_PARAM, char **argv)
        vtm.relsig = SIGUSR1;
        vtm.acqsig = SIGUSR2;
        ioctl(STDIN_FILENO, VT_SETMODE, &vtm);
+#endif
 
        tcgetattr(STDIN_FILENO, &oterm);
        term = oterm;
@@ -84,18 +99,21 @@ int vlock_main(int argc UNUSED_PARAM, char **argv)
        term.c_lflag &= ~(ECHO | ECHOCTL);
        tcsetattr_stdin_TCSANOW(&term);
 
-       do {
+       while (1) {
                printf("Virtual console%s locked by %s.\n",
-                               option_mask32 /*o_lock_all*/ ? "s" : "",
-                               pw->pw_name);
-               if (correct_password(pw)) {
+                               /* "s" if -a, else "": */ "s" + !option_mask32,
+                               pw->pw_name
+               );
+               if (ask_and_check_password(pw) > 0) {
                        break;
                }
-               bb_do_delay(FAIL_DELAY);
-               puts("Password incorrect");
-       } while (1);
+               bb_do_delay(LOGIN_FAIL_DELAY);
+               puts("Incorrect password");
+       }
 
+#ifdef __linux__
        ioctl(STDIN_FILENO, VT_SETMODE, &ovtm);
+#endif
        tcsetattr_stdin_TCSANOW(&oterm);
        fflush_stdout_and_exit(EXIT_SUCCESS);
 }
index b2fb735..6b4fb74 100644 (file)
@@ -2,12 +2,8 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
 INSERT
-lib-$(CONFIG_MAKEMIME)     += mime.o mail.o
-lib-$(CONFIG_POPMAILDIR)   += popmaildir.o mail.o
-lib-$(CONFIG_REFORMIME)    += mime.o mail.o
-lib-$(CONFIG_SENDMAIL)     += sendmail.o mail.o
index bcd3583..199f644 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 #include "mail.h"
@@ -57,10 +57,13 @@ void FAST_FUNC launch_helper(const char **argv)
        G.helper_pid = xvfork();
 
        i = (!G.helper_pid) * 2; // for parent:0, for child:2
-       close(pipes[i + 1]); // 1 or 3 - closing one write end
-       close(pipes[2 - i]); // 2 or 0 - closing one read end
-       xmove_fd(pipes[i], STDIN_FILENO); // 0 or 2 - using other read end
-       xmove_fd(pipes[3 - i], STDOUT_FILENO); // 3 or 1 - other write end
+       close(pipes[i + 1]);     // 1 or 3 - closing one write end
+       close(pipes[2 - i]);     // 2 or 0 - closing one read end
+       xmove_fd(pipes[i], STDIN_FILENO);      // 0 or 2 - using other read end
+       xmove_fd(pipes[3 - i], STDOUT_FILENO); // 3 or 1 - using other write end
+       // End result:
+       // parent stdout [3] -> child stdin [2]
+       // child stdout [1] -> parent stdin [0]
 
        if (!G.helper_pid) {
                // child: try to execute connection helper
@@ -75,13 +78,16 @@ void FAST_FUNC launch_helper(const char **argv)
        atexit(kill_helper);
 }
 
-const FAST_FUNC char *command(const char *fmt, const char *param)
+char* FAST_FUNC send_mail_command(const char *fmt, const char *param)
 {
-       const char *msg = fmt;
+       char *msg;
        if (timeout)
                alarm(timeout);
-       if (msg) {
+       msg = (char*)fmt;
+       if (fmt) {
                msg = xasprintf(fmt, param);
+               if (verbose)
+                       bb_error_msg("send:'%s'", msg);
                printf("%s\r\n", msg);
        }
        fflush_all();
@@ -90,7 +96,7 @@ const FAST_FUNC char *command(const char *fmt, const char *param)
 
 // NB: parse_url can modify url[] (despite const), but only if '@' is there
 /*
-static char FAST_FUNC *parse_url(char *url, char **user, char **pass)
+static char* FAST_FUNC parse_url(char *url, char **user, char **pass)
 {
        // parse [user[:pass]@]host
        // return host
@@ -113,19 +119,18 @@ static char FAST_FUNC *parse_url(char *url, char **user, char **pass)
 void FAST_FUNC encode_base64(char *fname, const char *text, const char *eol)
 {
        enum {
-               SRC_BUF_SIZE = 45,  /* This *MUST* be a multiple of 3 */
+               SRC_BUF_SIZE = 57,  /* This *MUST* be a multiple of 3 */
                DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3),
        };
-
 #define src_buf text
+       char src[SRC_BUF_SIZE];
        FILE *fp = fp;
        ssize_t len = len;
        char dst_buf[DST_BUF_SIZE + 1];
 
        if (fname) {
                fp = (NOT_LONE_DASH(fname)) ? xfopen_for_read(fname) : (FILE *)text;
-               src_buf = bb_common_bufsiz1;
-       // N.B. strlen(NULL) segfaults!
+               src_buf = src;
        } else if (text) {
                // though we do not call uuencode(NULL, NULL) explicitly
                // still we do not want to break things suddenly
@@ -161,73 +166,6 @@ void FAST_FUNC encode_base64(char *fname, const char *text, const char *eol)
 #undef src_buf
 }
 
-void FAST_FUNC decode_base64(FILE *src_stream, FILE *dst_stream)
-{
-       int term_count = 1;
-
-       while (1) {
-               char translated[4];
-               int count = 0;
-
-               while (count < 4) {
-                       char *table_ptr;
-                       int ch;
-
-                       /* Get next _valid_ character.
-                        * global vector bb_uuenc_tbl_base64[] contains this string:
-                        * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n"
-                        */
-                       do {
-                               ch = fgetc(src_stream);
-                               if (ch == EOF) {
-                                       bb_error_msg_and_die(bb_msg_read_error);
-                               }
-                               // - means end of MIME section
-                               if ('-' == ch) {
-                                       // push it back
-                                       ungetc(ch, src_stream);
-                                       return;
-                               }
-                               table_ptr = strchr(bb_uuenc_tbl_base64, ch);
-                       } while (table_ptr == NULL);
-
-                       /* Convert encoded character to decimal */
-                       ch = table_ptr - bb_uuenc_tbl_base64;
-
-                       if (*table_ptr == '=') {
-                               if (term_count == 0) {
-                                       translated[count] = '\0';
-                                       break;
-                               }
-                               term_count++;
-                       } else if (*table_ptr == '\n') {
-                               /* Check for terminating line */
-                               if (term_count == 5) {
-                                       return;
-                               }
-                               term_count = 1;
-                               continue;
-                       } else {
-                               translated[count] = ch;
-                               count++;
-                               term_count = 0;
-                       }
-               }
-
-               /* Merge 6 bit chars to 8 bit */
-               if (count > 1) {
-                       fputc(translated[0] << 2 | translated[1] >> 4, dst_stream);
-               }
-               if (count > 2) {
-                       fputc(translated[1] << 4 | translated[2] >> 2, dst_stream);
-               }
-               if (count > 3) {
-                       fputc(translated[2] << 6 | translated[3], dst_stream);
-               }
-       }
-}
-
-
 /*
  * get username and password from a file descriptor
  */
@@ -237,8 +175,8 @@ void FAST_FUNC get_cred_or_die(int fd)
                G.user = xstrdup(bb_ask(fd, /* timeout: */ 0, "User: "));
                G.pass = xstrdup(bb_ask(fd, /* timeout: */ 0, "Password: "));
        } else {
-               G.user = xmalloc_reads(fd, /* pfx: */ NULL, /* maxsize: */ NULL);
-               G.pass = xmalloc_reads(fd, /* pfx: */ NULL, /* maxsize: */ NULL);
+               G.user = xmalloc_reads(fd, /* maxsize: */ NULL);
+               G.pass = xmalloc_reads(fd, /* maxsize: */ NULL);
        }
        if (!G.user || !*G.user || !G.pass)
                bb_error_msg_and_die("no username or password");
index bb747c4..fa0c5b3 100644 (file)
@@ -1,35 +1,37 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * helper routines
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
 
 struct globals {
        pid_t helper_pid;
        unsigned timeout;
+       unsigned verbose;
        unsigned opts;
        char *user;
        char *pass;
        FILE *fp0; // initial stdin
        char *opt_charset;
-       char *content_type;
 };
 
 #define G (*ptr_to_globals)
 #define timeout         (G.timeout  )
+#define verbose         (G.verbose  )
 #define opts            (G.opts     )
-//#define user            (G.user     )
-//#define pass            (G.pass     )
-//#define fp0             (G.fp0      )
-//#define opt_charset     (G.opt_charset)
-//#define content_type    (G.content_type)
 #define INIT_G() do { \
        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
        G.opt_charset = (char *)CONFIG_FEATURE_MIME_CHARSET; \
-       G.content_type = (char *)"text/plain"; \
 } while (0)
 
 //char FAST_FUNC *parse_url(char *url, char **user, char **pass);
 
-void FAST_FUNC launch_helper(const char **argv);
-void FAST_FUNC get_cred_or_die(int fd);
+void launch_helper(const char **argv) FAST_FUNC;
+void get_cred_or_die(int fd) FAST_FUNC;
 
-const FAST_FUNC char *command(const char *fmt, const char *param);
+char *send_mail_command(const char *fmt, const char *param) FAST_FUNC;
 
-void FAST_FUNC encode_base64(char *fname, const char *text, const char *eol);
-void FAST_FUNC decode_base64(FILE *src_stream, FILE *dst_stream);
+void encode_base64(char *fname, const char *text, const char *eol) FAST_FUNC;
diff --git a/mailutils/makemime.c b/mailutils/makemime.c
new file mode 100644 (file)
index 0000000..1dadd71
--- /dev/null
@@ -0,0 +1,239 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * makemime: create MIME-encoded message
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//kbuild:lib-$(CONFIG_MAKEMIME) += makemime.o mail.o
+
+#include "libbb.h"
+#include "mail.h"
+
+#if 0
+# define dbg_error_msg(...) bb_error_msg(__VA_ARGS__)
+#else
+# define dbg_error_msg(...) ((void)0)
+#endif
+
+/*
+  makemime -c type [-o file] [-e encoding] [-C charset] [-N name] \
+                   [-a "Header: Contents"] file
+           -m [ type ] [-o file] [-e encoding] [-a "Header: Contents"] file
+           -j [-o file] file1 file2
+           @file
+
+   file:  filename    - read or write from filename
+          -           - read or write from stdin or stdout
+          &n          - read or write from file descriptor n
+          \( opts \)  - read from child process, that generates [ opts ]
+
+Options:
+  -c type         - create a new MIME section from "file" with this
+                    Content-Type: (default is application/octet-stream).
+  -C charset      - MIME charset of a new text/plain section.
+  -N name         - MIME content name of the new mime section.
+  -m [ type ]     - create a multipart mime section from "file" of this
+                    Content-Type: (default is multipart/mixed).
+  -e encoding     - use the given encoding (7bit, 8bit, quoted-printable,
+                    or base64), instead of guessing.  Omit "-e" and use
+                    -c auto to set Content-Type: to text/plain or
+                    application/octet-stream based on picked encoding.
+  -j file1 file2  - join mime section file2 to multipart section file1.
+  -o file         - write the result to file, instead of stdout (not
+                    allowed in child processes).
+  -a header       - prepend an additional header to the output.
+
+  @file - read all of the above options from file, one option or
+          value on each line.
+  {which version of makemime is this? What do we support?}
+*/
+/* man makemime:
+
+ * -c TYPE: create a (non-multipart) MIME section with Content-Type: TYPE
+ * makemime -c TYPE [-e ENCODING] [-o OUTFILE] [-C CHARSET] [-N NAME] [-a HEADER...] FILE
+ * The -C option sets the MIME charset attribute for text/plain content.
+ * The -N option sets the name attribute for Content-Type:
+ * Encoding must be one of the following: 7bit, 8bit, quoted-printable, or base64.
+
+ * -m multipart/TYPE: create a multipart MIME collection with Content-Type: multipart/TYPE
+ * makemime -m multipart/TYPE [-e ENCODING] [-o OUTFILE] [-a HEADER...] FILE
+ * Type must be either "multipart/mixed", "multipart/alternative", or some other MIME multipart content type.
+ * Additionally, encoding can only be "7bit" or "8bit", and will default to "8bit" if not specified.
+ * Finally, filename must be a MIME-formatted section, NOT a regular file.
+ * The -m option creates an initial multipart MIME collection, that contains only one MIME section, taken from filename.
+ * The collection is written to standard output, or the pipe or to outputfile.
+
+ * -j FILE1: add a section to a multipart MIME collection
+ * makemime -j FILE1 [-o OUTFILE] FILE2
+ * FILE1 must be a MIME collection that was previously created by the -m option.
+ * FILE2 must be a MIME section that was previously created by the -c option.
+ * The -j options adds the MIME section in FILE2 to the MIME collection in FILE1.
+ */
+
+
+/* In busybox 1.15.0.svn, makemime generates output like this
+ * (empty lines are shown exactly!):
+{headers added with -a HDR}
+Mime-Version: 1.0
+Content-Type: multipart/mixed; boundary="24269534-2145583448-1655890676"
+
+--24269534-2145583448-1655890676
+Content-Type: {set by -c, e.g. text/plain}; charset={set by -C, e.g. us-ascii}
+Content-Disposition: inline; filename="A"
+Content-Transfer-Encoding: base64
+
+...file A contents...
+--24269534-2145583448-1655890676
+Content-Type: {set by -c, e.g. text/plain}; charset={set by -C, e.g. us-ascii}
+Content-Disposition: inline; filename="B"
+Content-Transfer-Encoding: base64
+
+...file B contents...
+--24269534-2145583448-1655890676--
+
+ *
+ * For reference: here is an example email to LKML which has
+ * 1st unnamed part (so it serves as an email body)
+ * and one attached file:
+...other headers...
+Content-Type: multipart/mixed; boundary="=-tOfTf3byOS0vZgxEWcX+"
+...other headers...
+Mime-Version: 1.0
+...other headers...
+
+
+--=-tOfTf3byOS0vZgxEWcX+
+Content-Type: text/plain
+Content-Transfer-Encoding: 7bit
+
+...email text...
+...email text...
+
+
+--=-tOfTf3byOS0vZgxEWcX+
+Content-Disposition: attachment; filename="xyz"
+Content-Type: text/plain; name="xyz"; charset="UTF-8"
+Content-Transfer-Encoding: 7bit
+
+...file contents...
+...file contents...
+
+--=-tOfTf3byOS0vZgxEWcX+--
+
+...random junk added by mailing list robots and such...
+*/
+
+//usage:#define makemime_trivial_usage
+//usage:       "[OPTIONS] [FILE]..."
+//usage:#define makemime_full_usage "\n\n"
+//usage:       "Create multipart MIME-encoded message from FILEs\n"
+/* //usage:    "Transfer encoding is base64, disposition is inline (not attachment)\n" */
+//usage:     "\n       -o FILE Output. Default: stdout"
+//usage:     "\n       -a HDR  Add header(s). Examples:"
+//usage:     "\n               \"From: user@host.org\", \"Date: `date -R`\""
+//usage:     "\n       -c CT   Content type. Default: application/octet-stream"
+//usage:     "\n       -C CS   Charset. Default: " CONFIG_FEATURE_MIME_CHARSET
+/* //usage:  "\n       -e ENC  Transfer encoding. Ignored. base64 is assumed" */
+//usage:     "\n"
+//usage:     "\nOther options are silently ignored"
+
+/*
+ * -c [Content-Type] should create just one MIME section
+ * with "Content-Type:", "Content-Transfer-Encoding:", and HDRs from "-a HDR".
+ * NB: without "Content-Disposition:" auto-added, unlike we do now
+ * NB2: -c has *optional* param which nevertheless _can_ be specified after a space :(
+ *
+ * -m [multipart/mixed] should create multipart MIME section
+ * with "Content-Type:", "Content-Transfer-Encoding:", and HDRs from "-a HDR",
+ * and add FILE to it _verbatim_:
+ *  HEADERS
+ *
+ *  --=_1_1321709112_1605
+ *  FILE_CONTENTS
+ *  --=_1_1321709112_1605
+ * without any encoding of FILE_CONTENTS. (Basically, it expects that FILE
+ * is the result of "makemime -c").
+ *
+ * -j MULTIPART_FILE1 SINGLE_FILE2 should output MULTIPART_FILE1 + SINGLE_FILE2
+ *
+ * Our current behavior is a mutant "-m + -c + -j" one: we create multipart MIME
+ * and we put "-c" encoded FILEs into many multipart sections.
+ */
+
+int makemime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int makemime_main(int argc UNUSED_PARAM, char **argv)
+{
+       llist_t *opt_headers = NULL, *l;
+       const char *opt_output;
+       const char *content_type = "application/octet-stream";
+#define boundary opt_output
+       enum {
+               OPT_c = 1 << 0,         // create (non-multipart) section
+               OPT_e = 1 << 1,         // Content-Transfer-Encoding. Ignored. Assumed base64
+               OPT_o = 1 << 2,         // output to
+               OPT_C = 1 << 3,         // charset
+               OPT_N = 1 << 4,         // COMPAT
+               OPT_a = 1 << 5,         // additional headers
+               //OPT_m = 1 << 6,         // create mutipart section
+               //OPT_j = 1 << 7,         // join section to multipart section
+       };
+
+       INIT_G();
+
+       // parse options
+       opt_complementary = "a::";
+       opts = getopt32(argv,
+               "c:e:o:C:N:a:", // "m:j:",
+               &content_type, NULL, &opt_output, &G.opt_charset, NULL, &opt_headers //, NULL, NULL
+       );
+       //argc -= optind;
+       argv += optind;
+
+       // respect -o output
+       if (opts & OPT_o)
+               freopen(opt_output, "w", stdout);
+
+       // no files given on command line? -> use stdin
+       if (!*argv)
+               *--argv = (char *)"-";
+
+       // put additional headers
+       for (l = opt_headers; l; l = l->link)
+               puts(l->data);
+
+       // make a random string -- it will delimit message parts
+       srand(monotonic_us());
+       boundary = xasprintf("%u-%u-%u",
+                       (unsigned)rand(), (unsigned)rand(), (unsigned)rand());
+
+       // put multipart header
+       printf(
+               "Mime-Version: 1.0\n"
+               "Content-Type: multipart/mixed; boundary=\"%s\"\n"
+               , boundary
+       );
+
+       // put attachments
+       while (*argv) {
+               printf(
+                       "\n--%s\n"
+                       "Content-Type: %s; charset=%s\n"
+                       "Content-Disposition: inline; filename=\"%s\"\n"
+                       "Content-Transfer-Encoding: base64\n"
+                       , boundary
+                       , content_type
+                       , G.opt_charset
+                       , bb_get_last_path_component_strip(*argv)
+               );
+               encode_base64(*argv++, (const char *)stdin, "");
+       }
+
+       // put multipart footer
+       printf("\n--%s--\n" "\n", boundary);
+
+       return EXIT_SUCCESS;
+#undef boundary
+}
diff --git a/mailutils/mime.c b/mailutils/mime.c
deleted file mode 100644 (file)
index 5eb8ef6..0000000
+++ /dev/null
@@ -1,416 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * makemime: create MIME-encoded message
- * reformime: parse MIME-encoded message
- *
- * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
- *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
- */
-#include "libbb.h"
-#include "mail.h"
-
-/*
-  makemime -c type [-o file] [-e encoding] [-C charset] [-N name] \
-                   [-a "Header: Contents"] file
-           -m [ type ] [-o file] [-e encoding] [-a "Header: Contents"] file
-           -j [-o file] file1 file2
-           @file
-
-   file:  filename    - read or write from filename
-          -           - read or write from stdin or stdout
-          &n          - read or write from file descriptor n
-          \( opts \)  - read from child process, that generates [ opts ]
-
-Options:
-
-  -c type         - create a new MIME section from "file" with this
-                    Content-Type: (default is application/octet-stream).
-  -C charset      - MIME charset of a new text/plain section.
-  -N name         - MIME content name of the new mime section.
-  -m [ type ]     - create a multipart mime section from "file" of this
-                    Content-Type: (default is multipart/mixed).
-  -e encoding     - use the given encoding (7bit, 8bit, quoted-printable,
-                    or base64), instead of guessing.  Omit "-e" and use
-                    -c auto to set Content-Type: to text/plain or
-                    application/octet-stream based on picked encoding.
-  -j file1 file2  - join mime section file2 to multipart section file1.
-  -o file         - write the result to file, instead of stdout (not
-                    allowed in child processes).
-  -a header       - prepend an additional header to the output.
-
-  @file - read all of the above options from file, one option or
-          value on each line.
-  {which version of makemime is this? What do we support?}
-*/
-
-
-/* In busybox 1.15.0.svn, makemime generates output like this
- * (empty lines are shown exactly!):
-{headers added with -a HDR}
-Mime-Version: 1.0
-Content-Type: multipart/mixed; boundary="24269534-2145583448-1655890676"
-
---24269534-2145583448-1655890676
-Content-Type: {set by -c, e.g. text/plain}; charset={set by -C, e.g. us-ascii}
-Content-Disposition: inline; filename="A"
-Content-Transfer-Encoding: base64
-
-...file A contents...
---24269534-2145583448-1655890676
-Content-Type: {set by -c, e.g. text/plain}; charset={set by -C, e.g. us-ascii}
-Content-Disposition: inline; filename="B"
-Content-Transfer-Encoding: base64
-
-...file B contents...
---24269534-2145583448-1655890676--
-
-*/
-
-
-/* For reference: here is an example email to LKML which has
- * 1st unnamed part (so it serves as an email body)
- * and one attached file:
-...other headers...
-Content-Type: multipart/mixed; boundary="=-tOfTf3byOS0vZgxEWcX+"
-...other headers...
-Mime-Version: 1.0
-...other headers...
-
-
---=-tOfTf3byOS0vZgxEWcX+
-Content-Type: text/plain
-Content-Transfer-Encoding: 7bit
-
-...email text...
-...email text...
-
-
---=-tOfTf3byOS0vZgxEWcX+
-Content-Disposition: attachment; filename="xyz"
-Content-Type: text/plain; name="xyz"; charset="UTF-8"
-Content-Transfer-Encoding: 7bit
-
-...file contents...
-...file contents...
-
---=-tOfTf3byOS0vZgxEWcX+--
-
-...random junk added by mailing list robots and such...
-*/
-
-int makemime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int makemime_main(int argc UNUSED_PARAM, char **argv)
-{
-       llist_t *opt_headers = NULL, *l;
-       const char *opt_output;
-#define boundary opt_output
-
-       enum {
-               OPT_c = 1 << 0,         // Content-Type:
-               OPT_e = 1 << 1,         // Content-Transfer-Encoding. Ignored. Assumed base64
-               OPT_o = 1 << 2,         // output to
-               OPT_C = 1 << 3,         // charset
-               OPT_N = 1 << 4,         // COMPAT
-               OPT_a = 1 << 5,         // additional headers
-               OPT_m = 1 << 6,         // COMPAT
-               OPT_j = 1 << 7,         // COMPAT
-       };
-
-       INIT_G();
-
-       // parse options
-       opt_complementary = "a::";
-       opts = getopt32(argv,
-               "c:e:o:C:N:a:m:j:",
-               &G.content_type, NULL, &opt_output, &G.opt_charset, NULL, &opt_headers, NULL, NULL
-       );
-       //argc -= optind;
-       argv += optind;
-
-       // respect -o output
-       if (opts & OPT_o)
-               freopen(opt_output, "w", stdout);
-
-       // no files given on command line? -> use stdin
-       if (!*argv)
-               *--argv = (char *)"-";
-
-       // put additional headers
-       for (l = opt_headers; l; l = l->link)
-               puts(l->data);
-
-       // make a random string -- it will delimit message parts
-       srand(monotonic_us());
-       boundary = xasprintf("%u-%u-%u",
-                       (unsigned)rand(), (unsigned)rand(), (unsigned)rand());
-
-       // put multipart header
-       printf(
-               "Mime-Version: 1.0\n"
-               "Content-Type: multipart/mixed; boundary=\"%s\"\n"
-               , boundary
-       );
-
-       // put attachments
-       while (*argv) {
-               printf(
-                       "\n--%s\n"
-                       "Content-Type: %s; charset=%s\n"
-                       "Content-Disposition: inline; filename=\"%s\"\n"
-                       "Content-Transfer-Encoding: base64\n"
-                       , boundary
-                       , G.content_type
-                       , G.opt_charset
-                       , bb_get_last_path_component_strip(*argv)
-               );
-               encode_base64(*argv++, (const char *)stdin, "");
-       }
-
-       // put multipart footer
-       printf("\n--%s--\n" "\n", boundary);
-
-       return EXIT_SUCCESS;
-#undef boundary
-}
-
-static const char *find_token(const char *const string_array[], const char *key, const char *defvalue)
-{
-       const char *r = NULL;
-       int i;
-       for (i = 0; string_array[i] != NULL; i++) {
-               if (strcasecmp(string_array[i], key) == 0) {
-                       r = (char *)string_array[i+1];
-                       break;
-               }
-       }
-       return (r) ? r : defvalue;
-}
-
-static const char *xfind_token(const char *const string_array[], const char *key)
-{
-       const char *r = find_token(string_array, key, NULL);
-       if (r)
-               return r;
-       bb_error_msg_and_die("header: %s", key);
-}
-
-enum {
-       OPT_x = 1 << 0,
-       OPT_X = 1 << 1,
-#if ENABLE_FEATURE_REFORMIME_COMPAT
-       OPT_d = 1 << 2,
-       OPT_e = 1 << 3,
-       OPT_i = 1 << 4,
-       OPT_s = 1 << 5,
-       OPT_r = 1 << 6,
-       OPT_c = 1 << 7,
-       OPT_m = 1 << 8,
-       OPT_h = 1 << 9,
-       OPT_o = 1 << 10,
-       OPT_O = 1 << 11,
-#endif
-};
-
-static int parse(const char *boundary, char **argv)
-{
-       char *line, *s, *p;
-       const char *type;
-       int boundary_len = strlen(boundary);
-       const char *delims = " ;\"\t\r\n";
-       const char *uniq;
-       int ntokens;
-       const char *tokens[32]; // 32 is enough
-
-       // prepare unique string pattern
-       uniq = xasprintf("%%llu.%u.%s", (unsigned)getpid(), safe_gethostname());
-
-//bb_info_msg("PARSE[%s]", terminator);
-
-       while ((line = xmalloc_fgets_str(stdin, "\r\n\r\n")) != NULL) {
-
-               // seek to start of MIME section
-               // N.B. to avoid false positives let us seek to the _last_ occurance
-               p = NULL;
-               s = line;
-               while ((s = strcasestr(s, "Content-Type:")) != NULL)
-                       p = s++;
-               if (!p)
-                       goto next;
-//bb_info_msg("L[%s]", p);
-
-               // split to tokens
-               // TODO: strip of comments which are of form: (comment-text)
-               ntokens = 0;
-               tokens[ntokens] = NULL;
-               for (s = strtok(p, delims); s; s = strtok(NULL, delims)) {
-                       tokens[ntokens] = s;
-                       if (ntokens < ARRAY_SIZE(tokens) - 1)
-                               ntokens++;
-//bb_info_msg("L[%d][%s]", ntokens, s);
-               }
-               tokens[ntokens] = NULL;
-//bb_info_msg("N[%d]", ntokens);
-
-               // analyse tokens
-               type = find_token(tokens, "Content-Type:", "text/plain");
-//bb_info_msg("T[%s]", type);
-               if (0 == strncasecmp(type, "multipart/", 10)) {
-                       if (0 == strcasecmp(type+10, "mixed")) {
-                               parse(xfind_token(tokens, "boundary="), argv);
-                       } else
-                               bb_error_msg_and_die("no support of content type '%s'", type);
-               } else {
-                       pid_t pid = pid;
-                       int rc;
-                       FILE *fp;
-                       // fetch charset
-                       const char *charset = find_token(tokens, "charset=", CONFIG_FEATURE_MIME_CHARSET);
-                       // fetch encoding
-                       const char *encoding = find_token(tokens, "Content-Transfer-Encoding:", "7bit");
-                       // compose target filename
-                       char *filename = (char *)find_token(tokens, "filename=", NULL);
-                       if (!filename)
-                               filename = xasprintf(uniq, monotonic_us());
-                       else
-                               filename = bb_get_last_path_component_strip(xstrdup(filename));
-
-                       // start external helper, if any
-                       if (opts & OPT_X) {
-                               int fd[2];
-                               xpipe(fd);
-                               pid = vfork();
-                               if (0 == pid) {
-                                       // child reads from fd[0]
-                                       close(fd[1]);
-                                       xmove_fd(fd[0], STDIN_FILENO);
-                                       xsetenv("CONTENT_TYPE", type);
-                                       xsetenv("CHARSET", charset);
-                                       xsetenv("ENCODING", encoding);
-                                       xsetenv("FILENAME", filename);
-                                       BB_EXECVP_or_die(argv);
-                               }
-                               // parent dumps to fd[1]
-                               close(fd[0]);
-                               fp = xfdopen_for_write(fd[1]);
-                               signal(SIGPIPE, SIG_IGN); // ignore EPIPE
-                       // or create a file for dump
-                       } else {
-                               char *fname = xasprintf("%s%s", *argv, filename);
-                               fp = xfopen_for_write(fname);
-                               free(fname);
-                       }
-
-                       // housekeeping
-                       free(filename);
-
-                       // dump to fp
-                       if (0 == strcasecmp(encoding, "base64")) {
-                               decode_base64(stdin, fp);
-                       } else if (0 != strcasecmp(encoding, "7bit")
-                               && 0 != strcasecmp(encoding, "8bit")
-                       ) {
-                               // quoted-printable, binary, user-defined are unsupported so far
-                               bb_error_msg_and_die("no support of encoding '%s'", encoding);
-                       } else {
-                               // N.B. we have written redundant \n. so truncate the file
-                               // The following weird 2-tacts reading technique is due to
-                               // we have to not write extra \n at the end of the file
-                               // In case of -x option we could truncate the resulting file as
-                               // fseek(fp, -1, SEEK_END);
-                               // if (ftruncate(fileno(fp), ftell(fp)))
-                               //      bb_perror_msg("ftruncate");
-                               // But in case of -X we have to be much more careful. There is
-                               // no means to truncate what we already have sent to the helper.
-                               p = xmalloc_fgets_str(stdin, "\r\n");
-                               while (p) {
-                                       s = xmalloc_fgets_str(stdin, "\r\n");
-                                       if (s == NULL)
-                                               break;
-                                       if ('-' == s[0]
-                                        && '-' == s[1]
-                                        && 0 == strncmp(s+2, boundary, boundary_len)
-                                       ) {
-                                               break;
-                                       }
-                                       fputs(p, fp);
-                                       p = s;
-                               }
-
-/*
-                               while ((s = xmalloc_fgetline_str(stdin, "\r\n")) != NULL) {
-                                       if ('-' == s[0] && '-' == s[1]
-                                               && 0 == strncmp(s+2, boundary, boundary_len))
-                                               break;
-                                       fprintf(fp, "%s\n", s);
-                               }
-                               // N.B. we have written redundant \n. so truncate the file
-                               fseek(fp, -1, SEEK_END);
-                               if (ftruncate(fileno(fp), ftell(fp)))
-                                       bb_perror_msg("ftruncate");
-*/
-                       }
-                       fclose(fp);
-
-                       // finalize helper
-                       if (opts & OPT_X) {
-                               signal(SIGPIPE, SIG_DFL);
-                               // exit if helper exited >0
-                               rc = (wait4pid(pid) & 0xff);
-                               if (rc)
-                                       return rc+20;
-                       }
-
-                       // check multipart finalized
-                       if (s && '-' == s[2+boundary_len] && '-' == s[2+boundary_len+1]) {
-                               free(line);
-                               break;
-                       }
-               }
- next:
-               free(line);
-       }
-
-//bb_info_msg("ENDPARSE[%s]", boundary);
-
-       return EXIT_SUCCESS;
-}
-
-/*
-Usage: reformime [options]
-    -d - parse a delivery status notification.
-    -e - extract contents of MIME section.
-    -x - extract MIME section to a file.
-    -X - pipe MIME section to a program.
-    -i - show MIME info.
-    -s n.n.n.n - specify MIME section.
-    -r - rewrite message, filling in missing MIME headers.
-    -r7 - also convert 8bit/raw encoding to quoted-printable, if possible.
-    -r8 - also convert quoted-printable encoding to 8bit, if possible.
-    -c charset - default charset for rewriting, -o, and -O.
-    -m [file] [file]... - create a MIME message digest.
-    -h "header" - decode RFC 2047-encoded header.
-    -o "header" - encode unstructured header using RFC 2047.
-    -O "header" - encode address list header using RFC 2047.
-*/
-
-int reformime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int reformime_main(int argc UNUSED_PARAM, char **argv)
-{
-       const char *opt_prefix = "";
-
-       INIT_G();
-
-       // parse options
-       // N.B. only -x and -X are supported so far
-       opt_complementary = "x--X:X--x" IF_FEATURE_REFORMIME_COMPAT(":m::");
-       opts = getopt32(argv,
-               "x:X" IF_FEATURE_REFORMIME_COMPAT("deis:r:c:m:h:o:O:"),
-               &opt_prefix
-               IF_FEATURE_REFORMIME_COMPAT(, NULL, NULL, &G.opt_charset, NULL, NULL, NULL, NULL)
-       );
-       //argc -= optind;
-       argv += optind;
-
-       return parse("", (opts & OPT_X) ? argv : (char **)&opt_prefix);
-}
index ab9ddba..6203033 100644 (file)
@@ -7,16 +7,52 @@
  *
  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//kbuild:lib-$(CONFIG_POPMAILDIR) += popmaildir.o mail.o
+
+//usage:#define popmaildir_trivial_usage
+//usage:       "[OPTIONS] MAILDIR [CONN_HELPER ARGS]"
+//usage:#define popmaildir_full_usage "\n\n"
+//usage:       "Fetch content of remote mailbox to local maildir\n"
+/* //usage:  "\n       -b              Binary mode. Ignored" */
+/* //usage:  "\n       -d              Debug. Ignored" */
+/* //usage:  "\n       -m              Show used memory. Ignored" */
+/* //usage:  "\n       -V              Show version. Ignored" */
+/* //usage:  "\n       -c              Use tcpclient. Ignored" */
+/* //usage:  "\n       -a              Use APOP protocol. Implied. If server supports APOP -> use it" */
+//usage:     "\n       -s              Skip authorization"
+//usage:     "\n       -T              Get messages with TOP instead of RETR"
+//usage:     "\n       -k              Keep retrieved messages on the server"
+//usage:     "\n       -t SEC          Network timeout"
+//usage:       IF_FEATURE_POPMAILDIR_DELIVERY(
+//usage:     "\n       -F \"PROG ARGS\"        Filter program (may be repeated)"
+//usage:     "\n       -M \"PROG ARGS\"        Delivery program"
+//usage:       )
+//usage:     "\n"
+//usage:     "\nFetch from plain POP3 server:"
+//usage:     "\npopmaildir -k DIR nc pop3.server.com 110 <user_and_pass.txt"
+//usage:     "\nFetch from SSLed POP3 server and delete fetched emails:"
+//usage:     "\npopmaildir DIR -- openssl s_client -quiet -connect pop3.server.com:995 <user_and_pass.txt"
+/* //usage:  "\n       -R BYTES        Remove old messages on the server >= BYTES. Ignored" */
+/* //usage:  "\n       -Z N1-N2        Remove messages from N1 to N2 (dangerous). Ignored" */
+/* //usage:  "\n       -L BYTES        Don't retrieve new messages >= BYTES. Ignored" */
+/* //usage:  "\n       -H LINES        Type first LINES of a message. Ignored" */
+//usage:
+//usage:#define popmaildir_example_usage
+//usage:       "$ popmaildir -k ~/Maildir -- nc pop.drvv.ru 110 [<password_file]\n"
+//usage:       "$ popmaildir ~/Maildir -- openssl s_client -quiet -connect pop.gmail.com:995 [<password_file]\n"
+
 #include "libbb.h"
 #include "mail.h"
 
 static void pop3_checkr(const char *fmt, const char *param, char **ret)
 {
-       const char *msg = command(fmt, param);
+       char *msg = send_mail_command(fmt, param);
        char *answer = xmalloc_fgetline(stdin);
        if (answer && '+' == answer[0]) {
+               free(msg);
                if (timeout)
                        alarm(0);
                if (ret) {
@@ -27,7 +63,7 @@ static void pop3_checkr(const char *fmt, const char *param, char **ret)
                        free(answer);
                return;
        }
-       bb_error_msg_and_die("%s failed: %s", msg, answer);
+       bb_error_msg_and_die("%s failed, reply was: %s", msg, answer);
 }
 
 static void pop3_check(const char *fmt, const char *param)
@@ -109,9 +145,9 @@ int popmaildir_main(int argc UNUSED_PARAM, char **argv)
                                s[1] = '\0';
                        // get md5 sum of "<stamp>password" string
                        md5_begin(&md5.ctx);
-                       md5_hash(buf, strlen(buf), &md5.ctx);
-                       md5_hash(G.pass, strlen(G.pass), &md5.ctx);
-                       md5_end(res, &md5.ctx);
+                       md5_hash(&md5.ctx, buf, strlen(buf));
+                       md5_hash(&md5.ctx, G.pass, strlen(G.pass));
+                       md5_end(&md5.ctx, res);
                        *bin2hex(md5.hex, (char*)res, 16) = '\0';
                        // APOP
                        s = xasprintf("%s %s", G.user, md5.hex);
diff --git a/mailutils/reformime.c b/mailutils/reformime.c
new file mode 100644 (file)
index 0000000..8e7d455
--- /dev/null
@@ -0,0 +1,278 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * reformime: parse MIME-encoded message
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//kbuild:lib-$(CONFIG_REFORMIME) += reformime.o mail.o
+
+#include "libbb.h"
+#include "mail.h"
+
+#if 0
+# define dbg_error_msg(...) bb_error_msg(__VA_ARGS__)
+#else
+# define dbg_error_msg(...) ((void)0)
+#endif
+
+static const char *find_token(const char *const string_array[], const char *key, const char *defvalue)
+{
+       const char *r = NULL;
+       int i;
+       for (i = 0; string_array[i] != NULL; i++) {
+               if (strcasecmp(string_array[i], key) == 0) {
+                       r = (char *)string_array[i+1];
+                       break;
+               }
+       }
+       return (r) ? r : defvalue;
+}
+
+static const char *xfind_token(const char *const string_array[], const char *key)
+{
+       const char *r = find_token(string_array, key, NULL);
+       if (r)
+               return r;
+       bb_error_msg_and_die("not found: '%s'", key);
+}
+
+enum {
+       OPT_x = 1 << 0,
+       OPT_X = 1 << 1,
+#if ENABLE_FEATURE_REFORMIME_COMPAT
+       OPT_d = 1 << 2,
+       OPT_e = 1 << 3,
+       OPT_i = 1 << 4,
+       OPT_s = 1 << 5,
+       OPT_r = 1 << 6,
+       OPT_c = 1 << 7,
+       OPT_m = 1 << 8,
+       OPT_h = 1 << 9,
+       OPT_o = 1 << 10,
+       OPT_O = 1 << 11,
+#endif
+};
+
+static int parse(const char *boundary, char **argv)
+{
+       int boundary_len = strlen(boundary);
+       char uniq[sizeof("%%llu.%u") + sizeof(int)*3];
+
+       dbg_error_msg("BOUNDARY[%s]", boundary);
+
+       // prepare unique string pattern
+       sprintf(uniq, "%%llu.%u", (unsigned)getpid());
+       dbg_error_msg("UNIQ[%s]", uniq);
+
+       while (1) {
+               char *header;
+               const char *tokens[32]; /* 32 is enough */
+               const char *type;
+
+               /* Read the header (everything up to two \n) */
+               {
+                       unsigned header_idx = 0;
+                       int last_ch = 0;
+                       header = NULL;
+                       while (1) {
+                               int ch = fgetc(stdin);
+                               if (ch == '\r') /* Support both line endings */
+                                       continue;
+                               if (ch == EOF)
+                                       break;
+                               if (ch == '\n' && last_ch == ch)
+                                       break;
+                               if (!(header_idx & 0xff))
+                                       header = xrealloc(header, header_idx + 0x101);
+                               header[header_idx++] = last_ch = ch;
+                       }
+                       if (!header) {
+                               dbg_error_msg("EOF");
+                               break;
+                       }
+                       header[header_idx] = '\0';
+                       dbg_error_msg("H:'%s'", p);
+               }
+
+               /* Split to tokens */
+               {
+                       char *s, *p;
+                       unsigned ntokens;
+                       const char *delims = ";=\" \t\n";
+
+                       /* Skip to last Content-Type: */
+                       s = p = header;
+                       while ((p = strchr(p, '\n')) != NULL) {
+                               p++;
+                               if (strncasecmp(p, "Content-Type:", sizeof("Content-Type:")-1) == 0)
+                                       s = p;
+                       }
+                       dbg_error_msg("L:'%s'", p);
+                       ntokens = 0;
+                       s = strtok(s, delims);
+                       while (s) {
+                               tokens[ntokens] = s;
+                               if (ntokens < ARRAY_SIZE(tokens) - 1)
+                                       ntokens++;
+                               dbg_error_msg("L[%d]='%s'", ntokens, s);
+                               s = strtok(NULL, delims);
+                       }
+                       tokens[ntokens] = NULL;
+                       dbg_error_msg("EMPTYLINE, ntokens:%d", ntokens);
+                       if (ntokens == 0)
+                               break;
+               }
+
+               /* Is it multipart? */
+               type = find_token(tokens, "Content-Type:", "text/plain");
+               dbg_error_msg("TYPE:'%s'", type);
+               if (0 == strncasecmp(type, "multipart/", 10)) {
+                       /* Yes, recurse */
+                       if (strcasecmp(type + 10, "mixed") != 0)
+                               bb_error_msg_and_die("no support of content type '%s'", type);
+                       parse(xfind_token(tokens, "boundary"), argv);
+
+               } else {
+                       /* No, process one non-multipart section */
+                       char *end;
+                       pid_t pid = pid;
+                       FILE *fp;
+
+                       const char *charset = find_token(tokens, "charset", CONFIG_FEATURE_MIME_CHARSET);
+                       const char *encoding = find_token(tokens, "Content-Transfer-Encoding:", "7bit");
+
+                       /* Compose target filename */
+                       char *filename = (char *)find_token(tokens, "filename", NULL);
+                       if (!filename)
+                               filename = xasprintf(uniq, monotonic_us());
+                       else
+                               filename = bb_get_last_path_component_strip(xstrdup(filename));
+
+                       if (opts & OPT_X) {
+                               int fd[2];
+
+                               /* start external helper */
+                               xpipe(fd);
+                               pid = vfork();
+                               if (0 == pid) {
+                                       /* child reads from fd[0] */
+                                       close(fd[1]);
+                                       xmove_fd(fd[0], STDIN_FILENO);
+                                       xsetenv("CONTENT_TYPE", type);
+                                       xsetenv("CHARSET", charset);
+                                       xsetenv("ENCODING", encoding);
+                                       xsetenv("FILENAME", filename);
+                                       BB_EXECVP_or_die(argv);
+                               }
+                               /* parent will write to fd[1] */
+                               close(fd[0]);
+                               fp = xfdopen_for_write(fd[1]);
+                               signal(SIGPIPE, SIG_IGN);
+                       } else {
+                               /* write to file */
+                               char *fname = xasprintf("%s%s", *argv, filename);
+                               fp = xfopen_for_write(fname);
+                               free(fname);
+                       }
+                       free(filename);
+
+                       /* write to fp */
+                       end = NULL;
+                       if (0 == strcasecmp(encoding, "base64")) {
+                               read_base64(stdin, fp, '-');
+                       } else
+                       if (0 != strcasecmp(encoding, "7bit")
+                        && 0 != strcasecmp(encoding, "8bit")
+                       ) {
+                               /* quoted-printable, binary, user-defined are unsupported so far */
+                               bb_error_msg_and_die("encoding '%s' not supported", encoding);
+                       } else {
+                               /* plain 7bit or 8bit */
+                               while ((end = xmalloc_fgets(stdin)) != NULL) {
+                                       if ('-' == end[0]
+                                        && '-' == end[1]
+                                        && strncmp(end + 2, boundary, boundary_len) == 0
+                                       ) {
+                                               break;
+                                       }
+                                       fputs(end, fp);
+                               }
+                       }
+                       fclose(fp);
+
+                       /* Wait for child */
+                       if (opts & OPT_X) {
+                               int rc;
+                               signal(SIGPIPE, SIG_DFL);
+                               rc = (wait4pid(pid) & 0xff);
+                               if (rc != 0)
+                                       return rc + 20;
+                       }
+
+                       /* Multipart ended? */
+                       if (end && '-' == end[2 + boundary_len] && '-' == end[2 + boundary_len + 1]) {
+                               dbg_error_msg("FINISHED MPART:'%s'", end);
+                               break;
+                       }
+                       dbg_error_msg("FINISHED:'%s'", end);
+                       free(end);
+               } /* end of "handle one non-multipart block" */
+
+               free(header);
+       } /* while (1) */
+
+       dbg_error_msg("ENDPARSE[%s]", boundary);
+
+       return EXIT_SUCCESS;
+}
+
+//usage:#define reformime_trivial_usage
+//usage:       "[OPTIONS]"
+//usage:#define reformime_full_usage "\n\n"
+//usage:       "Parse MIME-encoded message on stdin\n"
+//usage:     "\n       -x PREFIX       Extract content of MIME sections to files"
+//usage:     "\n       -X PROG ARGS    Filter content of MIME sections through PROG"
+//usage:     "\n                       Must be the last option"
+//usage:     "\n"
+//usage:     "\nOther options are silently ignored"
+
+/*
+Usage: reformime [options]
+    -d - parse a delivery status notification.
+    -e - extract contents of MIME section.
+    -x - extract MIME section to a file.
+    -X - pipe MIME section to a program.
+    -i - show MIME info.
+    -s n.n.n.n - specify MIME section.
+    -r - rewrite message, filling in missing MIME headers.
+    -r7 - also convert 8bit/raw encoding to quoted-printable, if possible.
+    -r8 - also convert quoted-printable encoding to 8bit, if possible.
+    -c charset - default charset for rewriting, -o, and -O.
+    -m [file] [file]... - create a MIME message digest.
+    -h "header" - decode RFC 2047-encoded header.
+    -o "header" - encode unstructured header using RFC 2047.
+    -O "header" - encode address list header using RFC 2047.
+*/
+
+int reformime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int reformime_main(int argc UNUSED_PARAM, char **argv)
+{
+       const char *opt_prefix = "";
+
+       INIT_G();
+
+       // parse options
+       // N.B. only -x and -X are supported so far
+       opt_complementary = "x--X:X--x" IF_FEATURE_REFORMIME_COMPAT(":m::");
+       opts = getopt32(argv,
+               "x:X" IF_FEATURE_REFORMIME_COMPAT("deis:r:c:m:h:o:O:"),
+               &opt_prefix
+               IF_FEATURE_REFORMIME_COMPAT(, NULL, NULL, &G.opt_charset, NULL, NULL, NULL, NULL)
+       );
+       argv += optind;
+
+       return parse("", (opts & OPT_X) ? argv : (char **)&opt_prefix);
+}
index 4b58a78..b5aa1d1 100644 (file)
@@ -4,8 +4,42 @@
  *
  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//kbuild:lib-$(CONFIG_SENDMAIL) += sendmail.o mail.o
+
+//usage:#define sendmail_trivial_usage
+//usage:       "[OPTIONS] [RECIPIENT_EMAIL]..."
+//usage:#define sendmail_full_usage "\n\n"
+//usage:       "Read email from stdin and send it\n"
+//usage:     "\nStandard options:"
+//usage:     "\n       -t              Read additional recipients from message body"
+//usage:     "\n       -f SENDER       Sender (required)"
+//usage:     "\n       -o OPTIONS      Various options. -oi implied, others are ignored"
+//usage:     "\n       -i              -oi synonym. implied and ignored"
+//usage:     "\n"
+//usage:     "\nBusybox specific options:"
+//usage:     "\n       -v              Verbose"
+//usage:     "\n       -w SECS         Network timeout"
+//usage:     "\n       -H 'PROG ARGS'  Run connection helper"
+//usage:     "\n                       Examples:"
+//usage:     "\n                       -H 'exec openssl s_client -quiet -tls1 -starttls smtp"
+//usage:     "\n                               -connect smtp.gmail.com:25' <email.txt"
+//usage:     "\n                               [4<username_and_passwd.txt | -auUSER -apPASS]"
+//usage:     "\n                       -H 'exec openssl s_client -quiet -tls1"
+//usage:     "\n                               -connect smtp.gmail.com:465' <email.txt"
+//usage:     "\n                               [4<username_and_passwd.txt | -auUSER -apPASS]"
+//usage:     "\n       -S HOST[:PORT]  Server"
+//usage:     "\n       -auUSER         Username for AUTH LOGIN"
+//usage:     "\n       -apPASS         Password for AUTH LOGIN"
+////usage:     "\n     -amMETHOD       Authentication method. Ignored. LOGIN is implied"
+//usage:     "\n"
+//usage:     "\nOther options are silently ignored; -oi -t is implied"
+//usage:       IF_MAKEMIME(
+//usage:     "\nUse makemime to create emails with attachments"
+//usage:       )
+
 #include "libbb.h"
 #include "mail.h"
 
 // set to 0 to not limit
 #define MAX_HEADERS 256
 
+static void send_r_n(const char *s)
+{
+       if (verbose)
+               bb_error_msg("send:'%s'", s);
+       printf("%s\r\n", s);
+}
+
 static int smtp_checkp(const char *fmt, const char *param, int code)
 {
        char *answer;
-       const char *msg = command(fmt, param);
+       char *msg = send_mail_command(fmt, param);
        // read stdin
-       // if the string has a form \d\d\d- -- read next string. E.g. EHLO response
+       // if the string has a form NNN- -- read next string. E.g. EHLO response
        // parse first bytes to a number
        // if code = -1 then just return this number
        // if code != -1 then checks whether the number equals the code
        // if not equal -> die saying msg
-       while ((answer = xmalloc_fgetline(stdin)) != NULL)
+       while ((answer = xmalloc_fgetline(stdin)) != NULL) {
+               if (verbose)
+                       bb_error_msg("recv:'%.*s'", (int)(strchrnul(answer, '\r') - answer), answer);
                if (strlen(answer) <= 3 || '-' != answer[3])
                        break;
+               free(answer);
+       }
        if (answer) {
                int n = atoi(answer);
                if (timeout)
                        alarm(0);
                free(answer);
-               if (-1 == code || n == code)
+               if (-1 == code || n == code) {
+                       free(msg);
                        return n;
+               }
        }
        bb_error_msg_and_die("%s failed", msg);
 }
@@ -45,25 +92,73 @@ static int smtp_check(const char *fmt, int code)
 // strip argument of bad chars
 static char *sane_address(char *str)
 {
-       char *s = str;
-       char *p = s;
+       char *s;
+
+       trim(str);
+       s = str;
        while (*s) {
-               if (isalnum(*s) || '_' == *s || '-' == *s || '.' == *s || '@' == *s) {
-                       *p++ = *s;
+               if (!isalnum(*s) && !strchr("_-.@", *s)) {
+                       bb_error_msg("bad address '%s'", str);
+                       /* returning "": */
+                       str[0] = '\0';
+                       return str;
                }
                s++;
        }
-       *p = '\0';
        return str;
 }
 
+// check for an address inside angle brackets, if not found fall back to normal
+static char *angle_address(char *str)
+{
+       char *s, *e;
+
+       trim(str);
+       e = last_char_is(str, '>');
+       if (e) {
+               s = strrchr(str, '<');
+               if (s) {
+                       *e = '\0';
+                       str = s + 1;
+               }
+       }
+       return sane_address(str);
+}
+
 static void rcptto(const char *s)
 {
+       if (!*s)
+               return;
        // N.B. we don't die if recipient is rejected, for the other recipients may be accepted
        if (250 != smtp_checkp("RCPT TO:<%s>", s, -1))
                bb_error_msg("Bad recipient: <%s>", s);
 }
 
+// send to a list of comma separated addresses
+static void rcptto_list(const char *list)
+{
+       char *str = xstrdup(list);
+       char *s = str;
+       char prev = 0;
+       int in_quote = 0;
+
+       while (*s) {
+               char ch = *s++;
+
+               if (ch == '"' && prev != '\\') {
+                       in_quote = !in_quote;
+               } else if (!in_quote && ch == ',') {
+                       s[-1] = '\0';
+                       rcptto(angle_address(str));
+                       str = s;
+               }
+               prev = ch;
+       }
+       if (prev != ',')
+               rcptto(angle_address(str));
+       free(str);
+}
+
 int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int sendmail_main(int argc UNUSED_PARAM, char **argv)
 {
@@ -71,9 +166,16 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
        char *opt_from;
        char *s;
        llist_t *list = NULL;
-       char *domain = sane_address(safe_getdomainname());
+       char *host = sane_address(safe_gethostname());
        unsigned nheaders = 0;
        int code;
+       enum {
+               HDR_OTHER = 0,
+               HDR_TOCC,
+               HDR_BCC,
+       } last_hdr = 0;
+       int check_hdr;
+       int has_to = 0;
 
        enum {
        //--- standard options
@@ -86,6 +188,7 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
                OPT_H = 1 << 5,         // use external connection helper
                OPT_S = 1 << 6,         // specify connection string
                OPT_a = 1 << 7,         // authentication tokens
+               OPT_v = 1 << 8,         // verbosity
        };
 
        // init global variables
@@ -96,12 +199,13 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
        G.fp0 = xfdopen_for_read(3);
 
        // parse options
-       // -f is required. -H and -S are mutually exclusive
-       opt_complementary = "f:w+:H--S:S--H:a::";
+       // -v is a counter, -f is required. -H and -S are mutually exclusive, -a is a list
+       opt_complementary = "vv:f:w+:H--S:S--H:a::";
        // N.B. since -H and -S are mutually exclusive they do not interfere in opt_connect
        // -a is for ssmtp (http://downloads.openwrt.org/people/nico/man/man8/ssmtp.8.html) compatibility,
        // it is still under development.
-       opts = getopt32(argv, "tf:o:iw:H:S:a::", &opt_from, NULL, &timeout, &opt_connect, &opt_connect, &list);
+       opts = getopt32(argv, "tf:o:iw:H:S:a::v", &opt_from, NULL,
+                       &timeout, &opt_connect, &opt_connect, &list, &verbose);
        //argc -= optind;
        argv += optind;
 
@@ -128,11 +232,35 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
                const char *args[] = { "sh", "-c", opt_connect, NULL };
                // plug it in
                launch_helper(args);
-       // vanilla connection
+               // Now:
+               // our stdout will go to helper's stdin,
+               // helper's stdout will be available on our stdin.
+
+               // Wait for initial server message.
+               // If helper (such as openssl) invokes STARTTLS, the initial 220
+               // is swallowed by helper (and not repeated after TLS is initiated).
+               // We will send NOOP cmd to server and check the response.
+               // We should get 220+250 on plain connection, 250 on STARTTLSed session.
+               //
+               // The problem here is some servers delay initial 220 message,
+               // and consider client to be a spammer if it starts sending cmds
+               // before 220 reached it. The code below is unsafe in this regard:
+               // in non-STARTTLSed case, we potentially send NOOP before 220
+               // is sent by server.
+               // Ideas? (--delay SECS opt? --assume-starttls-helper opt?)
+               code = smtp_check("NOOP", -1);
+               if (code == 220)
+                       // we got 220 - this is not STARTTLSed connection,
+                       // eat 250 response to our NOOP
+                       smtp_check(NULL, 250);
+               else
+               if (code != 250)
+                       bb_error_msg_and_die("SMTP init failed");
        } else {
+               // vanilla connection
                int fd;
                // host[:port] not explicitly specified? -> use $SMTPHOST
-               // no $SMTPHOST ? -> use localhost
+               // no $SMTPHOST? -> use localhost
                if (!(opts & OPT_S)) {
                        opt_connect = getenv("SMTPHOST");
                        if (!opt_connect)
@@ -143,25 +271,15 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
                // and make ourselves a simple IO filter
                xmove_fd(fd, STDIN_FILENO);
                xdup2(STDIN_FILENO, STDOUT_FILENO);
+
+               // Wait for initial server 220 message
+               smtp_check(NULL, 220);
        }
-       // N.B. from now we know nothing about network :)
-
-       // wait for initial server OK
-       // N.B. if we used openssl the initial 220 answer is already swallowed during openssl TLS init procedure
-       // so we need to kick the server to see whether we are ok
-       code = smtp_check("NOOP", -1);
-       // 220 on plain connection, 250 on openssl-helped TLS session
-       if (220 == code)
-               smtp_check(NULL, 250); // reread the code to stay in sync
-       else if (250 != code)
-               bb_error_msg_and_die("INIT failed");
 
        // we should start with modern EHLO
-       if (250 != smtp_checkp("EHLO %s", domain, -1)) {
-               smtp_checkp("HELO %s", domain, 250);
-       }
-       if (ENABLE_FEATURE_CLEAN_UP)
-               free(domain);
+       if (250 != smtp_checkp("EHLO %s", host, -1))
+               smtp_checkp("HELO %s", host, 250);
+       free(host);
 
        // perform authentication
        if (opts & OPT_a) {
@@ -176,7 +294,7 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
        }
 
        // set sender
-       // N.B. we have here a very loosely defined algotythm
+       // N.B. we have here a very loosely defined algorythm
        // since sendmail historically offers no means to specify secrets on cmdline.
        // 1) server can require no authentication ->
        //      we must just provide a (possibly fake) reply address.
@@ -193,8 +311,6 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
        //      G.user = xuid2uname(getuid());
        //      opt_from = xasprintf("%s@%s", G.user, domain);
        //}
-       //if (ENABLE_FEATURE_CLEAN_UP)
-       //      free(domain);
        smtp_checkp("MAIL FROM:<%s>", opt_from, 250);
 
        // process message
@@ -214,44 +330,74 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
                        if ('.' == s[0] /*&& '\0' == s[1] */)
                                printf(".");
                        // dump read line
-                       printf("%s\r\n", s);
+                       send_r_n(s);
                        free(s);
                        continue;
                }
 
                // analyze headers
                // To: or Cc: headers add recipients
-               if (0 == strncasecmp("To: ", s, 4) || 0 == strncasecmp("Bcc: " + 1, s, 4)) {
-                       rcptto(sane_address(s+4));
-                       goto addheader;
-               // Bcc: header adds blind copy (hidden) recipient
-               } else if (0 == strncasecmp("Bcc: ", s, 5)) {
-                       rcptto(sane_address(s+5));
-                       free(s);
-                       // N.B. Bcc: vanishes from headers!
-
-               // other headers go verbatim
-
-               // N.B. RFC2822 2.2.3 "Long Header Fields" allows for headers to occupy several lines.
-               // Continuation is denoted by prefixing additional lines with whitespace(s).
-               // Thanks (stefan.seyfried at googlemail.com) for pointing this out.
-               } else if (strchr(s, ':') || (list && skip_whitespace(s) != s)) {
+               check_hdr = (0 == strncasecmp("To:", s, 3));
+               has_to |= check_hdr;
+               if (opts & OPT_t) {
+                       if (check_hdr || 0 == strncasecmp("Bcc:" + 1, s, 3)) {
+                               rcptto_list(s+3);
+                               last_hdr = HDR_TOCC;
+                               goto addheader;
+                       }
+                       // Bcc: header adds blind copy (hidden) recipient
+                       if (0 == strncasecmp("Bcc:", s, 4)) {
+                               rcptto_list(s+4);
+                               free(s);
+                               last_hdr = HDR_BCC;
+                               continue; // N.B. Bcc: vanishes from headers!
+                       }
+               }
+               check_hdr = (list && isspace(s[0]));
+               if (strchr(s, ':') || check_hdr) {
+                       // other headers go verbatim
+                       // N.B. RFC2822 2.2.3 "Long Header Fields" allows for headers to occupy several lines.
+                       // Continuation is denoted by prefixing additional lines with whitespace(s).
+                       // Thanks (stefan.seyfried at googlemail.com) for pointing this out.
+                       if (check_hdr && last_hdr != HDR_OTHER) {
+                               rcptto_list(s+1);
+                               if (last_hdr == HDR_BCC)
+                                       continue;
+                                       // N.B. Bcc: vanishes from headers!
+                       } else {
+                               last_hdr = HDR_OTHER;
+                       }
  addheader:
                        // N.B. we allow MAX_HEADERS generic headers at most to prevent attacks
                        if (MAX_HEADERS && ++nheaders >= MAX_HEADERS)
                                goto bail;
                        llist_add_to_end(&list, s);
-               // a line without ":" (an empty line too, by definition) doesn't look like a valid header
-               // so stop "analyze headers" mode
                } else {
+                       // a line without ":" (an empty line too, by definition) doesn't look like a valid header
+                       // so stop "analyze headers" mode
  reenter:
                        // put recipients specified on cmdline
+                       check_hdr = 1;
                        while (*argv) {
                                char *t = sane_address(*argv);
                                rcptto(t);
                                //if (MAX_HEADERS && ++nheaders >= MAX_HEADERS)
                                //      goto bail;
-                               llist_add_to_end(&list, xasprintf("To: %s", t));
+                               if (!has_to) {
+                                       const char *hdr;
+
+                                       if (check_hdr && argv[1])
+                                               hdr = "To: %s,";
+                                       else if (check_hdr)
+                                               hdr = "To: %s";
+                                       else if (argv[1])
+                                               hdr = "To: %s," + 3;
+                                       else
+                                               hdr = "To: %s" + 3;
+                                       llist_add_to_end(&list,
+                                                       xasprintf(hdr, t));
+                                       check_hdr = 0;
+                               }
                                argv++;
                        }
                        // enter "put message" mode
@@ -261,14 +407,14 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
                                goto bail;
                        // dump the headers
                        while (list) {
-                               printf("%s\r\n", (char *) llist_pop(&list));
+                               send_r_n((char *) llist_pop(&list));
                        }
                        // stop analyzing headers
                        code++;
                        // N.B. !s means: we read nothing, and nothing to be read in the future.
                        // just dump empty line and break the loop
                        if (!s) {
-                               puts("\r");
+                               send_r_n("");
                                break;
                        }
                        // go dump message body
index 2f7c502..1da9800 100644 (file)
@@ -10,6 +10,7 @@ INSERT
 config ADJTIMEX
        bool "adjtimex"
        default y
+       select PLATFORM_LINUX
        help
          Adjtimex reads and optionally sets adjustment parameters for
          the Linux clock adjustment algorithm.
@@ -21,9 +22,24 @@ config BBCONFIG
          The bbconfig applet will print the config file with which
          busybox was built.
 
+config FEATURE_COMPRESS_BBCONFIG
+       bool "Compress bbconfig data"
+       default y
+       depends on BBCONFIG
+       help
+         Store bbconfig data in compressed form, uncompress them on-the-fly
+         before output.
+
+         If you have a really tiny busybox with few applets enabled (and
+         bunzip2 isn't one of them), the overhead of the decompressor might
+         be noticeable. Also, if you run executables directly from ROM
+         and have very little memory, this might not be a win. Otherwise,
+         you probably want this.
+
 config BEEP
        bool "beep"
        default y
+       select PLATFORM_LINUX
        help
          The beep applets beeps in a given freq/Hz.
 
@@ -120,7 +136,6 @@ config CHRT
 config CROND
        bool "crond"
        default y
-       select FEATURE_SUID
        select FEATURE_SYSLOG
        help
          Crond is a background daemon that parses individual crontab
@@ -155,7 +170,6 @@ config FEATURE_CROND_DIR
 config CRONTAB
        bool "crontab"
        default y
-       select FEATURE_SUID
        help
          Crontab manipulates the crontab for a particular user. Only
          the superuser may specify a different user and/or crontab directory.
@@ -180,6 +194,7 @@ config FEATURE_DC_LIBM
 config DEVFSD
        bool "devfsd (obsolete)"
        default n
+       select PLATFORM_LINUX
        select FEATURE_SYSLOG
        help
          This is deprecated and should NOT be used anymore.
@@ -223,6 +238,7 @@ config DEVFSD_VERBOSE
 config FEATURE_DEVFS
        bool "Use devfs names for all devices (obsolete)"
        default n
+       select PLATFORM_LINUX
        help
          This is obsolete and should NOT be used anymore.
          Use linux >= 2.6 (optionally with hotplug) and mdev instead!
@@ -242,6 +258,7 @@ config DEVMEM
 config EJECT
        bool "eject"
        default y
+       select PLATFORM_LINUX
        help
          Used to eject cdroms. (defaults to /dev/cdrom)
 
@@ -256,6 +273,7 @@ config FEATURE_EJECT_SCSI
 config FBSPLASH
        bool "fbsplash"
        default y
+       select PLATFORM_LINUX
        help
          Shows splash image and progress bar on framebuffer device.
          Can be used during boot phase of an embedded device. ~2kb.
@@ -305,6 +323,7 @@ config FLASH_ERASEALL
 config IONICE
        bool "ionice"
        default y
+       select PLATFORM_LINUX
        help
          Set/set program io scheduling class and priority
          Requires kernel >= 2.6.13
@@ -341,75 +360,10 @@ config FEATURE_LAST_FANCY
          logged into the system (mimics sysvinit last). +900 bytes.
 endchoice
 
-config LESS
-       bool "less"
-       default y
-       help
-         'less' is a pager, meaning that it displays text files. It possesses
-         a wide array of features, and is an improvement over 'more'.
-
-config FEATURE_LESS_MAXLINES
-       int "Max number of input lines less will try to eat"
-       default 9999999
-       depends on LESS
-
-config FEATURE_LESS_BRACKETS
-       bool "Enable bracket searching"
-       default y
-       depends on LESS
-       help
-         This option adds the capability to search for matching left and right
-         brackets, facilitating programming.
-
-config FEATURE_LESS_FLAGS
-       bool "Enable extra flags"
-       default y
-       depends on LESS
-       help
-         The extra flags provided do the following:
-
-         The -M flag enables a more sophisticated status line.
-         The -m flag enables a simpler status line with a percentage.
-
-config FEATURE_LESS_MARKS
-       bool "Enable marks"
-       default y
-       depends on LESS
-       help
-         Marks enable positions in a file to be stored for easy reference.
-
-config FEATURE_LESS_REGEXP
-       bool "Enable regular expressions"
-       default y
-       depends on LESS
-       help
-         Enable regular expressions, allowing complex file searches.
-
-config FEATURE_LESS_WINCH
-       bool "Enable automatic resizing on window size changes"
-       default y
-       depends on LESS
-       help
-         Makes less track window size changes.
-
-config FEATURE_LESS_DASHCMD
-       bool "Enable flag changes ('-' command)"
-       default y
-       depends on LESS
-       help
-         This enables the ability to change command-line flags within
-         less itself ('-' keyboard command).
-
-config FEATURE_LESS_LINENUMS
-       bool "Enable dynamic switching of line numbers"
-       default y
-       depends on FEATURE_LESS_DASHCMD
-       help
-         Enable "-N" command.
-
 config HDPARM
        bool "hdparm"
        default y
+       select PLATFORM_LINUX
        help
          Get/Set hard drive parameters. Primarily intended for ATA
          drives. Adds about 13k (or around 30k if you enable the
@@ -471,15 +425,15 @@ config MAKEDEVS
        help
          'makedevs' is a utility used to create a batch of devices with
          one command.
-         .
+
          There are two choices for command line behaviour, the interface
          as used by LEAF/Linux Router Project, or a device table file.
-         .
+
          'leaf' is traditionally what busybox follows, it allows multiple
          devices of a particluar type to be created per command.
          e.g. /dev/hda[0-9]
          Device properties are passed as command line arguments.
-         .
+
          'table' reads device properties from a file or stdin, allowing
          a batch of unrelated devices to be made with one command.
          User/group names are allowed as an alternative to uid/gid.
@@ -526,6 +480,7 @@ config MT
 config RAIDAUTORUN
        bool "raidautorun"
        default y
+       select PLATFORM_LINUX
        help
          raidautorun tells the kernel md driver to
          search and start RAID arrays.
@@ -534,6 +489,7 @@ config READAHEAD
        bool "readahead"
        default y
        depends on LFS
+       select PLATFORM_LINUX
        help
          Preload the files listed on the command line into RAM cache so that
          subsequent reads on these files will not block on disk I/O.
@@ -547,20 +503,10 @@ config READAHEAD
          As readahead(2) blocks until each file has been read, it is best to
          run this applet as a background job.
 
-config RFKILL
-       bool "rfkill"
-       default n  # doesn't build on Ubuntu 9.04
-       help
-         Enable/disable wireless devices.
-
-         rfkill list : list all wireless devices
-         rfkill list bluetooth : list all bluetooth devices
-         rfkill list 1 : list device corresponding to the given index
-         rfkill block|unblock wlan : block/unblock all wlan(wifi) devices
-
 config RUNLEVEL
        bool "runlevel"
        default y
+       depends on FEATURE_UTMP
        help
          find the current and previous system runlevel.
 
@@ -570,6 +516,7 @@ config RUNLEVEL
 config RX
        bool "rx"
        default y
+       select PLATFORM_LINUX
        help
          Receive files using the Xmodem protocol.
 
@@ -632,15 +579,10 @@ config VOLNAME
        help
          Prints a CD-ROM volume name.
 
-config WALL
-       bool "wall"
-       default y
-       help
-         Write a message to all users that are logged in.
-
 config WATCHDOG
        bool "watchdog"
        default y
+       select PLATFORM_LINUX
        help
          The watchdog utility is used with hardware or software watchdog
          device drivers. It opens the specified watchdog device special file
index d9bf143..9e164f1 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
@@ -36,7 +36,6 @@ lib-$(CONFIG_MOUNTPOINT)  += mountpoint.o
 lib-$(CONFIG_MT)          += mt.o
 lib-$(CONFIG_RAIDAUTORUN) += raidautorun.o
 lib-$(CONFIG_READAHEAD)   += readahead.o
-lib-$(CONFIG_RFKILL)      += rfkill.o
 lib-$(CONFIG_RUNLEVEL)    += runlevel.o
 lib-$(CONFIG_RX)          += rx.o
 lib-$(CONFIG_SETSID)      += setsid.o
@@ -46,5 +45,4 @@ lib-$(CONFIG_TIME)        += time.o
 lib-$(CONFIG_TIMEOUT)     += timeout.o
 lib-$(CONFIG_TTYSIZE)     += ttysize.o
 lib-$(CONFIG_VOLNAME)     += volname.o
-lib-$(CONFIG_WALL)        += wall.o
 lib-$(CONFIG_WATCHDOG)    += watchdog.o
index c12a10b..c8816e9 100644 (file)
@@ -8,11 +8,26 @@
  *
  * busyboxed 20 March 2001, Larry Doolittle <ldoolitt@recycle.lbl.gov>
  *
- * Licensed under GPLv2 or later, see file License in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define adjtimex_trivial_usage
+//usage:       "[-q] [-o OFF] [-f FREQ] [-p TCONST] [-t TICK]"
+//usage:#define adjtimex_full_usage "\n\n"
+//usage:       "Read and optionally set system timebase parameters. See adjtimex(2)\n"
+//usage:     "\n       -q      Quiet"
+//usage:     "\n       -o OFF  Time offset, microseconds"
+//usage:     "\n       -f FREQ Frequency adjust, integer kernel units (65536 is 1ppm)"
+//usage:     "\n               (positive values make clock run faster)"
+//usage:     "\n       -t TICK Microseconds per tick, usually 10000"
+//usage:     "\n       -p TCONST"
+
 #include "libbb.h"
-#include <sys/timex.h>
+#ifdef __BIONIC__
+# include <linux/timex.h>
+#else
+# include <sys/timex.h>
+#endif
 
 static const uint16_t statlist_bit[] = {
        STA_PLL,
index 0d649b4..e5f4eb3 100644 (file)
@@ -1,12 +1,40 @@
 /* vi: set sw=4 ts=4: */
 /* This file was released into the public domain by Paul Fox.
  */
+
+//usage:#define bbconfig_trivial_usage
+//usage:       ""
+//usage:#define bbconfig_full_usage "\n\n"
+//usage:       "Print the config file used by busybox build"
+
 #include "libbb.h"
 #include "bbconfigopts.h"
+#if ENABLE_FEATURE_COMPRESS_BBCONFIG
+# include "bb_archive.h"
+# include "bbconfigopts_bz2.h"
+#endif
 
 int bbconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int bbconfig_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 {
+#if ENABLE_FEATURE_COMPRESS_BBCONFIG
+       bunzip_data *bd;
+       int i = start_bunzip(&bd,
+                       /* src_fd: */ -1,
+                       /* inbuf:  */ bbconfig_config_bz2,
+                       /* len:    */ sizeof(bbconfig_config_bz2));
+       /* read_bunzip can longjmp to start_bunzip, and ultimately
+        * end up here with i != 0 on read data errors! Not trivial */
+       if (!i) {
+               /* Cannot use xmalloc: will leak bd in NOFORK case! */
+               char *outbuf = malloc_or_warn(sizeof(bbconfig_config));
+               if (outbuf) {
+                       read_bunzip(bd, outbuf, sizeof(bbconfig_config));
+                       full_write1_str(outbuf);
+               }
+       }
+#else
        full_write1_str(bbconfig_config);
+#endif
        return 0;
 }
index b0ee7ea..910e03e 100644 (file)
@@ -4,9 +4,19 @@
  *
  * Copyright (C) 2009 Bernhard Reutner-Fischer
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  */
+
+//usage:#define beep_trivial_usage
+//usage:       "-f FREQ -l LEN -d DELAY -r COUNT -n"
+//usage:#define beep_full_usage "\n\n"
+//usage:       "       -f      Frequency in Hz"
+//usage:     "\n       -l      Length in ms"
+//usage:     "\n       -d      Delay in ms"
+//usage:     "\n       -r      Repetitions"
+//usage:     "\n       -n      Start new tone"
+
 #include "libbb.h"
 
 #include <linux/kd.h>
index 3ffd7b2..bd2abc2 100644 (file)
@@ -5,12 +5,21 @@
  *
  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define chat_trivial_usage
+//usage:       "EXPECT [SEND [EXPECT [SEND...]]]"
+//usage:#define chat_full_usage "\n\n"
+//usage:       "Useful for interacting with a modem connected to stdin/stdout.\n"
+//usage:       "A script consists of one or more \"expect-send\" pairs of strings,\n"
+//usage:       "each pair is a pair of arguments. Example:\n"
+//usage:       "chat '' ATZ OK ATD123456 CONNECT '' ogin: pppuser word: ppppass '~'"
+
 #include "libbb.h"
 
 // default timeout: 45 sec
-#define        DEFAULT_CHAT_TIMEOUT 45*1000
+#define DEFAULT_CHAT_TIMEOUT 45*1000
 // max length of "abort string",
 // i.e. device reply which causes termination
 #define MAX_ABORT_LEN 50
@@ -175,23 +184,24 @@ int chat_main(int argc UNUSED_PARAM, char **argv)
                                llist_add_to_end(&aborts, arg);
 #if ENABLE_FEATURE_CHAT_CLR_ABORT
                        } else if (DIR_CLR_ABORT == key) {
+                               llist_t *l;
                                // remove the string from abort conditions
                                // N.B. gotta refresh maximum length too...
-#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+# if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
                                max_abort_len = 0;
-#endif
-                               for (llist_t *l = aborts; l; l = l->link) {
-#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+# endif
+                               for (l = aborts; l; l = l->link) {
+# if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
                                        size_t len = strlen(l->data);
-#endif
-                                       if (!strcmp(arg, l->data)) {
+# endif
+                                       if (strcmp(arg, l->data) == 0) {
                                                llist_unlink(&aborts, l);
                                                continue;
                                        }
-#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+# if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
                                        if (len > max_abort_len)
                                                max_abort_len = len;
-#endif
+# endif
                                }
 #endif
                        } else if (DIR_TIMEOUT == key) {
@@ -286,7 +296,7 @@ int chat_main(int argc UNUSED_PARAM, char **argv)
                                                full_write(record_fd, buf+buf_len, 1);
                                        }
                                        // dump device input if ECHO ON
-                                       if (echo > 0) {
+                                       if (echo) {
 //                                             if (buf[buf_len] < ' ') {
 //                                                     full_write(STDERR_FILENO, "^", 1);
 //                                                     buf[buf_len] += '@';
index d5f87c4..f2f559f 100644 (file)
@@ -3,13 +3,26 @@
  * chrt - manipulate real-time attributes of a process
  * Copyright (c) 2006-2007 Bernhard Reutner-Fischer
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define chrt_trivial_usage
+//usage:       "[-prfom] [PRIO] [PID | PROG ARGS]"
+//usage:#define chrt_full_usage "\n\n"
+//usage:       "Change scheduling priority and class for a process\n"
+//usage:     "\n       -p      Operate on PID"
+//usage:     "\n       -r      Set SCHED_RR class"
+//usage:     "\n       -f      Set SCHED_FIFO class"
+//usage:     "\n       -o      Set SCHED_OTHER class"
+//usage:     "\n       -m      Show min/max priorities"
+//usage:
+//usage:#define chrt_example_usage
+//usage:       "$ chrt -r 4 sleep 900; x=$!\n"
+//usage:       "$ chrt -f -p 3 $x\n"
+//usage:       "You need CAP_SYS_NICE privileges to set scheduling attributes of a process"
+
 #include <sched.h>
 #include "libbb.h"
-#ifndef _POSIX_PRIORITY_SCHEDULING
-#warning your system may be foobared
-#endif
 
 static const struct {
        int policy;
@@ -53,23 +66,25 @@ int chrt_main(int argc UNUSED_PARAM, char **argv)
        const char *current_new;
        int policy = SCHED_RR;
 
-       /* at least 1 arg; only one policy accepted */
-       opt_complementary = "-1:r--fo:f--ro:r--fo";
+       /* only one policy accepted */
+       opt_complementary = "r--fo:f--ro:o--rf";
        opt = getopt32(argv, "+mprfo");
+       if (opt & OPT_m) { /* print min/max and exit */
+               show_min_max(SCHED_FIFO);
+               show_min_max(SCHED_RR);
+               show_min_max(SCHED_OTHER);
+               fflush_stdout_and_exit(EXIT_SUCCESS);
+       }
        if (opt & OPT_r)
                policy = SCHED_RR;
        if (opt & OPT_f)
                policy = SCHED_FIFO;
        if (opt & OPT_o)
                policy = SCHED_OTHER;
-       if (opt & OPT_m) { /* print min/max */
-               show_min_max(SCHED_FIFO);
-               show_min_max(SCHED_RR);
-               show_min_max(SCHED_OTHER);
-               fflush_stdout_and_exit(EXIT_SUCCESS);
-       }
 
        argv += optind;
+       if (!argv[0])
+               bb_show_usage();
        if (opt & OPT_p) {
                pid_str = *argv++;
                if (*argv) { /* "-p <priority> <pid> [...]" */
@@ -90,13 +105,13 @@ int chrt_main(int argc UNUSED_PARAM, char **argv)
  print_rt_info:
                pol = sched_getscheduler(pid);
                if (pol < 0)
-                       bb_perror_msg_and_die("can't %cet pid %d's policy", 'g', pid);
+                       bb_perror_msg_and_die("can't %cet pid %d's policy", 'g', (int)pid);
                printf("pid %d's %s scheduling policy: %s\n",
                                pid, current_new, policies[pol].name);
                if (sched_getparam(pid, &sp))
-                       bb_perror_msg_and_die("can't get pid %d's attributes", pid);
+                       bb_perror_msg_and_die("can't get pid %d's attributes", (int)pid);
                printf("pid %d's %s scheduling priority: %d\n",
-                               pid, current_new, sp.sched_priority);
+                               (int)pid, current_new, sp.sched_priority);
                if (!*argv) {
                        /* Either it was just "-p <pid>",
                         * or it was "-p <priority> <pid>" and we came here
@@ -115,7 +130,7 @@ int chrt_main(int argc UNUSED_PARAM, char **argv)
        sp.sched_priority = xstrtou_range(priority, 0, policy != SCHED_OTHER ? 1 : 0, 99);
 
        if (sched_setscheduler(pid, policy, &sp) < 0)
-               bb_perror_msg_and_die("can't %cet pid %d's policy", 's', pid);
+               bb_perror_msg_and_die("can't %cet pid %d's policy", 's', (int)pid);
 
        if (!argv[0]) /* "-p <priority> <pid> [...]" */
                goto print_rt_info;
index 509a0f2..1a46a43 100644 (file)
@@ -7,16 +7,17 @@
  * Based on Russell Stuart's conspy.c
  *   http://ace-host.stuart.id.au/russell/files/conspy.c
  *
- * Licensed under GPLv2 or later, see file License in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
-//applet:IF_CONSPY(APPLET(conspy, _BB_DIR_BIN, _BB_SUID_DROP))
+//applet:IF_CONSPY(APPLET(conspy, BB_DIR_BIN, BB_SUID_DROP))
 
 //kbuild:lib-$(CONFIG_CONSPY) += conspy.o
 
 //config:config CONSPY
 //config:      bool "conspy"
-//config:      default n
+//config:      default y
+//config:      select PLATFORM_LINUX
 //config:      help
 //config:        A text-mode VNC like program for Linux virtual terminals.
 //config:        example:  conspy NUM      shared access to console num
 //config:        or        conspy -cs NUM  poor man's GNU screen like
 
 //usage:#define conspy_trivial_usage
-//usage:       "[-vcsndf] [-x COL] [-y LINE] [CONSOLE_NO]"
+//usage:       "[-vcsndfFQ] [-x COL] [-y LINE] [CONSOLE_NO]"
 //usage:#define conspy_full_usage "\n\n"
 //usage:     "A text-mode VNC like program for Linux virtual consoles."
 //usage:     "\nTo exit, quickly press ESC 3 times."
 //usage:     "\n"
-//usage:     "\nOptions:"
 //usage:     "\n       -v      Don't send keystrokes to the console"
-//usage:     "\n       -c      Create missing devices in /dev"
+//usage:     "\n       -c      Create missing /dev/{tty,vcsa}N"
 //usage:     "\n       -s      Open a SHELL session"
 //usage:     "\n       -n      Black & white"
 //usage:     "\n       -d      Dump console to stdout"
 //usage:     "\n       -f      Follow cursor"
+//usage:     "\n       -F      Assume console is on a framebuffer device"
+//usage:     "\n       -Q      Disable exit on ESC-ESC-ESC"
 //usage:     "\n       -x COL  Starting column"
 //usage:     "\n       -y LINE Starting line"
 
 #include "libbb.h"
 #include <sys/kd.h>
 
+#define ESC "\033"
+#define CURSOR_ON      -1
+#define CURSOR_OFF     1
+
+#define DEV_TTY                "/dev/tty"
+#define DEV_VCSA       "/dev/vcsa"
+
 struct screen_info {
        unsigned char lines, cols, cursor_x, cursor_y;
 };
@@ -69,19 +78,17 @@ struct globals {
        unsigned col;
        unsigned line;
        smallint curoff; // unknown:0 cursor on:-1 cursor off:1
-       char attrbuf[sizeof("\033[0;1;5;30;40m")];
+       char attrbuf[sizeof("0;1;5;30;40m")];
        // remote console
        struct screen_info remote;
        // saved local tty terminfo
        struct termios term_orig;
-       char vcsa_name[sizeof("/dev/vcsaNN")];
+       char vcsa_name[sizeof(DEV_VCSA "NN")];
 };
 
 #define G (*ptr_to_globals)
 #define INIT_G() do { \
        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
-       G.attrbuf[0] = '\033'; \
-       G.attrbuf[1] = '['; \
        G.width = G.height = UINT_MAX; \
        G.last_attr--; \
 } while (0)
@@ -89,18 +96,26 @@ struct globals {
 enum {
        FLAG_v,  // view only
        FLAG_c,  // create device if need
+       FLAG_Q,  // never exit
        FLAG_s,  // session
        FLAG_n,  // no colors
        FLAG_d,  // dump screen
        FLAG_f,  // follow cursor
+       FLAG_F,  // framebuffer
 };
 #define FLAG(x) (1 << FLAG_##x)
 #define BW (option_mask32 & FLAG(n))
 
+static void putcsi(const char *s)
+{
+       fputs(ESC"[", stdout);
+       fputs(s, stdout);
+}
+
 static void clrscr(void)
 {
        // Home, clear till end of screen
-       fputs("\033[1;1H" "\033[J", stdout);
+       putcsi("1;1H" ESC"[J");
        G.col = G.line = 0;
 }
 
@@ -108,7 +123,7 @@ static void set_cursor(int state)
 {
        if (G.curoff != state) {
                G.curoff = state;
-               fputs("\033[?25", stdout);
+               putcsi("?25");
                bb_putchar("h?l"[1 + state]);
        }
 }
@@ -118,22 +133,23 @@ static void gotoxy(int col, int line)
        if (G.col != col || G.line != line) {
                G.col = col;
                G.line = line;
-               printf("\033[%u;%uH", line + 1, col + 1);
+               printf(ESC"[%u;%uH", line + 1, col + 1);
        }
 }
 
+static void cleanup(int code) NORETURN;
 static void cleanup(int code)
 {
-       set_cursor(-1); // cursor on
+       set_cursor(CURSOR_ON);
        tcsetattr(G.kbd_fd, TCSANOW, &G.term_orig);
        if (ENABLE_FEATURE_CLEAN_UP) {
                close(G.kbd_fd);
        }
        // Reset attributes
        if (!BW)
-               fputs("\033[0m", stdout);
+               putcsi("0m");
        bb_putchar('\n');
-       if (code > 1)
+       if (code > EXIT_FAILURE)
                kill_myself_with_sig(code);
        exit(code);
 }
@@ -154,8 +170,8 @@ static void screen_read_close(void)
                G.size = i;
                G.data = xzalloc(2 * i);
        }
-       else if (G.size != i) {
-               cleanup(1);
+       if (G.size != i) {
+               cleanup(EXIT_FAILURE);
        }
        data = G.data + G.current;
        xread(vcsa_fd, data, G.size);
@@ -165,10 +181,15 @@ static void screen_read_close(void)
                        unsigned x = j - G.x; // if will catch j < G.x too
                        unsigned y = i - G.y; // if will catch i < G.y too
 
-                       if (CHAR(data) < ' ')
-                               CHAR(data) = ' ';
                        if (y >= G.height || x >= G.width)
                                DATA(data) = 0;
+                       else {
+                               uint8_t ch = CHAR(data);
+                               if (ch < ' ')
+                                       CHAR(data) = ch | 0x40;
+                               else if (ch > 0x7e)
+                                       CHAR(data) = '?';
+                       }
                }
        }
 }
@@ -176,10 +197,13 @@ static void screen_read_close(void)
 static void screen_char(char *data)
 {
        if (!BW) {
+               uint8_t attr_diff;
                uint8_t attr = ATTR(data);
-               //uint8_t attr = ATTR(data) >> 1; // for framebuffer console
-               uint8_t attr_diff = G.last_attr ^ attr;
 
+               if (option_mask32 & FLAG(F)) {
+                       attr >>= 1;
+               }
+               attr_diff = G.last_attr ^ attr;
                if (attr_diff) {
 // Attribute layout for VGA compatible text videobuffer:
 // blinking text
@@ -210,7 +234,7 @@ static void screen_char(char *data)
                        const uint8_t bg_mask = 0x70, blink_mask = 0x80;
                        char *ptr;
 
-                       ptr = G.attrbuf + 2; // skip "ESC ["
+                       ptr = G.attrbuf;
 
                        // (G.last_attr & ~attr) has 1 only where
                        // G.last_attr has 1 but attr has 0.
@@ -241,12 +265,12 @@ static void screen_char(char *data)
                        if (attr_diff & bg_mask) {
                                *ptr++ = '4';
                                *ptr++ = color[(attr & bg_mask) >> 4];
-                               *ptr++ = ';';
+                               ptr++; // last attribute
                        }
-                       if (ptr != G.attrbuf + 2) {
+                       if (ptr != G.attrbuf) {
                                ptr[-1] = 'm';
                                *ptr = '\0';
-                               fputs(G.attrbuf, stdout);
+                               putcsi(G.attrbuf);
                        }
                }
        }
@@ -289,11 +313,11 @@ static void curmove(void)
 {
        unsigned cx = G.remote.cursor_x - G.x;
        unsigned cy = G.remote.cursor_y - G.y;
-       int cursor = 1;
+       int cursor = CURSOR_OFF;
 
        if (cx < G.width && cy < G.height) {
                gotoxy(cx, cy);
-               cursor = -1;
+               cursor = CURSOR_ON;
        }
        set_cursor(cursor);
 }
@@ -312,10 +336,8 @@ static NOINLINE void start_shell_in_child(const char* tty_name)
        int pid = xvfork();
        if (pid == 0) {
                struct termios termchild;
-               char *shell = getenv("SHELL");
+               const char *shell = get_shell_name();
 
-               if (!shell)
-                       shell = (char *) DEFAULT_SHELL;
                signal(SIGHUP, SIG_IGN);
                // set tty as a controlling tty
                setsid();
@@ -340,7 +362,7 @@ static NOINLINE void start_shell_in_child(const char* tty_name)
 int conspy_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int conspy_main(int argc UNUSED_PARAM, char **argv)
 {
-       char tty_name[sizeof("/dev/ttyNN")];
+       char tty_name[sizeof(DEV_TTY "NN")];
 #define keybuf bb_common_bufsiz1
        struct termios termbuf;
        unsigned opts;
@@ -350,26 +372,28 @@ int conspy_main(int argc UNUSED_PARAM, char **argv)
        static const char getopt_longopts[] ALIGN1 =
                "viewonly\0"     No_argument "v"
                "createdevice\0" No_argument "c"
+               "neverquit\0"    No_argument "Q"
                "session\0"      No_argument "s"
                "nocolors\0"     No_argument "n"
                "dump\0"         No_argument "d"
                "follow\0"       No_argument "f"
+               "framebuffer\0"  No_argument "F"
                ;
 
        applet_long_options = getopt_longopts;
 #endif
        INIT_G();
-       strcpy(G.vcsa_name, "/dev/vcsa");
+       strcpy(G.vcsa_name, DEV_VCSA);
 
        opt_complementary = "x+:y+"; // numeric params
-       opts = getopt32(argv, "vcsndfx:y:", &G.x, &G.y);
+       opts = getopt32(argv, "vcQsndfFx:y:", &G.x, &G.y);
        argv += optind;
        ttynum = 0;
        if (argv[0]) {
                ttynum = xatou_range(argv[0], 0, 63);
-               sprintf(G.vcsa_name + sizeof("/dev/vcsa")-1, "%u", ttynum);
+               sprintf(G.vcsa_name + sizeof(DEV_VCSA)-1, "%u", ttynum);
        }
-       sprintf(tty_name, "%s%u", "/dev/tty", ttynum);
+       sprintf(tty_name, "%s%u", DEV_TTY, ttynum);
        if (opts & FLAG(c)) {
                if ((opts & (FLAG(s)|FLAG(v))) != FLAG(v))
                        create_cdev_if_doesnt_exist(tty_name, makedev(4, ttynum));
@@ -416,19 +440,19 @@ int conspy_main(int argc UNUSED_PARAM, char **argv)
 
                        if (G.remote.cursor_x < G.x) {
                                G.x = G.remote.cursor_x;
-                               i = 0;  // force refresh
+                               i = 0; // force refresh
                        }
                        if (nx > G.x) {
                                G.x = nx;
-                               i = 0;  // force refresh
+                               i = 0; // force refresh
                        }
                        if (G.remote.cursor_y < G.y) {
                                G.y = G.remote.cursor_y;
-                               i = 0;  // force refresh
+                               i = 0; // force refresh
                        }
                        if (ny > G.y) {
                                G.y = ny;
-                               i = 0;  // force refresh
+                               i = 0; // force refresh
                        }
                }
 
@@ -480,7 +504,7 @@ int conspy_main(int argc UNUSED_PARAM, char **argv)
                        char *k;
                case -1:
                        if (errno != EINTR)
-                               cleanup(1);
+                               goto abort;
                        break;
                case 0:
                        if (++G.nokeys >= 4)
@@ -491,32 +515,36 @@ int conspy_main(int argc UNUSED_PARAM, char **argv)
                        k = keybuf + G.key_count;
                        bytes_read = read(G.kbd_fd, k, sizeof(keybuf) - G.key_count);
                        if (bytes_read < 0)
-                               cleanup(1);
+                               goto abort;
 
                        // Do exit processing
-                       for (i = 0; i < bytes_read; i++) {
-                               if (k[i] != '\033')
-                                       G.escape_count = 0;
-                               else if (++G.escape_count >= 3)
-                                       cleanup(0);
+                       if (!(option_mask32 & FLAG(Q))) {
+                               for (i = 0; i < bytes_read; i++) {
+                                       if (k[i] != '\033')
+                                               G.escape_count = -1;
+                                       if (++G.escape_count >= 3)
+                                               cleanup(EXIT_SUCCESS);
+                               }
                        }
                }
                poll_timeout_ms = 250;
+               if (option_mask32 & FLAG(v)) continue;
 
                // Insert all keys pressed into the virtual console's input
                // buffer.  Don't do this if the virtual console is in scan
                // code mode - giving ASCII characters to a program expecting
                // scan codes will confuse it.
-               if (!(option_mask32 & FLAG(v)) && G.escape_count == 0) {
+               G.key_count += bytes_read;
+               if (G.escape_count == 0) {
                        int handle, result;
                        long kbd_mode;
 
-                       G.key_count += bytes_read;
                        handle = xopen(tty_name, O_WRONLY);
                        result = ioctl(handle, KDGKBMODE, &kbd_mode);
                        if (result >= 0) {
                                char *p = keybuf;
 
+                               G.ioerror_count = 0;
                                if (kbd_mode != K_XLATE && kbd_mode != K_UNICODE) {
                                        G.key_count = 0; // scan code mode
                                }
@@ -532,16 +560,18 @@ int conspy_main(int argc UNUSED_PARAM, char **argv)
                                        poll_timeout_ms = 20;
                                }
                        }
+                       // We sometimes get spurious IO errors on the TTY
+                       // as programs close and re-open it
+                       else if (errno != EIO || ++G.ioerror_count > 4) {
+                               if (ENABLE_FEATURE_CLEAN_UP)
+                                       close(handle);
+                               goto abort;
+                       }
                        // Close & re-open tty in case they have
                        // swapped virtual consoles
                        close(handle);
-
-                       // We sometimes get spurious IO errors on the TTY
-                       // as programs close and re-open it
-                       if (result >= 0)
-                               G.ioerror_count = 0;
-                       else if (errno != EIO || ++G.ioerror_count > 4)
-                               cleanup(1);
                }
        } /* while (1) */
+  abort:
+       cleanup(EXIT_FAILURE);
 }
index 4a3103c..582dc99 100644 (file)
@@ -8,65 +8,77 @@
  * (version 2.3.2)
  * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define crond_trivial_usage
+//usage:       "-fbS -l N " IF_FEATURE_CROND_D("-d N ") "-L LOGFILE -c DIR"
+//usage:#define crond_full_usage "\n\n"
+//usage:       "       -f      Foreground"
+//usage:     "\n       -b      Background (default)"
+//usage:     "\n       -S      Log to syslog (default)"
+//usage:     "\n       -l      Set log level. 0 is the most verbose, default 8"
+//usage:       IF_FEATURE_CROND_D(
+//usage:     "\n       -d      Set log level, log to stderr"
+//usage:       )
+//usage:     "\n       -L      Log to file"
+//usage:     "\n       -c      Working dir"
+
 #include "libbb.h"
 #include <syslog.h>
 
 /* glibc frees previous setenv'ed value when we do next setenv()
  * of the same variable. uclibc does not do this! */
 #if (defined(__GLIBC__) && !defined(__UCLIBC__)) /* || OTHER_SAFE_LIBC... */
-#define SETENV_LEAKS 0
+# define SETENV_LEAKS 0
 #else
-#define SETENV_LEAKS 1
+# define SETENV_LEAKS 1
 #endif
 
 
 #define TMPDIR          CONFIG_FEATURE_CROND_DIR
 #define CRONTABS        CONFIG_FEATURE_CROND_DIR "/crontabs"
 #ifndef SENDMAIL
-#define SENDMAIL        "sendmail"
+# define SENDMAIL       "sendmail"
 #endif
 #ifndef SENDMAIL_ARGS
-#define SENDMAIL_ARGS   "-ti", NULL
+# define SENDMAIL_ARGS  "-ti"
 #endif
 #ifndef CRONUPDATE
-#define CRONUPDATE      "cron.update"
+# define CRONUPDATE     "cron.update"
 #endif
 #ifndef MAXLINES
-#define MAXLINES        256    /* max lines in non-root crontabs */
+# define MAXLINES       256  /* max lines in non-root crontabs */
 #endif
 
 
 typedef struct CronFile {
-       struct CronFile *cf_Next;
-       struct CronLine *cf_LineBase;
-       char *cf_User;                  /* username                     */
-       smallint cf_Ready;              /* bool: one or more jobs ready */
-       smallint cf_Running;            /* bool: one or more jobs running */
-       smallint cf_Deleted;            /* marked for deletion, ignore  */
+       struct CronFile *cf_next;
+       struct CronLine *cf_lines;
+       char *cf_username;
+       smallint cf_wants_starting;     /* bool: one or more jobs ready */
+       smallint cf_has_running;        /* bool: one or more jobs running */
+       smallint cf_deleted;            /* marked for deletion (but still has running jobs) */
 } CronFile;
 
 typedef struct CronLine {
-       struct CronLine *cl_Next;
-       char *cl_Shell;         /* shell command                        */
-       pid_t cl_Pid;           /* running pid, 0, or armed (-1)        */
+       struct CronLine *cl_next;
+       char *cl_cmd;                   /* shell command */
+       pid_t cl_pid;                   /* >0:running, <0:needs to be started in this minute, 0:dormant */
 #if ENABLE_FEATURE_CROND_CALL_SENDMAIL
-       int cl_MailPos;         /* 'empty file' size                    */
-       smallint cl_MailFlag;   /* running pid is for mail              */
-       char *cl_MailTo;        /* whom to mail results                 */
+       int cl_empty_mail_size;         /* size of mail header only, 0 if no mailfile */
+       char *cl_mailto;                /* whom to mail results, may be NULL */
 #endif
        /* ordered by size, not in natural order. makes code smaller: */
-       char cl_Dow[7];         /* 0-6, beginning sunday                */
-       char cl_Mons[12];       /* 0-11                                 */
-       char cl_Hrs[24];        /* 0-23                                 */
-       char cl_Days[32];       /* 1-31                                 */
-       char cl_Mins[60];       /* 0-59                                 */
+       char cl_Dow[7];                 /* 0-6, beginning sunday */
+       char cl_Mons[12];               /* 0-11 */
+       char cl_Hrs[24];                /* 0-23 */
+       char cl_Days[32];               /* 1-31 */
+       char cl_Mins[60];               /* 0-59 */
 } CronLine;
 
 
-#define DaemonUid 0
+#define DAEMON_UID 0
 
 
 enum {
@@ -79,49 +91,30 @@ enum {
        OPT_d = (1 << 6) * ENABLE_FEATURE_CROND_D,
 };
 #if ENABLE_FEATURE_CROND_D
-#define DebugOpt (option_mask32 & OPT_d)
+# define DebugOpt (option_mask32 & OPT_d)
 #else
-#define DebugOpt 0
+# define DebugOpt 0
 #endif
 
 
 struct globals {
-       unsigned LogLevel; /* = 8; */
-       const char *LogFile;
-       const char *CDir; /* = CRONTABS; */
-       CronFile *FileBase;
+       unsigned log_level; /* = 8; */
+       time_t crontab_dir_mtime;
+       const char *log_filename;
+       const char *crontab_dir_name; /* = CRONTABS; */
+       CronFile *cron_files;
 #if SETENV_LEAKS
        char *env_var_user;
        char *env_var_home;
 #endif
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
-#define LogLevel           (G.LogLevel               )
-#define LogFile            (G.LogFile                )
-#define CDir               (G.CDir                   )
-#define FileBase           (G.FileBase               )
-#define env_var_user       (G.env_var_user           )
-#define env_var_home       (G.env_var_home           )
 #define INIT_G() do { \
-       LogLevel = 8; \
-       CDir = CRONTABS; \
+       G.log_level = 8; \
+       G.crontab_dir_name = CRONTABS; \
 } while (0)
 
 
-static void CheckUpdates(void);
-static void SynchronizeDir(void);
-static int TestJobs(time_t t1, time_t t2);
-static void RunJobs(void);
-static int CheckJobs(void);
-static void RunJob(const char *user, CronLine *line);
-#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
-static void EndJob(const char *user, CronLine *line);
-#else
-#define EndJob(user, line)  ((line)->cl_Pid = 0)
-#endif
-static void DeleteFile(const char *userName);
-
-
 /* 0 is the most verbose, default 8 */
 #define LVL5  "\x05"
 #define LVL7  "\x07"
@@ -138,12 +131,12 @@ static void crondlog(const char *ctl, ...)
        int level = (ctl[0] & 0x1f);
 
        va_start(va, ctl);
-       if (level >= (int)LogLevel) {
+       if (level >= (int)G.log_level) {
                /* Debug mode: all to (non-redirected) stderr, */
                /* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */
-               if (!DebugOpt && LogFile) {
+               if (!DebugOpt && G.log_filename) {
                        /* Otherwise (log to file): we reopen log file at every write: */
-                       int logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0666);
+                       int logfd = open_or_warn(G.log_filename, O_WRONLY | O_CREAT | O_APPEND);
                        if (logfd >= 0)
                                xmove_fd(logfd, STDERR_FILENO);
                }
@@ -163,141 +156,6 @@ static void crondlog(const char *ctl, ...)
                exit(20);
 }
 
-int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int crond_main(int argc UNUSED_PARAM, char **argv)
-{
-       unsigned opts;
-
-       INIT_G();
-
-       /* "-b after -f is ignored", and so on for every pair a-b */
-       opt_complementary = "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l")
-                       ":l+:d+"; /* -l and -d have numeric param */
-       opts = getopt32(argv, "l:L:fbSc:" IF_FEATURE_CROND_D("d:"),
-                       &LogLevel, &LogFile, &CDir
-                       IF_FEATURE_CROND_D(,&LogLevel));
-       /* both -d N and -l N set the same variable: LogLevel */
-
-       if (!(opts & OPT_f)) {
-               /* close stdin, stdout, stderr.
-                * close unused descriptors - don't need them. */
-               bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
-       }
-
-       if (!(opts & OPT_d) && LogFile == NULL) {
-               /* logging to syslog */
-               openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);
-               logmode = LOGMODE_SYSLOG;
-       }
-
-       xchdir(CDir);
-       //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */
-       xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */
-       crondlog(LVL8 "crond (busybox "BB_VER") started, log level %d", LogLevel);
-       SynchronizeDir();
-
-       /* main loop - synchronize to 1 second after the minute, minimum sleep
-        * of 1 second. */
-       {
-               time_t t1 = time(NULL);
-               int rescan = 60;
-               int sleep_time = 60;
-
-               write_pidfile("/var/run/crond.pid");
-               for (;;) {
-                       time_t t2;
-                       long dt;
-
-                       sleep((sleep_time + 1) - (time(NULL) % sleep_time));
-
-                       t2 = time(NULL);
-                       dt = (long)t2 - (long)t1;
-
-                       /*
-                        * The file 'cron.update' is checked to determine new cron
-                        * jobs.  The directory is rescanned once an hour to deal
-                        * with any screwups.
-                        *
-                        * check for disparity.  Disparities over an hour either way
-                        * result in resynchronization.  A reverse-indexed disparity
-                        * less then an hour causes us to effectively sleep until we
-                        * match the original time (i.e. no re-execution of jobs that
-                        * have just been run).  A forward-indexed disparity less then
-                        * an hour causes intermediate jobs to be run, but only once
-                        * in the worst case.
-                        *
-                        * when running jobs, the inequality used is greater but not
-                        * equal to t1, and less then or equal to t2.
-                        */
-                       if (--rescan == 0) {
-                               rescan = 60;
-                               SynchronizeDir();
-                       }
-                       CheckUpdates();
-                       if (DebugOpt)
-                               crondlog(LVL5 "wakeup dt=%ld", dt);
-                       if (dt < -60 * 60 || dt > 60 * 60) {
-                               crondlog(WARN9 "time disparity of %ld minutes detected", dt / 60);
-                       } else if (dt > 0) {
-                               TestJobs(t1, t2);
-                               RunJobs();
-                               sleep(5);
-                               if (CheckJobs() > 0) {
-                                       sleep_time = 10;
-                               } else {
-                                       sleep_time = 60;
-                               }
-                       }
-                       t1 = t2;
-               } /* for (;;) */
-       }
-
-       return 0; /* not reached */
-}
-
-#if SETENV_LEAKS
-/* We set environment *before* vfork (because we want to use vfork),
- * so we cannot use setenv() - repeated calls to setenv() may leak memory!
- * Using putenv(), and freeing memory after unsetenv() won't leak */
-static void safe_setenv(char **pvar_val, const char *var, const char *val)
-{
-       char *var_val = *pvar_val;
-
-       if (var_val) {
-               bb_unsetenv_and_free(var_val);
-       }
-       *pvar_val = xasprintf("%s=%s", var, val);
-       putenv(*pvar_val);
-}
-#endif
-
-static void SetEnv(struct passwd *pas)
-{
-#if SETENV_LEAKS
-       safe_setenv(&env_var_user, "USER", pas->pw_name);
-       safe_setenv(&env_var_home, "HOME", pas->pw_dir);
-       /* if we want to set user's shell instead: */
-       /*safe_setenv(env_var_user, "SHELL", pas->pw_shell);*/
-#else
-       xsetenv("USER", pas->pw_name);
-       xsetenv("HOME", pas->pw_dir);
-#endif
-       /* currently, we use constant one: */
-       /*setenv("SHELL", DEFAULT_SHELL, 1); - done earlier */
-}
-
-static void ChangeUser(struct passwd *pas)
-{
-       /* careful: we're after vfork! */
-       change_identity(pas); /* - initgroups, setgid, setuid */
-       if (chdir(pas->pw_dir) < 0) {
-               crondlog(WARN9 "chdir(%s)", pas->pw_dir);
-               if (chdir(TMPDIR) < 0) {
-                       crondlog(DIE9 "chdir(%s)", TMPDIR); /* exits */
-               }
-       }
-}
-
 static const char DowAry[] ALIGN1 =
        "sun""mon""tue""wed""thu""fri""sat"
        /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */
@@ -325,7 +183,7 @@ static void ParseField(char *user, char *ary, int modvalue, int off,
 
                /* Handle numeric digit or symbol or '*' */
                if (*ptr == '*') {
-                       n1 = 0;         /* everything will be filled */
+                       n1 = 0;  /* everything will be filled */
                        n2 = modvalue - 1;
                        skip = 1;
                        ++ptr;
@@ -398,7 +256,6 @@ static void ParseField(char *user, char *ary, int modvalue, int off,
                                        goto err;
                                }
                        } while (n1 != n2);
-
                }
                if (*ptr != ',') {
                        break;
@@ -414,7 +271,7 @@ static void ParseField(char *user, char *ary, int modvalue, int off,
                return;
        }
 
-       if (DebugOpt && (LogLevel <= 5)) { /* like LVL5 */
+       if (DebugOpt && (G.log_level <= 5)) { /* like LVL5 */
                /* can't use crondlog, it inserts '\n' */
                int i;
                for (i = 0; i < modvalue; ++i)
@@ -449,7 +306,60 @@ static void FixDayDow(CronLine *line)
        }
 }
 
-static void SynchronizeFile(const char *fileName)
+/*
+ * delete_cronfile() - delete user database
+ *
+ * Note: multiple entries for same user may exist if we were unable to
+ * completely delete a database due to running processes.
+ */
+//FIXME: we will start a new job even if the old job is running
+//if crontab was reloaded: crond thinks that "new" job is different from "old"
+//even if they are in fact completely the same. Example
+//Crontab was:
+// 0-59 * * * * job1
+// 0-59 * * * * long_running_job2
+//User edits crontab to:
+// 0-59 * * * * job1_updated
+// 0-59 * * * * long_running_job2
+//Bug: crond can now start another long_running_job2 even if old one
+//is still running.
+//OTOH most other versions of cron do not wait for job termination anyway,
+//they end up with multiple copies of jobs if they don't terminate soon enough.
+static void delete_cronfile(const char *userName)
+{
+       CronFile **pfile = &G.cron_files;
+       CronFile *file;
+
+       while ((file = *pfile) != NULL) {
+               if (strcmp(userName, file->cf_username) == 0) {
+                       CronLine **pline = &file->cf_lines;
+                       CronLine *line;
+
+                       file->cf_has_running = 0;
+                       file->cf_deleted = 1;
+
+                       while ((line = *pline) != NULL) {
+                               if (line->cl_pid > 0) {
+                                       file->cf_has_running = 1;
+                                       pline = &line->cl_next;
+                               } else {
+                                       *pline = line->cl_next;
+                                       free(line->cl_cmd);
+                                       free(line);
+                               }
+                       }
+                       if (file->cf_has_running == 0) {
+                               *pfile = file->cf_next;
+                               free(file->cf_username);
+                               free(file);
+                               continue;
+                       }
+               }
+               pfile = &file->cf_next;
+       }
+}
+
+static void load_crontab(const char *fileName)
 {
        struct parser_t *parser;
        struct stat sbuf;
@@ -459,23 +369,26 @@ static void SynchronizeFile(const char *fileName)
        char *mailTo = NULL;
 #endif
 
-       if (!fileName)
+       delete_cronfile(fileName);
+
+       if (!getpwnam(fileName)) {
+               crondlog(LVL7 "ignoring file '%s' (no such user)", fileName);
                return;
+       }
 
-       DeleteFile(fileName);
        parser = config_open(fileName);
        if (!parser)
                return;
 
        maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES;
 
-       if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {
+       if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DAEMON_UID) {
                CronFile *file = xzalloc(sizeof(CronFile));
                CronLine **pline;
                int n;
 
-               file->cf_User = xstrdup(fileName);
-               pline = &file->cf_LineBase;
+               file->cf_username = xstrdup(fileName);
+               pline = &file->cf_lines;
 
                while (1) {
                        CronLine *line;
@@ -502,11 +415,11 @@ static void SynchronizeFile(const char *fileName)
                                continue;
                        *pline = line = xzalloc(sizeof(*line));
                        /* parse date ranges */
-                       ParseField(file->cf_User, line->cl_Mins, 60, 0, NULL, tokens[0]);
-                       ParseField(file->cf_User, line->cl_Hrs, 24, 0, NULL, tokens[1]);
-                       ParseField(file->cf_User, line->cl_Days, 32, 0, NULL, tokens[2]);
-                       ParseField(file->cf_User, line->cl_Mons, 12, -1, MonAry, tokens[3]);
-                       ParseField(file->cf_User, line->cl_Dow, 7, 0, DowAry, tokens[4]);
+                       ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]);
+                       ParseField(file->cf_username, line->cl_Hrs, 24, 0, NULL, tokens[1]);
+                       ParseField(file->cf_username, line->cl_Days, 32, 0, NULL, tokens[2]);
+                       ParseField(file->cf_username, line->cl_Mons, 12, -1, MonAry, tokens[3]);
+                       ParseField(file->cf_username, line->cl_Dow, 7, 0, DowAry, tokens[4]);
                        /*
                         * fix days and dow - if one is not "*" and the other
                         * is "*", the other is set to 0, and vise-versa
@@ -514,20 +427,20 @@ static void SynchronizeFile(const char *fileName)
                        FixDayDow(line);
 #if ENABLE_FEATURE_CROND_CALL_SENDMAIL
                        /* copy mailto (can be NULL) */
-                       line->cl_MailTo = xstrdup(mailTo);
+                       line->cl_mailto = xstrdup(mailTo);
 #endif
                        /* copy command */
-                       line->cl_Shell = xstrdup(tokens[5]);
+                       line->cl_cmd = xstrdup(tokens[5]);
                        if (DebugOpt) {
                                crondlog(LVL5 " command:%s", tokens[5]);
                        }
-                       pline = &line->cl_Next;
+                       pline = &line->cl_next;
 //bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]);
                }
                *pline = NULL;
 
-               file->cf_Next = FileBase;
-               FileBase = file;
+               file->cf_next = G.cron_files;
+               G.cron_files = file;
 
                if (maxLines == 0) {
                        crondlog(WARN9 "user %s: too many lines", fileName);
@@ -536,7 +449,7 @@ static void SynchronizeFile(const char *fileName)
        config_close(parser);
 }
 
-static void CheckUpdates(void)
+static void process_cron_update_file(void)
 {
        FILE *fi;
        char buf[256];
@@ -546,36 +459,34 @@ static void CheckUpdates(void)
                unlink(CRONUPDATE);
                while (fgets(buf, sizeof(buf), fi) != NULL) {
                        /* use first word only */
-                       SynchronizeFile(strtok(buf, " \t\r\n"));
+                       skip_non_whitespace(buf)[0] = '\0';
+                       load_crontab(buf);
                }
                fclose(fi);
        }
 }
 
-static void SynchronizeDir(void)
+static void rescan_crontab_dir(void)
 {
        CronFile *file;
-       /* Attempt to delete the database. */
+
+       /* Delete all files until we only have ones with running jobs (or none) */
  again:
-       for (file = FileBase; file; file = file->cf_Next) {
-               if (!file->cf_Deleted) {
-                       DeleteFile(file->cf_User);
+       for (file = G.cron_files; file; file = file->cf_next) {
+               if (!file->cf_deleted) {
+                       delete_cronfile(file->cf_username);
                        goto again;
                }
        }
 
-       /*
-        * Remove cron update file
-        *
-        * Re-chdir, in case directory was renamed & deleted, or otherwise
-        * screwed up.
-        *
-        * scan directory and add associated users
-        */
+       /* Remove cron update file */
        unlink(CRONUPDATE);
-       if (chdir(CDir) < 0) {
-               crondlog(DIE9 "chdir(%s)", CDir);
+       /* Re-chdir, in case directory was renamed & deleted */
+       if (chdir(G.crontab_dir_name) < 0) {
+               crondlog(DIE9 "chdir(%s)", G.crontab_dir_name);
        }
+
+       /* Scan directory and add associated users */
        {
                DIR *dir = opendir(".");
                struct dirent *den;
@@ -586,184 +497,63 @@ static void SynchronizeDir(void)
                        if (strchr(den->d_name, '.') != NULL) {
                                continue;
                        }
-                       if (getpwnam(den->d_name)) {
-                               SynchronizeFile(den->d_name);
-                       } else {
-                               crondlog(LVL7 "ignoring %s", den->d_name);
-                       }
+                       load_crontab(den->d_name);
                }
                closedir(dir);
        }
 }
 
-/*
- *  DeleteFile() - delete user database
- *
- *  Note: multiple entries for same user may exist if we were unable to
- *  completely delete a database due to running processes.
- */
-static void DeleteFile(const char *userName)
-{
-       CronFile **pfile = &FileBase;
-       CronFile *file;
-
-       while ((file = *pfile) != NULL) {
-               if (strcmp(userName, file->cf_User) == 0) {
-                       CronLine **pline = &file->cf_LineBase;
-                       CronLine *line;
-
-                       file->cf_Running = 0;
-                       file->cf_Deleted = 1;
-
-                       while ((line = *pline) != NULL) {
-                               if (line->cl_Pid > 0) {
-                                       file->cf_Running = 1;
-                                       pline = &line->cl_Next;
-                               } else {
-                                       *pline = line->cl_Next;
-                                       free(line->cl_Shell);
-                                       free(line);
-                               }
-                       }
-                       if (file->cf_Running == 0) {
-                               *pfile = file->cf_Next;
-                               free(file->cf_User);
-                               free(file);
-                       } else {
-                               pfile = &file->cf_Next;
-                       }
-               } else {
-                       pfile = &file->cf_Next;
-               }
-       }
-}
-
-/*
- * TestJobs()
- *
- * determine which jobs need to be run.  Under normal conditions, the
- * period is about a minute (one scan).  Worst case it will be one
- * hour (60 scans).
- */
-static int TestJobs(time_t t1, time_t t2)
+#if SETENV_LEAKS
+/* We set environment *before* vfork (because we want to use vfork),
+ * so we cannot use setenv() - repeated calls to setenv() may leak memory!
+ * Using putenv(), and freeing memory after unsetenv() won't leak */
+static void safe_setenv(char **pvar_val, const char *var, const char *val)
 {
-       int nJobs = 0;
-       time_t t;
-
-       /* Find jobs > t1 and <= t2 */
-
-       for (t = t1 - t1 % 60; t <= t2; t += 60) {
-               struct tm *ptm;
-               CronFile *file;
-               CronLine *line;
-
-               if (t <= t1)
-                       continue;
+       char *var_val = *pvar_val;
 
-               ptm = localtime(&t);
-               for (file = FileBase; file; file = file->cf_Next) {
-                       if (DebugOpt)
-                               crondlog(LVL5 "file %s:", file->cf_User);
-                       if (file->cf_Deleted)
-                               continue;
-                       for (line = file->cf_LineBase; line; line = line->cl_Next) {
-                               if (DebugOpt)
-                                       crondlog(LVL5 " line %s", line->cl_Shell);
-                               if (line->cl_Mins[ptm->tm_min] && line->cl_Hrs[ptm->tm_hour]
-                                && (line->cl_Days[ptm->tm_mday] || line->cl_Dow[ptm->tm_wday])
-                                && line->cl_Mons[ptm->tm_mon]
-                               ) {
-                                       if (DebugOpt) {
-                                               crondlog(LVL5 " job: %d %s",
-                                                       (int)line->cl_Pid, line->cl_Shell);
-                                       }
-                                       if (line->cl_Pid > 0) {
-                                               crondlog(LVL8 "user %s: process already running: %s",
-                                                       file->cf_User, line->cl_Shell);
-                                       } else if (line->cl_Pid == 0) {
-                                               line->cl_Pid = -1;
-                                               file->cf_Ready = 1;
-                                               ++nJobs;
-                                       }
-                               }
-                       }
-               }
+       if (var_val) {
+               bb_unsetenv_and_free(var_val);
        }
-       return nJobs;
+       *pvar_val = xasprintf("%s=%s", var, val);
+       putenv(*pvar_val);
 }
+#endif
 
-static void RunJobs(void)
+static void set_env_vars(struct passwd *pas)
 {
-       CronFile *file;
-       CronLine *line;
-
-       for (file = FileBase; file; file = file->cf_Next) {
-               if (!file->cf_Ready)
-                       continue;
-
-               file->cf_Ready = 0;
-               for (line = file->cf_LineBase; line; line = line->cl_Next) {
-                       if (line->cl_Pid >= 0)
-                               continue;
-
-                       RunJob(file->cf_User, line);
-                       crondlog(LVL8 "USER %s pid %3d cmd %s",
-                               file->cf_User, (int)line->cl_Pid, line->cl_Shell);
-                       if (line->cl_Pid < 0) {
-                               file->cf_Ready = 1;
-                       } else if (line->cl_Pid > 0) {
-                               file->cf_Running = 1;
-                       }
-               }
-       }
+#if SETENV_LEAKS
+       safe_setenv(&G.env_var_user, "USER", pas->pw_name);
+       safe_setenv(&G.env_var_home, "HOME", pas->pw_dir);
+       /* if we want to set user's shell instead: */
+       /*safe_setenv(G.env_var_shell, "SHELL", pas->pw_shell);*/
+#else
+       xsetenv("USER", pas->pw_name);
+       xsetenv("HOME", pas->pw_dir);
+#endif
+       /* currently, we use constant one: */
+       /*setenv("SHELL", DEFAULT_SHELL, 1); - done earlier */
 }
 
-/*
- * CheckJobs() - check for job completion
- *
- * Check for job completion, return number of jobs still running after
- * all done.
- */
-static int CheckJobs(void)
+static void change_user(struct passwd *pas)
 {
-       CronFile *file;
-       CronLine *line;
-       int nStillRunning = 0;
-
-       for (file = FileBase; file; file = file->cf_Next) {
-               if (file->cf_Running) {
-                       file->cf_Running = 0;
-
-                       for (line = file->cf_LineBase; line; line = line->cl_Next) {
-                               int status, r;
-                               if (line->cl_Pid <= 0)
-                                       continue;
-
-                               r = waitpid(line->cl_Pid, &status, WNOHANG);
-                               if (r < 0 || r == line->cl_Pid) {
-                                       EndJob(file->cf_User, line);
-                                       if (line->cl_Pid) {
-                                               file->cf_Running = 1;
-                                       }
-                               } else if (r == 0) {
-                                       file->cf_Running = 1;
-                               }
-                       }
+       /* careful: we're after vfork! */
+       change_identity(pas); /* - initgroups, setgid, setuid */
+       if (chdir(pas->pw_dir) < 0) {
+               crondlog(WARN9 "chdir(%s)", pas->pw_dir);
+               if (chdir(TMPDIR) < 0) {
+                       crondlog(DIE9 "chdir(%s)", TMPDIR); /* exits */
                }
-               nStillRunning += file->cf_Running;
        }
-       return nStillRunning;
 }
 
-#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
-
 // TODO: sendmail should be _run-time_ option, not compile-time!
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
 
-static void
-ForkJob(const char *user, CronLine *line, int mailFd,
-               const char *prog, const char *cmd, const char *arg,
-               const char *mail_filename)
-{
+static pid_t
+fork_job(const char *user, int mailFd,
+               const char *prog,
+               const char *shell_cmd /* if NULL, we run sendmail */
+{
        struct passwd *pas;
        pid_t pid;
 
@@ -773,48 +563,36 @@ ForkJob(const char *user, CronLine *line, int mailFd,
                crondlog(WARN9 "can't get uid for %s", user);
                goto err;
        }
-       SetEnv(pas);
+       set_env_vars(pas);
 
        pid = vfork();
        if (pid == 0) {
                /* CHILD */
-               /* change running state to the user in question */
-               ChangeUser(pas);
+               /* initgroups, setgid, setuid, and chdir to home or TMPDIR */
+               change_user(pas);
                if (DebugOpt) {
                        crondlog(LVL5 "child running %s", prog);
                }
                if (mailFd >= 0) {
-                       xmove_fd(mailFd, mail_filename ? 1 : 0);
+                       xmove_fd(mailFd, shell_cmd ? 1 : 0);
                        dup2(1, 2);
                }
                /* crond 3.0pl1-100 puts tasks in separate process groups */
                bb_setpgrp();
-               execlp(prog, prog, cmd, arg, (char *) NULL);
-               crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user, prog, cmd, arg);
-               if (mail_filename) {
-                       fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);
+               execlp(prog, prog, (shell_cmd ? "-c" : SENDMAIL_ARGS), shell_cmd, (char *) NULL);
+               crondlog(ERR20 "can't execute '%s' for user %s", prog, user);
+               if (shell_cmd) {
+                       fdprintf(1, "Exec failed: %s -c %s\n", prog, shell_cmd);
                }
                _exit(EXIT_SUCCESS);
        }
 
-       line->cl_Pid = pid;
        if (pid < 0) {
                /* FORK FAILED */
                crondlog(ERR20 "can't vfork");
  err:
-               line->cl_Pid = 0;
-               if (mail_filename) {
-                       unlink(mail_filename);
-               }
-       } else if (mail_filename) {
-               /* PARENT, FORK SUCCESS
-                * rename mail-file based on pid of process
-                */
-               char mailFile2[128];
-
-               snprintf(mailFile2, sizeof(mailFile2), "%s/cron.%s.%d", TMPDIR, user, pid);
-               rename(mail_filename, mailFile2); // TODO: xrename?
-       }
+               pid = 0;
+       } /* else: PARENT, FORK SUCCESS */
 
        /*
         * Close the mail file descriptor.. we can't just leave it open in
@@ -823,112 +601,120 @@ ForkJob(const char *user, CronLine *line, int mailFd,
        if (mailFd >= 0) {
                close(mailFd);
        }
+       return pid;
 }
 
-static void RunJob(const char *user, CronLine *line)
+static void start_one_job(const char *user, CronLine *line)
 {
        char mailFile[128];
        int mailFd = -1;
 
-       line->cl_Pid = 0;
-       line->cl_MailFlag = 0;
+       line->cl_pid = 0;
+       line->cl_empty_mail_size = 0;
 
-       if (line->cl_MailTo) {
-               /* open mail file - owner root so nobody can screw with it. */
+       if (line->cl_mailto) {
+               /* Open mail file (owner is root so nobody can screw with it) */
                snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, getpid());
                mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600);
 
                if (mailFd >= 0) {
-                       line->cl_MailFlag = 1;
-                       fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", line->cl_MailTo,
-                               line->cl_Shell);
-                       line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR);
+                       fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", line->cl_mailto,
+                               line->cl_cmd);
+                       line->cl_empty_mail_size = lseek(mailFd, 0, SEEK_CUR);
                } else {
                        crondlog(ERR20 "can't create mail file %s for user %s, "
                                        "discarding output", mailFile, user);
                }
        }
 
-       ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile);
+       line->cl_pid = fork_job(user, mailFd, DEFAULT_SHELL, line->cl_cmd);
+       if (mailFd >= 0) {
+               if (line->cl_pid <= 0) {
+                       unlink(mailFile);
+               } else {
+                       /* rename mail-file based on pid of process */
+                       char *mailFile2 = xasprintf("%s/cron.%s.%d", TMPDIR, user, (int)line->cl_pid);
+                       rename(mailFile, mailFile2); // TODO: xrename?
+                       free(mailFile2);
+               }
+       }
 }
 
 /*
- * EndJob - called when job terminates and when mail terminates
+ * process_finished_job - called when job terminates and when mail terminates
  */
-static void EndJob(const char *user, CronLine *line)
+static void process_finished_job(const char *user, CronLine *line)
 {
+       pid_t pid;
        int mailFd;
        char mailFile[128];
        struct stat sbuf;
 
-       /* No job */
-       if (line->cl_Pid <= 0) {
-               line->cl_Pid = 0;
+       pid = line->cl_pid;
+       line->cl_pid = 0;
+       if (pid <= 0) {
+               /* No job */
                return;
        }
-
-       /*
-        * End of job and no mail file
-        * End of sendmail job
-        */
-       snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, line->cl_Pid);
-       line->cl_Pid = 0;
-
-       if (line->cl_MailFlag == 0) {
+       if (line->cl_empty_mail_size <= 0) {
+               /* End of job and no mail file, or end of sendmail job */
                return;
        }
-       line->cl_MailFlag = 0;
 
        /*
-        * End of primary job - check for mail file.  If size has increased and
-        * the file is still valid, we sendmail it.
+        * End of primary job - check for mail file.
+        * If size has changed and the file is still valid, we send it.
         */
+       snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, (int)pid);
        mailFd = open(mailFile, O_RDONLY);
        unlink(mailFile);
        if (mailFd < 0) {
                return;
        }
 
-       if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid
-        || sbuf.st_nlink != 0 || sbuf.st_size == line->cl_MailPos
+       if (fstat(mailFd, &sbuf) < 0
+        || sbuf.st_uid != DAEMON_UID
+        || sbuf.st_nlink != 0
+        || sbuf.st_size == line->cl_empty_mail_size
         || !S_ISREG(sbuf.st_mode)
        ) {
                close(mailFd);
                return;
        }
-       if (line->cl_MailTo)
-               ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
+       line->cl_empty_mail_size = 0;
+       /* if (line->cl_mailto) - always true if cl_empty_mail_size was nonzero */
+               line->cl_pid = fork_job(user, mailFd, SENDMAIL, NULL);
 }
 
-#else /* crond without sendmail */
+#else /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */
 
-static void RunJob(const char *user, CronLine *line)
+static void start_one_job(const char *user, CronLine *line)
 {
        struct passwd *pas;
        pid_t pid;
 
-       /* prepare things before vfork */
        pas = getpwnam(user);
        if (!pas) {
                crondlog(WARN9 "can't get uid for %s", user);
                goto err;
        }
-       SetEnv(pas);
 
-       /* fork as the user in question and run program */
+       /* Prepare things before vfork */
+       set_env_vars(pas);
+
+       /* Fork as the user in question and run program */
        pid = vfork();
        if (pid == 0) {
                /* CHILD */
-               /* change running state to the user in question */
-               ChangeUser(pas);
+               /* initgroups, setgid, setuid, and chdir to home or TMPDIR */
+               change_user(pas);
                if (DebugOpt) {
                        crondlog(LVL5 "child running %s", DEFAULT_SHELL);
                }
                /* crond 3.0pl1-100 puts tasks in separate process groups */
                bb_setpgrp();
-               execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, (char *) NULL);
-               crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user,
-                                DEFAULT_SHELL, "-c", line->cl_Shell);
+               execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_cmd, (char *) NULL);
+               crondlog(ERR20 "can't execute '%s' for user %s", DEFAULT_SHELL, user);
                _exit(EXIT_SUCCESS);
        }
        if (pid < 0) {
@@ -937,7 +723,232 @@ static void RunJob(const char *user, CronLine *line)
  err:
                pid = 0;
        }
-       line->cl_Pid = pid;
+       line->cl_pid = pid;
+}
+
+#define process_finished_job(user, line)  ((line)->cl_pid = 0)
+
+#endif /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */
+
+/*
+ * Determine which jobs need to be run.  Under normal conditions, the
+ * period is about a minute (one scan).  Worst case it will be one
+ * hour (60 scans).
+ */
+static void flag_starting_jobs(time_t t1, time_t t2)
+{
+       time_t t;
+
+       /* Find jobs > t1 and <= t2 */
+
+       for (t = t1 - t1 % 60; t <= t2; t += 60) {
+               struct tm *ptm;
+               CronFile *file;
+               CronLine *line;
+
+               if (t <= t1)
+                       continue;
+
+               ptm = localtime(&t);
+               for (file = G.cron_files; file; file = file->cf_next) {
+                       if (DebugOpt)
+                               crondlog(LVL5 "file %s:", file->cf_username);
+                       if (file->cf_deleted)
+                               continue;
+                       for (line = file->cf_lines; line; line = line->cl_next) {
+                               if (DebugOpt)
+                                       crondlog(LVL5 " line %s", line->cl_cmd);
+                               if (line->cl_Mins[ptm->tm_min]
+                                && line->cl_Hrs[ptm->tm_hour]
+                                && (line->cl_Days[ptm->tm_mday] || line->cl_Dow[ptm->tm_wday])
+                                && line->cl_Mons[ptm->tm_mon]
+                               ) {
+                                       if (DebugOpt) {
+                                               crondlog(LVL5 " job: %d %s",
+                                                       (int)line->cl_pid, line->cl_cmd);
+                                       }
+                                       if (line->cl_pid > 0) {
+                                               crondlog(LVL8 "user %s: process already running: %s",
+                                                       file->cf_username, line->cl_cmd);
+                                       } else if (line->cl_pid == 0) {
+                                               line->cl_pid = -1;
+                                               file->cf_wants_starting = 1;
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+static void start_jobs(void)
+{
+       CronFile *file;
+       CronLine *line;
+
+       for (file = G.cron_files; file; file = file->cf_next) {
+               if (!file->cf_wants_starting)
+                       continue;
+
+               file->cf_wants_starting = 0;
+               for (line = file->cf_lines; line; line = line->cl_next) {
+                       pid_t pid;
+                       if (line->cl_pid >= 0)
+                               continue;
+
+                       start_one_job(file->cf_username, line);
+                       pid = line->cl_pid;
+                       crondlog(LVL8 "USER %s pid %3d cmd %s",
+                               file->cf_username, (int)pid, line->cl_cmd);
+                       if (pid < 0) {
+                               file->cf_wants_starting = 1;
+                       }
+                       if (pid > 0) {
+                               file->cf_has_running = 1;
+                       }
+               }
+       }
+}
+
+/*
+ * Check for job completion, return number of jobs still running after
+ * all done.
+ */
+static int check_completions(void)
+{
+       CronFile *file;
+       CronLine *line;
+       int num_still_running = 0;
+
+       for (file = G.cron_files; file; file = file->cf_next) {
+               if (!file->cf_has_running)
+                       continue;
+
+               file->cf_has_running = 0;
+               for (line = file->cf_lines; line; line = line->cl_next) {
+                       int r;
+
+                       if (line->cl_pid <= 0)
+                               continue;
+
+                       r = waitpid(line->cl_pid, NULL, WNOHANG);
+                       if (r < 0 || r == line->cl_pid) {
+                               process_finished_job(file->cf_username, line);
+                               if (line->cl_pid == 0) {
+                                       /* sendmail was not started for it */
+                                       continue;
+                               }
+                               /* else: sendmail was started, job is still running, fall thru */
+                       }
+                       /* else: r == 0: "process is still running" */
+                       file->cf_has_running = 1;
+               }
+//FIXME: if !file->cf_has_running && file->deleted: delete it!
+//otherwise deleted entries will stay forever, right?
+               num_still_running += file->cf_has_running;
+       }
+       return num_still_running;
 }
 
-#endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */
+int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int crond_main(int argc UNUSED_PARAM, char **argv)
+{
+       time_t t2;
+       int rescan;
+       int sleep_time;
+       unsigned opts;
+
+       INIT_G();
+
+       /* "-b after -f is ignored", and so on for every pair a-b */
+       opt_complementary = "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l")
+                       /* -l and -d have numeric param */
+                       ":l+" IF_FEATURE_CROND_D(":d+");
+       opts = getopt32(argv, "l:L:fbSc:" IF_FEATURE_CROND_D("d:"),
+                       &G.log_level, &G.log_filename, &G.crontab_dir_name
+                       IF_FEATURE_CROND_D(,&G.log_level));
+       /* both -d N and -l N set the same variable: G.log_level */
+
+       if (!(opts & OPT_f)) {
+               /* close stdin, stdout, stderr.
+                * close unused descriptors - don't need them. */
+               bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
+       }
+
+       if (!(opts & OPT_d) && G.log_filename == NULL) {
+               /* logging to syslog */
+               openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);
+               logmode = LOGMODE_SYSLOG;
+       }
+
+       xchdir(G.crontab_dir_name);
+       //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */
+       xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */
+       crondlog(LVL8 "crond (busybox "BB_VER") started, log level %d", G.log_level);
+       rescan_crontab_dir();
+       write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid");
+
+       /* Main loop */
+       t2 = time(NULL);
+       rescan = 60;
+       sleep_time = 60;
+       for (;;) {
+               struct stat sbuf;
+               time_t t1;
+               long dt;
+
+               t1 = t2;
+
+               /* Synchronize to 1 minute, minimum 1 second */
+               sleep(sleep_time - (time(NULL) % sleep_time) + 1);
+
+               t2 = time(NULL);
+               dt = (long)t2 - (long)t1;
+
+               /*
+                * The file 'cron.update' is checked to determine new cron
+                * jobs.  The directory is rescanned once an hour to deal
+                * with any screwups.
+                *
+                * Check for time jump.  Disparities over an hour either way
+                * result in resynchronization.  A negative disparity
+                * less than an hour causes us to effectively sleep until we
+                * match the original time (i.e. no re-execution of jobs that
+                * have just been run).  A positive disparity less than
+                * an hour causes intermediate jobs to be run, but only once
+                * in the worst case.
+                *
+                * When running jobs, the inequality used is greater but not
+                * equal to t1, and less then or equal to t2.
+                */
+               if (stat(G.crontab_dir_name, &sbuf) != 0)
+                       sbuf.st_mtime = 0; /* force update (once) if dir was deleted */
+               if (G.crontab_dir_mtime != sbuf.st_mtime) {
+                       G.crontab_dir_mtime = sbuf.st_mtime;
+                       rescan = 1;
+               }
+               if (--rescan == 0) {
+                       rescan = 60;
+                       rescan_crontab_dir();
+               }
+               process_cron_update_file();
+               if (DebugOpt)
+                       crondlog(LVL5 "wakeup dt=%ld", dt);
+               if (dt < -60 * 60 || dt > 60 * 60) {
+                       crondlog(WARN9 "time disparity of %ld minutes detected", dt / 60);
+                       /* and we do not run any jobs in this case */
+               } else if (dt > 0) {
+                       /* Usual case: time advances forward, as expected */
+                       flag_starting_jobs(t1, t2);
+                       start_jobs();
+                       if (check_completions() > 0) {
+                               /* some jobs are still running */
+                               sleep_time = 10;
+                       } else {
+                               sleep_time = 60;
+                       }
+               }
+               /* else: time jumped back, do not run any jobs */
+       } /* for (;;) */
+
+       return 0; /* not reached */
+}
index b8a5abc..aad242f 100644 (file)
@@ -7,9 +7,19 @@
  * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
  * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define crontab_trivial_usage
+//usage:       "[-c DIR] [-u USER] [-ler]|[FILE]"
+//usage:#define crontab_full_usage "\n\n"
+//usage:       "       -c      Crontab directory"
+//usage:     "\n       -u      User"
+//usage:     "\n       -l      List crontab"
+//usage:     "\n       -e      Edit crontab"
+//usage:     "\n       -r      Delete crontab"
+//usage:     "\n       FILE    Replace crontab by FILE ('-': stdin)"
+
 #include "libbb.h"
 
 #define CRONTABS        CONFIG_FEATURE_CROND_DIR "/crontabs"
@@ -20,8 +30,9 @@
 static void edit_file(const struct passwd *pas, const char *file)
 {
        const char *ptr;
-       int pid = xvfork();
+       pid_t pid;
 
+       pid = xvfork();
        if (pid) { /* parent */
                wait4pid(pid);
                return;
@@ -30,7 +41,7 @@ static void edit_file(const struct passwd *pas, const char *file)
        /* CHILD - change user and run editor */
        /* initgroups, setgid, setuid */
        change_identity(pas);
-       setup_environment(DEFAULT_SHELL,
+       setup_environment(pas->pw_shell,
                        SETUP_ENV_CHANGEENV | SETUP_ENV_TO_TMP,
                        pas);
        ptr = getenv("VISUAL");
@@ -41,29 +52,7 @@ static void edit_file(const struct passwd *pas, const char *file)
        }
 
        BB_EXECLP(ptr, ptr, file, NULL);
-       bb_perror_msg_and_die("exec %s", ptr);
-}
-
-static int open_as_user(const struct passwd *pas, const char *file)
-{
-       pid_t pid;
-       char c;
-
-       pid = xvfork();
-       if (pid) { /* PARENT */
-               if (wait4pid(pid) == 0) {
-                       /* exitcode 0: child says it can read */
-                       return open(file, O_RDONLY);
-               }
-               return -1;
-       }
-
-       /* CHILD */
-       /* initgroups, setgid, setuid */
-       change_identity(pas);
-       /* We just try to read one byte. If it works, file is readable
-        * under this user. We signal that by exiting with 0. */
-       _exit(safe_read(xopen(file, O_RDONLY), &c, 1) < 0);
+       bb_perror_msg_and_die("can't execute '%s'", ptr);
 }
 
 int crontab_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -126,10 +115,7 @@ int crontab_main(int argc UNUSED_PARAM, char **argv)
                if (!argv[0])
                        bb_show_usage();
                if (NOT_LONE_DASH(argv[0])) {
-                       src_fd = open_as_user(pas, argv[0]);
-                       if (src_fd < 0)
-                               bb_error_msg_and_die("user %s cannot read %s",
-                                               pas->pw_name, argv[0]);
+                       src_fd = xopen_as_uid_gid(argv[0], O_RDONLY, pas->pw_uid, pas->pw_gid);
                }
        }
 
index cb4b1e9..6bcfbe2 100644 (file)
@@ -1,12 +1,44 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 #include <math.h>
 
-/* Tiny RPN calculator, because "expr" didn't give me bitwise operations. */
+//usage:#define dc_trivial_usage
+//usage:       "EXPRESSION..."
+//usage:
+//usage:#define dc_full_usage "\n\n"
+//usage:       "Tiny RPN calculator. Operations:\n"
+//usage:       "+, add, -, sub, *, mul, /, div, %, mod, "IF_FEATURE_DC_LIBM("**, exp, ")"and, or, not, xor,\n"
+//usage:       "p - print top of the stack (without popping),\n"
+//usage:       "f - print entire stack,\n"
+//usage:       "o - pop the value and set output radix (must be 10, 16, 8 or 2).\n"
+//usage:       "Examples: 'dc 2 2 add p' -> 4, 'dc 8 8 mul 2 2 + / p' -> 16"
+//usage:
+//usage:#define dc_example_usage
+//usage:       "$ dc 2 2 + p\n"
+//usage:       "4\n"
+//usage:       "$ dc 8 8 \\* 2 2 + / p\n"
+//usage:       "16\n"
+//usage:       "$ dc 0 1 and p\n"
+//usage:       "0\n"
+//usage:       "$ dc 0 1 or p\n"
+//usage:       "1\n"
+//usage:       "$ echo 72 9 div 8 mul p | dc\n"
+//usage:       "64\n"
+
+#if 0
+typedef unsigned data_t;
+#define DATA_FMT ""
+#elif 0
+typedef unsigned long data_t;
+#define DATA_FMT "l"
+#else
+typedef unsigned long long data_t;
+#define DATA_FMT "ll"
+#endif
 
 
 struct globals {
@@ -73,29 +105,29 @@ static void divide(void)
 
 static void mod(void)
 {
-       unsigned d = pop();
+       data_t d = pop();
 
-       push((unsigned) pop() % d);
+       push((data_t) pop() % d);
 }
 
 static void and(void)
 {
-       push((unsigned) pop() & (unsigned) pop());
+       push((data_t) pop() & (data_t) pop());
 }
 
 static void or(void)
 {
-       push((unsigned) pop() | (unsigned) pop());
+       push((data_t) pop() | (data_t) pop());
 }
 
 static void eor(void)
 {
-       push((unsigned) pop() ^ (unsigned) pop());
+       push((data_t) pop() ^ (data_t) pop());
 }
 
 static void not(void)
 {
-       push(~(unsigned) pop());
+       push(~(data_t) pop());
 }
 
 static void set_output_base(void)
@@ -112,25 +144,30 @@ static void set_output_base(void)
 
 static void print_base(double print)
 {
-       unsigned x, i;
+       data_t x, i;
 
+       x = (data_t) print;
        if (base == 10) {
-               printf("%g\n", print);
+               if (x == print) /* exactly representable as unsigned integer */
+                       printf("%"DATA_FMT"u\n", x);
+               else
+                       printf("%g\n", print);
                return;
        }
 
-       x = (unsigned)print;
        switch (base) {
        case 16:
-               printf("%x\n", x);
+               printf("%"DATA_FMT"x\n", x);
                break;
        case 8:
-               printf("%o\n", x);
+               printf("%"DATA_FMT"o\n", x);
                break;
        default: /* base 2 */
-               i = (unsigned)INT_MAX + 1;
+               i = MAXINT(data_t) - (MAXINT(data_t) >> 1);
+               /* i is 100000...00000 */
                do {
-                       if (x & i) break;
+                       if (x & i)
+                               break;
                        i >>= 1;
                } while (i > 1);
                do {
@@ -182,43 +219,30 @@ static const struct op operators[] = {
        {"p", print_no_pop},
        {"f", print_stack_no_pop},
        {"o", set_output_base},
-       { "", NULL }
 };
 
 static void stack_machine(const char *argument)
 {
-       char *endPointer;
+       char *end;
        double d;
-       const struct op *o = operators;
-
-       d = strtod(argument, &endPointer);
+       const struct op *o;
 
-       if (endPointer != argument && *endPointer == '\0') {
+       d = strtod(argument, &end);
+       if (end != argument && *end == '\0') {
                push(d);
                return;
        }
 
-       while (o->function) {
+       o = operators;
+       do {
                if (strcmp(o->name, argument) == 0) {
                        o->function();
                        return;
                }
                o++;
-       }
-       bb_error_msg_and_die("syntax error at '%s'", argument);
-}
+       } while (o != operators + ARRAY_SIZE(operators));
 
-/* return pointer to next token in buffer and set *buffer to one char
- * past the end of the above mentioned token
- */
-static char *get_token(char **buffer)
-{
-       char *current = skip_whitespace(*buffer);
-       if (*current != '\0') {
-               *buffer = skip_non_whitespace(current);
-               return current;
-       }
-       return NULL;
+       bb_error_msg_and_die("syntax error at '%s'", argument);
 }
 
 int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -235,16 +259,18 @@ int dc_main(int argc UNUSED_PARAM, char **argv)
                while ((line = xmalloc_fgetline(stdin)) != NULL) {
                        cursor = line;
                        while (1) {
-                               token = get_token(&cursor);
-                               if (!token)
+                               token = skip_whitespace(cursor);
+                               if (*token == '\0')
                                        break;
-                               *cursor++ = '\0';
+                               cursor = skip_non_whitespace(token);
+                               if (*cursor != '\0')
+                                       *cursor++ = '\0';
                                stack_machine(token);
                        }
                        free(line);
                }
        } else {
-               // why? it breaks "dc -2 2 * p"
+               // why? it breaks "dc -2 2 + p"
                //if (argv[0][0] == '-')
                //      bb_show_usage();
                do {
index 4ccb76d..96ffe07 100644 (file)
@@ -1,6 +1,6 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*
     The postal address is:
       Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
 */
+
+//usage:#define devfsd_trivial_usage
+//usage:       "mntpnt [-v]" IF_DEVFSD_FG_NP("[-fg][-np]")
+//usage:#define devfsd_full_usage "\n\n"
+//usage:       "Manage devfs permissions and old device name symlinks\n"
+//usage:     "\n       mntpnt  The mount point where devfs is mounted"
+//usage:     "\n       -v      Print the protocol version numbers for devfsd"
+//usage:     "\n               and the kernel-side protocol version and exit"
+//usage:       IF_DEVFSD_FG_NP(
+//usage:     "\n       -fg     Run in foreground"
+//usage:     "\n       -np     Exit after parsing the configuration file"
+//usage:     "\n               and processing synthetic REGISTER events,"
+//usage:     "\n               don't poll for events"
+//usage:       )
+
 #include "libbb.h"
 #include "xregex.h"
 #include <syslog.h>
@@ -75,7 +90,7 @@
 
 /* Various defines taken from linux/devfs_fs.h */
 #define DEVFSD_PROTOCOL_REVISION_KERNEL  5
-#define        DEVFSD_IOCTL_BASE       'd'
+#define DEVFSD_IOCTL_BASE      'd'
 /*  These are the various ioctls  */
 #define DEVFSDIOC_GET_PROTO_REV         _IOR(DEVFSD_IOCTL_BASE, 0, int)
 #define DEVFSDIOC_SET_EVENT_MASK        _IOW(DEVFSD_IOCTL_BASE, 2, int)
@@ -204,7 +219,7 @@ static void action_execute(const struct devfsd_notify_struct *, const struct con
                                                        const regmatch_t *, unsigned);
 static void action_modload(const struct devfsd_notify_struct *info, const struct config_entry_struct *entry);
 static void action_copy(const struct devfsd_notify_struct *, const struct config_entry_struct *,
-                                                const regmatch_t *, unsigned);
+                                               const regmatch_t *, unsigned);
 static void action_compat(const struct devfsd_notify_struct *, unsigned);
 static void free_config(void);
 static void restore(char *spath, struct stat source_stat, int rootlen);
@@ -214,12 +229,12 @@ static void signal_handler(int);
 static const char *get_variable(const char *, void *);
 static int make_dir_tree(const char *);
 static int expand_expression(char *, unsigned, const char *, const char *(*)(const char *, void *), void *,
-                                                        const char *, const regmatch_t *, unsigned);
+                                                       const char *, const regmatch_t *, unsigned);
 static void expand_regexp(char *, size_t, const char *, const char *, const regmatch_t *, unsigned);
 static const char *expand_variable(    char *, unsigned, unsigned *, const char *,
                                                                        const char *(*)(const char *, void *), void *);
 static const char *get_variable_v2(const char *, const char *(*)(const char *, void *), void *);
-static char get_old_ide_name(unsigned , unsigned);
+static char get_old_ide_name(unsigned, unsigned);
 static char *write_old_sd_name(char *, unsigned, unsigned, const char *);
 
 /* busybox functions */
@@ -565,9 +580,9 @@ static void process_config_line(const char *line, unsigned long *event_mask)
                        /*This  action will pass "/dev/$devname"(i.e. "/dev/" prefixed to
                        the device name) to the module loading  facility.  In  addition,
                        the /etc/modules.devfs configuration file is used.*/
-                        if (ENABLE_DEVFSD_MODLOAD)
+                       if (ENABLE_DEVFSD_MODLOAD)
                                new->action.what = AC_MODLOAD;
-                        break;
+                       break;
                case 6: /* EXECUTE */
                        new->action.what = AC_EXECUTE;
                        num_args -= 3;
@@ -735,7 +750,7 @@ static void action_permissions(const struct devfsd_notify_struct *info,
 }   /*  End Function action_permissions  */
 
 static void action_modload(const struct devfsd_notify_struct *info,
-                           const struct config_entry_struct *entry UNUSED_PARAM)
+                       const struct config_entry_struct *entry UNUSED_PARAM)
 /*  [SUMMARY] Load a module.
     <info> The devfs change.
     <entry> The config file entry.
@@ -756,8 +771,8 @@ static void action_modload(const struct devfsd_notify_struct *info,
 }  /*  End Function action_modload  */
 
 static void action_execute(const struct devfsd_notify_struct *info,
-                           const struct config_entry_struct *entry,
-                           const regmatch_t *regexpr, unsigned int numexpr)
+                       const struct config_entry_struct *entry,
+                       const regmatch_t *regexpr, unsigned int numexpr)
 /*  [SUMMARY] Execute a programme.
     <info> The devfs change.
     <entry> The config file entry.
@@ -788,8 +803,8 @@ static void action_execute(const struct devfsd_notify_struct *info,
 
 
 static void action_copy(const struct devfsd_notify_struct *info,
-                        const struct config_entry_struct *entry,
-                        const regmatch_t *regexpr, unsigned int numexpr)
+                       const struct config_entry_struct *entry,
+                       const regmatch_t *regexpr, unsigned int numexpr)
 /*  [SUMMARY] Copy permissions.
     <info> The devfs change.
     <entry> The config file entry.
@@ -1068,21 +1083,23 @@ static int get_uid_gid(int flag, const char *string)
 {
        struct passwd *pw_ent;
        struct group *grp_ent;
-       static const char *msg;
-
-       if (ENABLE_DEVFSD_VERBOSE)
-               msg = "user";
+       const char *msg;
 
-       if (isdigit(string[0]) ||((string[0] == '-') && isdigit(string[1])))
+       if (isdigit(string[0]) || ((string[0] == '-') && isdigit(string[1])))
                return atoi(string);
 
        if (flag == UID && (pw_ent = getpwnam(string)) != NULL)
                return pw_ent->pw_uid;
 
-       if (flag == GID && (grp_ent = getgrnam(string)) != NULL)
-               return grp_ent->gr_gid;
-       else if (ENABLE_DEVFSD_VERBOSE)
-               msg = "group";
+       if (ENABLE_DEVFSD_VERBOSE)
+               msg = "user";
+
+       if (flag == GID) {
+               if ((grp_ent = getgrnam(string)) != NULL)
+                       return grp_ent->gr_gid;
+               if (ENABLE_DEVFSD_VERBOSE)
+                       msg = "group";
+       }
 
        if (ENABLE_DEVFSD_VERBOSE)
                msg_logger(LOG_ERR, "unknown %s: %s, defaulting to %cid=0",  msg, string, msg[0]);
@@ -1244,11 +1261,11 @@ static int make_dir_tree(const char *path)
 } /*  End Function make_dir_tree  */
 
 static int expand_expression(char *output, unsigned int outsize,
-                             const char *input,
-                             const char *(*get_variable_func)(const char *variable, void *info),
-                             void *info,
-                             const char *devname,
-                             const regmatch_t *ex, unsigned int numexp)
+                       const char *input,
+                       const char *(*get_variable_func)(const char *variable, void *info),
+                       void *info,
+                       const char *devname,
+                       const regmatch_t *ex, unsigned int numexp)
 /*  [SUMMARY] Expand environment variables and regular subexpressions in string.
     <output> The output expanded expression is written here.
     <length> The size of the output buffer.
@@ -1273,8 +1290,8 @@ static int expand_expression(char *output, unsigned int outsize,
 }   /*  End Function expand_expression  */
 
 static void expand_regexp(char *output, size_t outsize, const char *input,
-                          const char *devname,
-                          const regmatch_t *ex, unsigned int numex)
+                       const char *devname,
+                       const regmatch_t *ex, unsigned int numex)
 /*  [SUMMARY] Expand all occurrences of the regular subexpressions \0 to \9.
     <output> The output expanded expression is written here.
     <outsize> The size of the output buffer.
@@ -1370,7 +1387,7 @@ static struct translate_struct translate_table[] =
 };
 
 const char *get_old_name(const char *devname, unsigned int namelen,
-                         char *buffer, unsigned int major, unsigned int minor)
+                       char *buffer, unsigned int major, unsigned int minor)
 /*  [SUMMARY] Translate a kernel-supplied name into an old name.
     <devname> The device name provided by the kernel.
     <namelen> The length of the name.
@@ -1408,7 +1425,7 @@ const char *get_old_name(const char *devname, unsigned int namelen,
        };
 
        for (trans = translate_table; trans->match != NULL; ++trans) {
-                len = strlen(trans->match);
+               len = strlen(trans->match);
 
                if (strncmp(devname, trans->match, len) == 0) {
                        if (trans->format == NULL)
@@ -1534,9 +1551,9 @@ static char *write_old_sd_name(char *buffer,
 /*EXPERIMENTAL_FUNCTION*/
 
 int st_expr_expand(char *output, unsigned int length, const char *input,
-                    const char *(*get_variable_func)(const char *variable,
-                                                 void *info),
-                    void *info)
+               const char *(*get_variable_func)(const char *variable,
+                                               void *info),
+               void *info)
 /*  [SUMMARY] Expand an expression using Borne Shell-like unquoted rules.
     <output> The output expanded expression is written here.
     <length> The size of the output buffer.
@@ -1626,10 +1643,10 @@ st_expr_expand_out:
 /*  Private functions follow  */
 
 static const char *expand_variable(char *buffer, unsigned int length,
-                                   unsigned int *out_pos, const char *input,
-                                   const char *(*func)(const char *variable,
-                                                        void *info),
-                                   void *info)
+                               unsigned int *out_pos, const char *input,
+                               const char *(*func)(const char *variable,
+                                                       void *info),
+                               void *info)
 /*  [SUMMARY] Expand a variable.
     <buffer> The buffer to write to.
     <length> The length of the output buffer.
@@ -1771,8 +1788,8 @@ expand_variable_out:
 
 
 static const char *get_variable_v2(const char *variable,
-                                 const char *(*func)(const char *variable, void *info),
-                                void *info)
+                               const char *(*func)(const char *variable, void *info),
+                               void *info)
 /*  [SUMMARY] Get a variable from the environment or .
     <variable> The variable name.
     <func> A function which will be used to get the variable. If this returns
index 39b5808..786a21b 100644 (file)
@@ -1,9 +1,17 @@
 /*
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *  Copyright (C) 2000, Jan-Derk Bakker (J.D.Bakker@its.tudelft.nl)
  *  Copyright (C) 2008, BusyBox Team. -solar 4/26/08
  */
 
+//usage:#define devmem_trivial_usage
+//usage:       "ADDRESS [WIDTH [VALUE]]"
+//usage:#define devmem_full_usage "\n\n"
+//usage:       "Read/write from physical address\n"
+//usage:     "\n       ADDRESS Address to act upon"
+//usage:     "\n       WIDTH   Width (8/16/...)"
+//usage:     "\n       VALUE   Data to be written"
+
 #include "libbb.h"
 
 int devmem_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
index a869c63..a20e04b 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (C) 2004  Peter Willis <psyphreak@phreaker.net>
  * Copyright (C) 2005  Tito Ragusa <farmatito@tiscali.it>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*
  * Most of the dirty work blatantly ripped off from cat.c =)
  */
 
+//usage:#define eject_trivial_usage
+//usage:       "[-t] [-T] [DEVICE]"
+//usage:#define eject_full_usage "\n\n"
+//usage:       "Eject DEVICE or default /dev/cdrom\n"
+//usage:       IF_FEATURE_EJECT_SCSI(
+//usage:     "\n       -s      SCSI device"
+//usage:       )
+//usage:     "\n       -t      Close tray"
+//usage:     "\n       -T      Open/close tray (toggle)"
+
 #include <sys/mount.h>
 #include "libbb.h"
 /* Must be after libbb.h: they need size_t */
index e370d20..12a77b7 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2008 Michele Sanges <michele.sanges@gmail.com>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Usage:
  * - use kernel option 'vga=xxx' or otherwise enable framebuffer device.
  *   "exit" (or just close fifo) - well you guessed it.
  */
 
+//usage:#define fbsplash_trivial_usage
+//usage:       "-s IMGFILE [-c] [-d DEV] [-i INIFILE] [-f CMD]"
+//usage:#define fbsplash_full_usage "\n\n"
+//usage:       "       -s      Image"
+//usage:     "\n       -c      Hide cursor"
+//usage:     "\n       -d      Framebuffer device (default /dev/fb0)"
+//usage:     "\n       -i      Config file (var=value):"
+//usage:     "\n                       BAR_LEFT,BAR_TOP,BAR_WIDTH,BAR_HEIGHT"
+//usage:     "\n                       BAR_R,BAR_G,BAR_B"
+//usage:     "\n       -f      Control pipe (else exit after drawing image)"
+//usage:     "\n                       commands: 'NN' (% for progress bar) or 'exit'"
+
 #include "libbb.h"
 #include <linux/fb.h>
 
 /* If you want logging messages on /tmp/fbsplash.log... */
 #define DEBUG 0
 
-#define BYTES_PER_PIXEL 2
-
-typedef unsigned short DATA;
-
 struct globals {
 #if DEBUG
        bool bdebug_messages;   // enable/disable logging
@@ -41,6 +49,11 @@ struct globals {
        const char *image_filename;
        struct fb_var_screeninfo scr_var;
        struct fb_fix_screeninfo scr_fix;
+       unsigned bytes_per_pixel;
+       // cached (8 - scr_var.COLOR.length):
+       unsigned red_shift;
+       unsigned green_shift;
+       unsigned blue_shift;
 };
 #define G (*ptr_to_globals)
 #define INIT_G() do { \
@@ -65,9 +78,46 @@ struct globals {
 #define DEBUG_MESSAGE(...) ((void)0)
 #endif
 
+/**
+ * Configure palette for RGB:332
+ */
+static void fb_setpal(int fd)
+{
+       struct fb_cmap cmap;
+       /* fb colors are 16 bit */
+       unsigned short red[256], green[256], blue[256];
+       unsigned i;
+
+       /* RGB:332 */
+       for (i = 0; i < 256; i++) {
+               /* Color is encoded in pixel value as rrrgggbb.
+                * 3-bit color is mapped to 16-bit one as:
+                * 000 -> 00000000 00000000
+                * 001 -> 00100100 10010010
+                * ...
+                * 011 -> 01101101 10110110
+                * 100 -> 10010010 01001001
+                * ...
+                * 111 -> 11111111 11111111
+                */
+               red[i]   = (( i >> 5       ) * 0x9249) >> 2; // rrr * 00 10010010 01001001 >> 2
+               green[i] = (((i >> 2) & 0x7) * 0x9249) >> 2; // ggg * 00 10010010 01001001 >> 2
+               /* 2-bit color is easier: */
+               blue[i]  =  ( i       & 0x3) * 0x5555; // bb * 01010101 01010101
+       }
+
+       cmap.start = 0;
+       cmap.len   = 256;
+       cmap.red   = red;
+       cmap.green = green;
+       cmap.blue  = blue;
+       cmap.transp = 0;
+
+       xioctl(fd, FBIOPUTCMAP, &cmap);
+}
 
 /**
- *     Open and initialize the framebuffer device
+ * Open and initialize the framebuffer device
  * \param *strfb_device pointer to framebuffer device
  */
 static void fb_open(const char *strfb_device)
@@ -78,59 +128,133 @@ static void fb_open(const char *strfb_device)
        xioctl(fbfd, FBIOGET_VSCREENINFO, &G.scr_var);
        xioctl(fbfd, FBIOGET_FSCREENINFO, &G.scr_fix);
 
-       if (G.scr_var.bits_per_pixel != 16)
-               bb_error_msg_and_die("only 16 bpp is supported");
+       switch (G.scr_var.bits_per_pixel) {
+       case 8:
+               fb_setpal(fbfd);
+               break;
+
+       case 16:
+       case 24:
+       case 32:
+               break;
+
+       default:
+               bb_error_msg_and_die("unsupported %u bpp", (int)G.scr_var.bits_per_pixel);
+               break;
+       }
+
+       G.red_shift   = 8 - G.scr_var.red.length;
+       G.green_shift = 8 - G.scr_var.green.length;
+       G.blue_shift  = 8 - G.scr_var.blue.length;
+       G.bytes_per_pixel = (G.scr_var.bits_per_pixel + 7) >> 3;
 
        // map the device in memory
        G.addr = mmap(NULL,
-                       G.scr_var.xres * G.scr_var.yres
-                       * BYTES_PER_PIXEL /*(G.scr_var.bits_per_pixel / 8)*/,
+                       G.scr_var.yres * G.scr_fix.line_length,
                        PROT_WRITE, MAP_SHARED, fbfd, 0);
        if (G.addr == MAP_FAILED)
                bb_perror_msg_and_die("mmap");
+
+       // point to the start of the visible screen
+       G.addr += G.scr_var.yoffset * G.scr_fix.line_length + G.scr_var.xoffset * G.bytes_per_pixel;
        close(fbfd);
 }
 
 
 /**
- *     Draw hollow rectangle on framebuffer
+ * Return pixel value of the passed RGB color.
+ * This is performance critical fn.
+ */
+static unsigned fb_pixel_value(unsigned r, unsigned g, unsigned b)
+{
+       /* We assume that the r,g,b values are <= 255 */
+
+       if (G.bytes_per_pixel == 1) {
+               r = r        & 0xe0; // 3-bit red
+               g = (g >> 3) & 0x1c; // 3-bit green
+               b =  b >> 6;         // 2-bit blue
+               return r + g + b;
+       }
+       if (G.bytes_per_pixel == 2) {
+               // ARM PL110 on Integrator/CP has RGBA5551 bit arrangement.
+               // We want to support bit locations like that.
+               //
+               // First shift out unused bits
+               r = r >> G.red_shift;
+               g = g >> G.green_shift;
+               b = b >> G.blue_shift;
+               // Then shift the remaining bits to their offset
+               return (r << G.scr_var.red.offset) +
+                       (g << G.scr_var.green.offset) +
+                       (b << G.scr_var.blue.offset);
+       }
+       // RGB 888
+       return b + (g << 8) + (r << 16);
+}
+
+/**
+ * Draw pixel on framebuffer
+ */
+static void fb_write_pixel(unsigned char *addr, unsigned pixel)
+{
+       switch (G.bytes_per_pixel) {
+       case 1:
+               *addr = pixel;
+               break;
+       case 2:
+               *(uint16_t *)addr = pixel;
+               break;
+       case 4:
+               *(uint32_t *)addr = pixel;
+               break;
+       default: // 24 bits per pixel
+               addr[0] = pixel;
+               addr[1] = pixel >> 8;
+               addr[2] = pixel >> 16;
+       }
+}
+
+
+/**
+ * Draw hollow rectangle on framebuffer
  */
 static void fb_drawrectangle(void)
 {
        int cnt;
-       DATA thispix;
-       DATA *ptr1, *ptr2;
+       unsigned thispix;
+       unsigned char *ptr1, *ptr2;
        unsigned char nred = G.nbar_colr/2;
        unsigned char ngreen =  G.nbar_colg/2;
        unsigned char nblue = G.nbar_colb/2;
 
-       nred   >>= 3;  // 5-bit red
-       ngreen >>= 2;  // 6-bit green
-       nblue  >>= 3;  // 5-bit blue
-       thispix = nblue + (ngreen << 5) + (nred << (5+6));
+       thispix = fb_pixel_value(nred, ngreen, nblue);
 
        // horizontal lines
-       ptr1 = (DATA*)(G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx) * BYTES_PER_PIXEL);
-       ptr2 = (DATA*)(G.addr + ((G.nbar_posy + G.nbar_height - 1) * G.scr_var.xres + G.nbar_posx) * BYTES_PER_PIXEL);
+       ptr1 = G.addr + G.nbar_posy * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel;
+       ptr2 = G.addr + (G.nbar_posy + G.nbar_height - 1) * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel;
        cnt = G.nbar_width - 1;
        do {
-               *ptr1++ = thispix;
-               *ptr2++ = thispix;
+               fb_write_pixel(ptr1, thispix);
+               fb_write_pixel(ptr2, thispix);
+               ptr1 += G.bytes_per_pixel;
+               ptr2 += G.bytes_per_pixel;
        } while (--cnt >= 0);
 
        // vertical lines
-       ptr1 = (DATA*)(G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx) * BYTES_PER_PIXEL);
-       ptr2 = (DATA*)(G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx + G.nbar_width - 1) * BYTES_PER_PIXEL);
+       ptr1 = G.addr + G.nbar_posy * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel;
+       ptr2 = G.addr + G.nbar_posy * G.scr_fix.line_length + (G.nbar_posx + G.nbar_width - 1) * G.bytes_per_pixel;
        cnt = G.nbar_height - 1;
        do {
-               *ptr1 = thispix; ptr1 += G.scr_var.xres;
-               *ptr2 = thispix; ptr2 += G.scr_var.xres;
+               fb_write_pixel(ptr1, thispix);
+               fb_write_pixel(ptr2, thispix);
+               ptr1 += G.scr_fix.line_length;
+               ptr2 += G.scr_fix.line_length;
        } while (--cnt >= 0);
 }
 
 
 /**
- *     Draw filled rectangle on framebuffer
+ * Draw filled rectangle on framebuffer
  * \param nx1pos,ny1pos upper left position
  * \param nx2pos,ny2pos down right position
  * \param nred,ngreen,nblue rgb color
@@ -139,21 +263,19 @@ static void fb_drawfullrectangle(int nx1pos, int ny1pos, int nx2pos, int ny2pos,
        unsigned char nred, unsigned char ngreen, unsigned char nblue)
 {
        int cnt1, cnt2, nypos;
-       DATA thispix;
-       DATA *ptr;
+       unsigned thispix;
+       unsigned char *ptr;
 
-       nred   >>= 3;  // 5-bit red
-       ngreen >>= 2;  // 6-bit green
-       nblue  >>= 3;  // 5-bit blue
-       thispix = nblue + (ngreen << 5) + (nred << (5+6));
+       thispix = fb_pixel_value(nred, ngreen, nblue);
 
        cnt1 = ny2pos - ny1pos;
        nypos = ny1pos;
        do {
-               ptr = (DATA*)(G.addr + (nypos * G.scr_var.xres + nx1pos) * BYTES_PER_PIXEL);
+               ptr = G.addr + nypos * G.scr_fix.line_length + nx1pos * G.bytes_per_pixel;
                cnt2 = nx2pos - nx1pos;
                do {
-                       *ptr++ = thispix;
+                       fb_write_pixel(ptr, thispix);
+                       ptr += G.bytes_per_pixel;
                } while (--cnt2 >= 0);
 
                nypos++;
@@ -162,19 +284,20 @@ static void fb_drawfullrectangle(int nx1pos, int ny1pos, int nx2pos, int ny2pos,
 
 
 /**
- *     Draw a progress bar on framebuffer
+ * Draw a progress bar on framebuffer
  * \param percent percentage of loading
  */
 static void fb_drawprogressbar(unsigned percent)
 {
-       int i, left_x, top_y, width, height;
+       int left_x, top_y, pos_x;
+       unsigned width, height;
 
        // outer box
        left_x = G.nbar_posx;
        top_y = G.nbar_posy;
        width = G.nbar_width - 1;
        height = G.nbar_height - 1;
-       if ((height | width) < 0)
+       if ((int)(height | width) < 0)
                return;
        // NB: "width" of 1 actually makes rect with width of 2!
        fb_drawrectangle();
@@ -184,35 +307,41 @@ static void fb_drawprogressbar(unsigned percent)
        top_y++;
        width -= 2;
        height -= 2;
-       if ((height | width) < 0)
+       if ((int)(height | width) < 0)
                return;
-       fb_drawfullrectangle(
-                       left_x, top_y,
-                                       left_x + width, top_y + height,
-                       G.nbar_colr, G.nbar_colg, G.nbar_colb);
 
+       pos_x = left_x;
        if (percent > 0) {
+               int i, y;
+
                // actual progress bar
-               width = width * percent / 100;
+               pos_x += (unsigned)(width * percent) / 100;
+
+               y = top_y;
                i = height;
                if (height == 0)
                        height++; // divide by 0 is bad
                while (i >= 0) {
                        // draw one-line thick "rectangle"
                        // top line will have gray lvl 200, bottom one 100
-                       unsigned gray_level = 100 + i*100/height;
+                       unsigned gray_level = 100 + (unsigned)i*100 / height;
                        fb_drawfullrectangle(
-                                       left_x, top_y, left_x + width, top_y,
+                                       left_x, y, pos_x, y,
                                        gray_level, gray_level, gray_level);
-                       top_y++;
+                       y++;
                        i--;
                }
        }
+
+       fb_drawfullrectangle(
+                       pos_x, top_y,
+                       left_x + width, top_y + height,
+                       G.nbar_colr, G.nbar_colg, G.nbar_colb);
 }
 
 
 /**
- *     Draw image from PPM file
+ * Draw image from PPM file
  */
 static void fb_drawimage(void)
 {
@@ -273,18 +402,16 @@ static void fb_drawimage(void)
                height = G.scr_var.yres;
        for (j = 0; j < height; j++) {
                unsigned char *pixel;
-               DATA *src;
+               unsigned char *src;
 
                if (fread(pixline, 1, line_size, theme_file) != line_size)
                        bb_error_msg_and_die("bad PPM file '%s'", G.image_filename);
                pixel = pixline;
-               src = (DATA *)(G.addr + j * G.scr_fix.line_length);
+               src = G.addr + j * G.scr_fix.line_length;
                for (i = 0; i < width; i++) {
-                       unsigned thispix;
-                       thispix = (((unsigned)pixel[0] << 8) & 0xf800)
-                               | (((unsigned)pixel[1] << 3) & 0x07e0)
-                               | (((unsigned)pixel[2] >> 3));
-                       *src++ = thispix;
+                       unsigned thispix = fb_pixel_value(pixel[0], pixel[1], pixel[2]);
+                       fb_write_pixel(src, thispix);
+                       src += G.bytes_per_pixel;
                        pixel += 3;
                }
        }
@@ -294,7 +421,7 @@ static void fb_drawimage(void)
 
 
 /**
- *     Parse configuration file
+ * Parse configuration file
  * \param *cfg_filename name of the configuration file
  */
 static void init(const char *cfg_filename)
@@ -311,7 +438,7 @@ static void init(const char *cfg_filename)
        parser_t *parser = config_open2(cfg_filename, xfopen_stdin);
        while (config_read(parser, token, 2, 2, "#=",
                                (PARSE_NORMAL | PARSE_MIN_DIE) & ~(PARSE_TRIM | PARSE_COLLAPSE))) {
-               unsigned val = xatoi_u(token[1]);
+               unsigned val = xatoi_positive(token[1]);
                int i = index_in_strings(param_names, token[0]);
                if (i < 0)
                        bb_error_msg_and_die("syntax error: %s", token[0]);
index ca00a13..bf9b739 100644 (file)
@@ -7,17 +7,25 @@
  *
  * Renamed to flash_eraseall.c
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define flash_eraseall_trivial_usage
+//usage:       "[-jNq] MTD_DEVICE"
+//usage:#define flash_eraseall_full_usage "\n\n"
+//usage:       "Erase an MTD device\n"
+//usage:     "\n       -j      Format the device for jffs2"
+//usage:     "\n       -N      Don't skip bad blocks"
+//usage:     "\n       -q      Don't display progress messages"
+
 #include "libbb.h"
 #include <mtd/mtd-user.h>
 #include <linux/jffs2.h>
 
-#define OPTION_J       (1 << 0)
-#define OPTION_Q       (1 << 1)
-#define IS_NAND                (1 << 2)
-#define BBTEST         (1 << 3)
+#define OPTION_J  (1 << 0)
+#define OPTION_N  (1 << 1)
+#define OPTION_Q  (1 << 2)
+#define IS_NAND   (1 << 3)
 
 /* mtd/jffs2-user.h used to have this atrocity:
 extern int target_endian;
@@ -42,15 +50,6 @@ but mtd/jffs2-user.h is gone now (at least 2.6.31.6 does not have it anymore)
 #define cpu_to_je16(v) ((jint16_t){(v)})
 #define cpu_to_je32(v) ((jint32_t){(v)})
 
-static uint32_t crc32(uint32_t val, const void *ss, int len,
-               uint32_t *crc32_table)
-{
-       const unsigned char *s = ss;
-       while (--len >= 0)
-               val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
-       return val;
-}
-
 static void show_progress(mtd_info_t *meminfo, erase_info_t *erase)
 {
        printf("\rErasing %u Kibyte @ %x - %2u%% complete.",
@@ -73,7 +72,7 @@ int flash_eraseall_main(int argc UNUSED_PARAM, char **argv)
        char *mtd_name;
 
        opt_complementary = "=1";
-       flags = BBTEST | getopt32(argv, "jq");
+       flags = getopt32(argv, "jNq");
 
        mtd_name = argv[optind];
        fd = xopen(mtd_name, O_RDWR);
@@ -131,8 +130,9 @@ int flash_eraseall_main(int argc UNUSED_PARAM, char **argv)
                        cleanmarker.totlen = cpu_to_je32(8);
                }
 
-               cleanmarker.hdr_crc = cpu_to_je32(crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node) - 4,
-                                       crc32_table));
+               cleanmarker.hdr_crc = cpu_to_je32(
+                       crc32_block_endian0(0, &cleanmarker, sizeof(struct jffs2_unknown_node) - 4, crc32_table)
+               );
        }
 
        /* Don't want to destroy progress indicator by bb_error_msg's */
@@ -140,7 +140,7 @@ int flash_eraseall_main(int argc UNUSED_PARAM, char **argv)
 
        for (erase.start = 0; erase.start < meminfo.size;
             erase.start += meminfo.erasesize) {
-               if (flags & BBTEST) {
+               if (!(flags & OPTION_N)) {
                        int ret;
                        loff_t offset = erase.start;
 
@@ -155,7 +155,7 @@ int flash_eraseall_main(int argc UNUSED_PARAM, char **argv)
                                 * types e.g. NOR
                                 */
                                if (errno == EOPNOTSUPP) {
-                                       flags &= ~BBTEST;
+                                       flags |= OPTION_N;
                                        if (flags & IS_NAND)
                                                bb_error_msg_and_die("bad block check not available");
                                } else {
index f4e2f73..1fefd95 100644 (file)
@@ -1,8 +1,20 @@
 /* vi: set sw=4 ts=4: */
 /* Ported to busybox from mtd-utils.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define flash_lock_trivial_usage
+//usage:       "MTD_DEVICE OFFSET SECTORS"
+//usage:#define flash_lock_full_usage "\n\n"
+//usage:       "Lock part or all of an MTD device. If SECTORS is -1, then all sectors\n"
+//usage:       "will be locked, regardless of the value of OFFSET"
+//usage:
+//usage:#define flash_unlock_trivial_usage
+//usage:       "MTD_DEVICE"
+//usage:#define flash_unlock_full_usage "\n\n"
+//usage:       "Unlock an MTD device"
+
 #include "libbb.h"
 #include <mtd/mtd-user.h>
 
index 9472c75..b526566 100644 (file)
@@ -4,12 +4,19 @@
  *
  * (C) 2009 Stefan Seyfried <seife@sphairon.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#define flashcp_trivial_usage
+//usage:       "-v FILE MTD_DEVICE"
+//usage:#define flashcp_full_usage "\n\n"
+//usage:       "Copy an image to MTD device\n"
+//usage:     "\n       -v      Verbose"
+
 #include "libbb.h"
 #include <mtd/mtd-user.h>
 
+/* If 1, simulates "flashing" by writing to existing regular file */
 #define MTD_DEBUG 0
 
 #define OPT_v (1 << 0)
@@ -26,7 +33,7 @@ static void progress(int mode, uoff_t count, uoff_t total)
        if (total)
                percent = (unsigned) (percent / total);
        printf("\r%s: %"OFF_FMT"u/%"OFF_FMT"u (%u%%) ",
-               (mode == 0) ? "Erasing block" : ((mode == 1) ? "Writing kb" : "Verifying kb"),
+               (mode < 0) ? "Erasing block" : ((mode == 0) ? "Writing kb" : "Verifying kb"),
                count, total, (unsigned)percent);
        fflush_all();
 }
@@ -44,7 +51,6 @@ int flashcp_main(int argc UNUSED_PARAM, char **argv)
        int fd_f, fd_d; /* input file and mtd device file descriptors */
        int i;
        uoff_t erase_count;
-       unsigned opts;
        struct mtd_info_user mtd;
        struct erase_info_user e;
        struct stat statb;
@@ -53,7 +59,7 @@ int flashcp_main(int argc UNUSED_PARAM, char **argv)
        RESERVE_CONFIG_UBUFFER(buf2, BUFSIZE);
 
        opt_complementary = "=2"; /* exactly 2 non-option args: file, dev */
-       opts = getopt32(argv, "v");
+       /*opts =*/ getopt32(argv, "v");
        argv += optind;
 //     filename = *argv++;
 //     devicename = *argv;
@@ -92,8 +98,7 @@ int flashcp_main(int argc UNUSED_PARAM, char **argv)
 #endif
        e.start = 0;
        for (i = 1; i <= erase_count; i++) {
-               progress(0, i, erase_count);
-               errno = 0;
+               progress(-1, i, erase_count);
 #if !MTD_DEBUG
                if (ioctl(fd_d, MEMERASE, &e) < 0) {
                        bb_perror_msg_and_die("erase error at 0x%llx on %s",
@@ -108,7 +113,7 @@ int flashcp_main(int argc UNUSED_PARAM, char **argv)
 
        /* doing this outer loop gives significantly smaller code
         * than doing two separate loops for writing and verifying */
-       for (i = 1; i <= 2; i++) {
+       for (i = 0; i <= 1; i++) {
                uoff_t done;
                unsigned count;
 
@@ -117,25 +122,29 @@ int flashcp_main(int argc UNUSED_PARAM, char **argv)
                done = 0;
                count = BUFSIZE;
                while (1) {
-                       uoff_t rem = statb.st_size - done;
+                       uoff_t rem;
+
+                       progress(i, done / 1024, (uoff_t)statb.st_size / 1024);
+                       rem = statb.st_size - done;
                        if (rem == 0)
                                break;
                        if (rem < BUFSIZE)
                                count = rem;
-                       progress(i, done / 1024, (uoff_t)statb.st_size / 1024);
                        xread(fd_f, buf, count);
-                       if (i == 1) {
+                       if (i == 0) {
                                int ret;
+                               if (count < BUFSIZE)
+                                       memset((char*)buf + count, 0, BUFSIZE - count);
                                errno = 0;
-                               ret = full_write(fd_d, buf, count);
-                               if (ret != count) {
+                               ret = full_write(fd_d, buf, BUFSIZE);
+                               if (ret != BUFSIZE) {
                                        bb_perror_msg_and_die("write error at 0x%"OFF_FMT"x on %s, "
                                                "write returned %d",
                                                done, devicename, ret);
                                }
-                       } else { /* i == 2 */
+                       } else { /* i == 1 */
                                xread(fd_d, buf2, count);
-                               if (memcmp(buf, buf2, count)) {
+                               if (memcmp(buf, buf2, count) != 0) {
                                        bb_error_msg_and_die("verification mismatch at 0x%"OFF_FMT"x", done);
                                }
                        }
index 9738620..f0e9c9d 100644 (file)
@@ -5,12 +5,63 @@
  * Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it>
  * Hacked by Tito <farmatito@tiscali.it> for size optimization.
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * This program is based on the source code of hdparm: see below...
  * hdparm.c - Command line interface to get/set hard disk parameters
  *          - by Mark Lord (C) 1994-2002 -- freely distributable
  */
+
+//usage:#define hdparm_trivial_usage
+//usage:       "[OPTIONS] [DEVICE]"
+//usage:#define hdparm_full_usage "\n\n"
+//usage:       "       -a      Get/set fs readahead"
+//usage:     "\n       -A      Set drive read-lookahead flag (0/1)"
+//usage:     "\n       -b      Get/set bus state (0 == off, 1 == on, 2 == tristate)"
+//usage:     "\n       -B      Set Advanced Power Management setting (1-255)"
+//usage:     "\n       -c      Get/set IDE 32-bit IO setting"
+//usage:     "\n       -C      Check IDE power mode status"
+//usage:       IF_FEATURE_HDPARM_HDIO_GETSET_DMA(
+//usage:     "\n       -d      Get/set using_dma flag")
+//usage:     "\n       -D      Enable/disable drive defect-mgmt"
+//usage:     "\n       -f      Flush buffer cache for device on exit"
+//usage:     "\n       -g      Display drive geometry"
+//usage:     "\n       -h      Display terse usage information"
+//usage:       IF_FEATURE_HDPARM_GET_IDENTITY(
+//usage:     "\n       -i      Display drive identification")
+//usage:       IF_FEATURE_HDPARM_GET_IDENTITY(
+//usage:     "\n       -I      Detailed/current information directly from drive")
+//usage:     "\n       -k      Get/set keep_settings_over_reset flag (0/1)"
+//usage:     "\n       -K      Set drive keep_features_over_reset flag (0/1)"
+//usage:     "\n       -L      Set drive doorlock (0/1) (removable harddisks only)"
+//usage:     "\n       -m      Get/set multiple sector count"
+//usage:     "\n       -n      Get/set ignore-write-errors flag (0/1)"
+//usage:     "\n       -p      Set PIO mode on IDE interface chipset (0,1,2,3,4,...)"
+//usage:     "\n       -P      Set drive prefetch count"
+/* //usage:  "\n       -q      Change next setting quietly" - not supported ib bbox */
+//usage:     "\n       -Q      Get/set DMA tagged-queuing depth (if supported)"
+//usage:     "\n       -r      Get/set readonly flag (DANGEROUS to set)"
+//usage:       IF_FEATURE_HDPARM_HDIO_SCAN_HWIF(
+//usage:     "\n       -R      Register an IDE interface (DANGEROUS)")
+//usage:     "\n       -S      Set standby (spindown) timeout"
+//usage:     "\n       -t      Perform device read timings"
+//usage:     "\n       -T      Perform cache read timings"
+//usage:     "\n       -u      Get/set unmaskirq flag (0/1)"
+//usage:       IF_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF(
+//usage:     "\n       -U      Unregister an IDE interface (DANGEROUS)")
+//usage:     "\n       -v      Defaults; same as -mcudkrag for IDE drives"
+//usage:     "\n       -V      Display program version and exit immediately"
+//usage:       IF_FEATURE_HDPARM_HDIO_DRIVE_RESET(
+//usage:     "\n       -w      Perform device reset (DANGEROUS)")
+//usage:     "\n       -W      Set drive write-caching flag (0/1) (DANGEROUS)"
+//usage:       IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF(
+//usage:     "\n       -x      Tristate device for hotswap (0/1) (DANGEROUS)")
+//usage:     "\n       -X      Set IDE xfer mode (DANGEROUS)"
+//usage:     "\n       -y      Put IDE drive in standby mode"
+//usage:     "\n       -Y      Put IDE drive to sleep"
+//usage:     "\n       -Z      Disable Seagate auto-powersaving mode"
+//usage:     "\n       -z      Reread partition table"
+
 #include "libbb.h"
 /* must be _after_ libbb.h: */
 #include <linux/hdreg.h>
@@ -382,6 +433,7 @@ struct BUG_G_too_big {
 #define hwif_data          (G.hwif_data              )
 #define hwif_ctrl          (G.hwif_ctrl              )
 #define hwif_irq           (G.hwif_irq               )
+#define INIT_G() do { } while (0)
 
 
 /* Busybox messages and functions */
@@ -413,14 +465,14 @@ static void on_off(int value)
 static void print_flag_on_off(int get_arg, const char *s, unsigned long arg)
 {
        if (get_arg) {
-               printf(" setting %s to %ld", s, arg);
+               printf(" setting %s to %lu", s, arg);
                on_off(arg);
        }
 }
 
 static void print_value_on_off(const char *str, unsigned long argp)
 {
-       printf(" %s\t= %2ld", str, argp);
+       printf(" %s\t= %2lu", str, argp);
        on_off(argp != 0);
 }
 
@@ -730,8 +782,8 @@ static void identify(uint16_t *val)
                if (val[MINOR] && (val[MINOR] <= MINOR_MAX)) {
                        if (like_std < 3) like_std = 3;
                        std = actual_ver[val[MINOR]];
-                       if (std) printf("\n\tUsed: %s ", nth_string(minor_str, val[MINOR]));
-
+                       if (std)
+                               printf("\n\tUsed: %s ", nth_string(minor_str, val[MINOR]));
                }
                /* looks like when they up-issue the std, they obsolete one;
                 * thus, only the newest 4 issues need be supported. (That's
@@ -970,8 +1022,8 @@ static void identify(uint16_t *val)
                }
                if ((like_std > 3) && (val[CMDS_SUPP_1] & 0x0008)) {
                        /* We print out elsewhere whether the APM feature is enabled or
-                          not.  If it's not enabled, let's not repeat the info; just print
-                          nothing here. */
+                        * not.  If it's not enabled, let's not repeat the info; just print
+                        * nothing here. */
                        printf("\tAdvancedPM level: ");
                        if ((val[ADV_PWR] & 0xFF00) == 0x4000) {
                                uint8_t apm_level = val[ADV_PWR] & 0x00FF;
@@ -986,7 +1038,7 @@ static void identify(uint16_t *val)
                                val[ACOUSTIC] & 0x00ff);
                }
        } else {
-                /* ATAPI */
+               /* ATAPI */
                if (eqpt != CDROM && (val[CAPAB_0] & SWRST_REQ))
                        printf("\tATA sw reset required\n");
 
@@ -1457,7 +1509,7 @@ static void bus_state_value(unsigned value)
        else if (value == BUSSTATE_TRISTATE)
                printf(" (tristate)\n");
        else
-               printf(" (unknown: %d)\n", value);
+               printf(" (unknown: %u)\n", value);
 }
 #endif
 
@@ -1537,7 +1589,7 @@ static void interpret_xfermode(unsigned xfermode)
 static void print_flag(int flag, const char *s, unsigned long value)
 {
        if (flag)
-               printf(" setting %s to %ld\n", s, value);
+               printf(" setting %s to %lu\n", s, value);
 }
 
 static void process_dev(char *devname)
@@ -1745,7 +1797,7 @@ static void process_dev(char *devname)
                if (-1 == read(fd, buf, sizeof(buf)))
                        bb_perror_msg("read of 512 bytes failed");
        }
-#endif /* HDIO_DRIVE_CMD */
+#endif  /* HDIO_DRIVE_CMD */
        if (getset_mult || get_identity) {
                multcount = -1;
                if (ioctl(fd, HDIO_GET_MULTCOUNT, &multcount)) {
@@ -2008,6 +2060,8 @@ int hdparm_main(int argc, char **argv)
        int c;
        int flagcount = 0;
 
+       INIT_G();
+
        while ((c = getopt(argc, argv, hdparm_options)) >= 0) {
                flagcount++;
                IF_FEATURE_HDPARM_GET_IDENTITY(get_IDentity |= (c == 'I'));
@@ -2055,8 +2109,8 @@ int hdparm_main(int argc, char **argv)
 #if ENABLE_FEATURE_HDPARM_HDIO_SCAN_HWIF
                if (c == 'R') {
                        scan_hwif = parse_opts_0_INTMAX(&hwif_data);
-                       hwif_ctrl = xatoi_u((argv[optind]) ? argv[optind] : "");
-                       hwif_irq  = xatoi_u((argv[optind+1]) ? argv[optind+1] : "");
+                       hwif_ctrl = xatoi_positive((argv[optind]) ? argv[optind] : "");
+                       hwif_irq  = xatoi_positive((argv[optind+1]) ? argv[optind+1] : "");
                        /* Move past the 2 additional arguments */
                        argv += 2;
                        argc -= 2;
index 271f3ad..7a1a6a2 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /*
  * See below for mask names explanation.
  */
 
+//usage:#define inotifyd_trivial_usage
+//usage:       "PROG FILE1[:MASK]..."
+//usage:#define inotifyd_full_usage "\n\n"
+//usage:       "Run PROG on filesystem changes."
+//usage:     "\nWhen a filesystem event matching MASK occurs on FILEn,"
+//usage:     "\nPROG ACTUAL_EVENTS FILEn [SUBFILE] is run."
+//usage:     "\nIf PROG is -, events are sent to stdout."
+//usage:     "\nEvents:"
+//usage:     "\n       a       File is accessed"
+//usage:     "\n       c       File is modified"
+//usage:     "\n       e       Metadata changed"
+//usage:     "\n       w       Writable file is closed"
+//usage:     "\n       0       Unwritable file is closed"
+//usage:     "\n       r       File is opened"
+//usage:     "\n       D       File is deleted"
+//usage:     "\n       M       File is moved"
+//usage:     "\n       u       Backing fs is unmounted"
+//usage:     "\n       o       Event queue overflowed"
+//usage:     "\n       x       File can't be watched anymore"
+//usage:     "\nIf watching a directory:"
+//usage:     "\n       m       Subfile is moved into dir"
+//usage:     "\n       y       Subfile is moved out of dir"
+//usage:     "\n       n       Subfile is created"
+//usage:     "\n       d       Subfile is deleted"
+//usage:     "\n"
+//usage:     "\ninotifyd waits for PROG to exit."
+//usage:     "\nWhen x event happens for all FILEs, inotifyd exits."
+
 #include "libbb.h"
 #include <sys/inotify.h>
 
@@ -150,12 +178,20 @@ int inotifyd_main(int argc, char **argv)
                                                *s++ = mask_names[i];
                                }
                                *s = '\0';
-//                             bb_error_msg("exec %s %08X\t%s\t%s\t%s", args[0],
-//                                     ie->mask, events, watches[ie->wd], ie->len ? ie->name : "");
-                               args[1] = events;
-                               args[2] = watches[ie->wd];
-                               args[3] = ie->len ? ie->name : NULL;
-                               spawn_and_wait((char **)args);
+                               if (LONE_CHAR(args[0], '-')) {
+                                       /* "inotifyd - FILE": built-in echo */
+                                       printf(ie->len ? "%s\t%s\t%s\n" : "%s\t%s\n", events,
+                                                       watches[ie->wd],
+                                                       ie->name);
+                                       fflush(stdout);
+                               } else {
+//                                     bb_error_msg("exec %s %08X\t%s\t%s\t%s", args[0],
+//                                             ie->mask, events, watches[ie->wd], ie->len ? ie->name : "");
+                                       args[1] = events;
+                                       args[2] = watches[ie->wd];
+                                       args[3] = ie->len ? ie->name : NULL;
+                                       spawn_and_wait((char **)args);
+                               }
                                // we are done if all files got final x event
                                if (ie->mask & 0x8000) {
                                        if (--argc <= 0)
index 52e51b9..bd30060 100644 (file)
@@ -4,9 +4,16 @@
  *
  * Copyright (C) 2008 by  <u173034@informatik.uni-oldenburg.de>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define ionice_trivial_usage
+//usage:       "[-c 1-3] [-n 0-7] [-p PID] [PROG]"
+//usage:#define ionice_full_usage "\n\n"
+//usage:       "Change I/O priority and class\n"
+//usage:     "\n       -c      Class. 1:realtime 2:best-effort 3:idle"
+//usage:     "\n       -n      Priority"
+
 #include <sys/syscall.h>
 #include <asm/unistd.h>
 #include "libbb.h"
@@ -73,7 +80,7 @@ int ionice_main(int argc UNUSED_PARAM, char **argv)
 
        if (!(opt & (OPT_n|OPT_c))) {
                if (!(opt & OPT_p) && *argv)
-                       pid = xatoi_u(*argv);
+                       pid = xatoi_positive(*argv);
 
                pri = ioprio_get(IOPRIO_WHO_PROCESS, pid);
                if (pri == -1)
index 55c03ae..24f6e1c 100644 (file)
@@ -4,11 +4,20 @@
  *
  * Copyright (C) 2003-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#define last_trivial_usage
+//usage:       ""IF_FEATURE_LAST_FANCY("[-HW] [-f FILE]")
+//usage:#define last_full_usage "\n\n"
+//usage:       "Show listing of the last users that logged into the system"
+//usage:       IF_FEATURE_LAST_FANCY( "\n"
+/* //usage:  "\n       -H      Show header line" */
+//usage:     "\n       -W      Display with no host column truncation"
+//usage:     "\n       -f FILE Read from FILE instead of /var/log/wtmp"
+//usage:       )
+
 #include "libbb.h"
-#include <utmp.h>
 
 /* NB: ut_name and ut_user are the same field, use only one name (ut_user)
  * to reduce confusion */
@@ -46,14 +55,14 @@ int last_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
        static const char _ut_lin[] ALIGN1 =
                        "~\0" "{\0" "|\0" /* "LOGIN\0" "date\0" */;
        enum {
-               TYPE_RUN_LVL = RUN_LVL,         /* 1 */
-               TYPE_BOOT_TIME = BOOT_TIME,     /* 2 */
+               TYPE_RUN_LVL = RUN_LVL,         /* 1 */
+               TYPE_BOOT_TIME = BOOT_TIME,     /* 2 */
                TYPE_SHUTDOWN_TIME = SHUTDOWN_TIME
        };
        enum {
-               _TILDE = EMPTY,                         /* 0 */
-               TYPE_NEW_TIME,  /* NEW_TIME, 3 */
-               TYPE_OLD_TIME   /* OLD_TIME, 4 */
+               _TILDE = EMPTY, /* 0 */
+               TYPE_NEW_TIME,  /* NEW_TIME, 3 */
+               TYPE_OLD_TIME   /* OLD_TIME, 4 */
        };
 
        if (argv[1]) {
@@ -62,7 +71,7 @@ int last_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
        file = xopen(bb_path_wtmp_file, O_RDONLY);
 
        printf("%-10s %-14s %-18s %-12.12s %s\n",
-              "USER", "TTY", "HOST", "LOGIN", "TIME");
+               "USER", "TTY", "HOST", "LOGIN", "TIME");
        /* yikes. We reverse over the file and that is a not too elegant way */
        pos = xlseek(file, 0, SEEK_END);
        pos = lseek(file, pos - sizeof(ut), SEEK_SET);
@@ -122,7 +131,7 @@ int last_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
                 * but some systems have it wrong */
                t_tmp = (time_t)ut.ut_tv.tv_sec;
                printf("%-10s %-14s %-18s %-12.12s\n",
-                      ut.ut_user, ut.ut_line, ut.ut_host, ctime(&t_tmp) + 4);
+                       ut.ut_user, ut.ut_line, ut.ut_host, ctime(&t_tmp) + 4);
  next:
                pos -= sizeof(ut);
                if (pos <= 0)
index f3ea037..f687d7e 100644 (file)
@@ -4,11 +4,10 @@
  *
  * Copyright (C) 2008 by Patricia Muscalu <patricia.muscalu@axis.com>
  *
- * Licensed under the GPLv2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
-#include <utmp.h>
 
 /* NB: ut_name and ut_user are the same field, use only one name (ut_user)
  * to reduce confusion */
@@ -94,14 +93,14 @@ static void show_entry(struct utmp *ut, int state, time_t dur_secs)
        }
 
        printf(HEADER_FORMAT,
-                  ut->ut_user,
-                  ut->ut_line,
-                  show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN,
-                  show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN,
-                  ut->ut_host,
-                  login_time,
-                  logout_str,
-                  duration_str);
+               ut->ut_user,
+               ut->ut_line,
+               show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN,
+               show_wide ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN,
+               ut->ut_host,
+               login_time,
+               logout_str,
+               duration_str);
 }
 
 static int get_ut_type(struct utmp *ut)
@@ -162,11 +161,10 @@ int last_main(int argc UNUSED_PARAM, char **argv)
        time_t boot_time;
        time_t down_time;
        int file;
-       unsigned opt;
        smallint going_down;
        smallint boot_down;
 
-       opt = getopt32(argv, "Wf:" /* "H" */, &filename);
+       /*opt =*/ getopt32(argv, "Wf:" /* "H" */, &filename);
 #ifdef BUT_UTIL_LINUX_LAST_HAS_NO_SUCH_OPT
        if (opt & LAST_OPT_H) {
                /* Print header line */
index da2cd07..60105f4 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2005 by Rob Sullivan <cogito.ergo.cogito@gmail.com>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*
  *   redirected input has been read from stdin
  */
 
-#include <sched.h>     /* sched_yield() */
+//config:config LESS
+//config:      bool "less"
+//config:      default y
+//config:      help
+//config:        'less' is a pager, meaning that it displays text files. It possesses
+//config:        a wide array of features, and is an improvement over 'more'.
+//config:
+//config:config FEATURE_LESS_MAXLINES
+//config:      int "Max number of input lines less will try to eat"
+//config:      default 9999999
+//config:      depends on LESS
+//config:
+//config:config FEATURE_LESS_BRACKETS
+//config:      bool "Enable bracket searching"
+//config:      default y
+//config:      depends on LESS
+//config:      help
+//config:        This option adds the capability to search for matching left and right
+//config:        brackets, facilitating programming.
+//config:
+//config:config FEATURE_LESS_FLAGS
+//config:      bool "Enable -m/-M"
+//config:      default y
+//config:      depends on LESS
+//config:      help
+//config:        The -M/-m flag enables a more sophisticated status line.
+//config:
+//config:config FEATURE_LESS_MARKS
+//config:      bool "Enable marks"
+//config:      default y
+//config:      depends on LESS
+//config:      help
+//config:        Marks enable positions in a file to be stored for easy reference.
+//config:
+//config:config FEATURE_LESS_REGEXP
+//config:      bool "Enable regular expressions"
+//config:      default y
+//config:      depends on LESS
+//config:      help
+//config:        Enable regular expressions, allowing complex file searches.
+//config:
+//config:config FEATURE_LESS_WINCH
+//config:      bool "Enable automatic resizing on window size changes"
+//config:      default y
+//config:      depends on LESS
+//config:      help
+//config:        Makes less track window size changes.
+//config:
+//config:config FEATURE_LESS_ASK_TERMINAL
+//config:      bool "Use 'tell me cursor position' ESC sequence to measure window"
+//config:      default y
+//config:      depends on FEATURE_LESS_WINCH
+//config:      help
+//config:        Makes less track window size changes.
+//config:        If terminal size can't be retrieved and $LINES/$COLUMNS are not set,
+//config:        this option makes less perform a last-ditch effort to find it:
+//config:        position cursor to 999,999 and ask terminal to report real
+//config:        cursor position using "ESC [ 6 n" escape sequence, then read stdin.
+//config:
+//config:        This is not clean but helps a lot on serial lines and such.
+//config:
+//config:config FEATURE_LESS_DASHCMD
+//config:      bool "Enable flag changes ('-' command)"
+//config:      default y
+//config:      depends on LESS
+//config:      help
+//config:        This enables the ability to change command-line flags within
+//config:        less itself ('-' keyboard command).
+//config:
+//config:config FEATURE_LESS_LINENUMS
+//config:      bool "Enable dynamic switching of line numbers"
+//config:      default y
+//config:      depends on FEATURE_LESS_DASHCMD
+//config:      help
+//config:        Enables "-N" command.
+
+//usage:#define less_trivial_usage
+//usage:       "[-E" IF_FEATURE_LESS_FLAGS("Mm") "Nh~I?] [FILE]..."
+//usage:#define less_full_usage "\n\n"
+//usage:       "View FILE (or stdin) one screenful at a time\n"
+//usage:     "\n       -E      Quit once the end of a file is reached"
+//usage:       IF_FEATURE_LESS_FLAGS(
+//usage:     "\n       -M,-m   Display status line with line numbers"
+//usage:     "\n               and percentage through the file"
+//usage:       )
+//usage:     "\n       -N      Prefix line number to each line"
+//usage:     "\n       -I      Ignore case in all searches"
+//usage:     "\n       -~      Suppress ~s displayed past EOF"
+
+#include <sched.h>  /* sched_yield() */
 
 #include "libbb.h"
 #if ENABLE_FEATURE_LESS_REGEXP
 #include "xregex.h"
 #endif
 
+
+#define ESC "\033"
 /* The escape codes for highlighted and normal text */
-#define HIGHLIGHT   "\033[7m"
-#define NORMAL      "\033[0m"
+#define HIGHLIGHT   ESC"[7m"
+#define NORMAL      ESC"[0m"
 /* The escape code to home and clear to the end of screen */
-#define CLEAR       "\033[H\033[J"
+#define CLEAR       ESC"[H\033[J"
 /* The escape code to clear to the end of line */
-#define CLEAR_2_EOL "\033[K"
+#define CLEAR_2_EOL ESC"[K"
 
 enum {
 /* Absolute max of lines eaten */
@@ -95,6 +186,9 @@ struct globals {
        regex_t pattern;
        smallint pattern_valid;
 #endif
+#if ENABLE_FEATURE_LESS_ASK_TERMINAL
+       smallint winsize_err;
+#endif
        smallint terminated;
        struct termios term_orig, term_less;
        char kbd_input[KEYCODE_BUFFER_SIZE];
@@ -165,12 +259,12 @@ static void set_tty_cooked(void)
    top-left corner of the console */
 static void move_cursor(int line, int row)
 {
-       printf("\033[%u;%uH", line, row);
+       printf(ESC"[%u;%uH", line, row);
 }
 
 static void clear_line(void)
 {
-       printf("\033[%u;0H" CLEAR_2_EOL, max_displayed_line + 2);
+       printf(ESC"[%u;0H" CLEAR_2_EOL, max_displayed_line + 2);
 }
 
 static void print_hilite(const char *str)
@@ -477,7 +571,7 @@ static void m_status_print(void)
 {
        int percentage;
 
-       if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
+       if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
                return;
 
        clear_line();
@@ -503,7 +597,7 @@ static void status_print(void)
 {
        const char *p;
 
-       if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
+       if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
                return;
 
        /* Change the status if flags have been set */
@@ -615,9 +709,9 @@ static void print_found(const char *line)
        /* buf[] holds quarantined version of str */
 
        /* Each part of the line that matches has the HIGHLIGHT
-          and NORMAL escape sequences placed around it.
-          NB: we regex against line, but insert text
-          from quarantined copy (buf[]) */
+        * and NORMAL escape sequences placed around it.
+        * NB: we regex against line, but insert text
+        * from quarantined copy (buf[]) */
        str = buf;
        growline = NULL;
        eflags = 0;
@@ -626,8 +720,8 @@ static void print_found(const char *line)
        while (match_status == 0) {
                char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"NORMAL,
                                growline ? growline : "",
-                               match_structs.rm_so, str,
-                               match_structs.rm_eo - match_structs.rm_so,
+                               (int)match_structs.rm_so, str,
+                               (int)(match_structs.rm_eo - match_structs.rm_so),
                                                str + match_structs.rm_so);
                free(growline);
                growline = new;
@@ -802,12 +896,17 @@ static void reinitialize(void)
        cur_fline = 0;
        max_lineno = 0;
        open_file_and_read_lines();
+#if ENABLE_FEATURE_LESS_ASK_TERMINAL
+       if (G.winsize_err)
+               printf("\033[999;999H" "\033[6n");
+#endif
        buffer_fill_and_print();
 }
 
-static int getch_nowait(void)
+static int64_t getch_nowait(void)
 {
        int rd;
+       int64_t key64;
        struct pollfd pfd[2];
 
        pfd[0].fd = STDIN_FILENO;
@@ -855,8 +954,8 @@ static int getch_nowait(void)
 
        /* We have kbd_fd in O_NONBLOCK mode, read inside read_key()
         * would not block even if there is no input available */
-       rd = read_key(kbd_fd, kbd_input, /*timeout off:*/ -2);
-       if (rd == -1) {
+       key64 = read_key(kbd_fd, kbd_input, /*timeout off:*/ -2);
+       if ((int)key64 == -1) {
                if (errno == EAGAIN) {
                        /* No keyboard input available. Since poll() did return,
                         * we should have input on stdin */
@@ -868,25 +967,30 @@ static int getch_nowait(void)
                less_exit(0);
        }
        set_tty_cooked();
-       return rd;
+       return key64;
 }
 
 /* Grab a character from input without requiring the return key.
  * May return KEYCODE_xxx values.
  * Note that this function works best with raw input. */
-static int less_getch(int pos)
+static int64_t less_getch(int pos)
 {
-       int i;
+       int64_t key64;
+       int key;
 
  again:
        less_gets_pos = pos;
-       i = getch_nowait();
+       key = key64 = getch_nowait();
        less_gets_pos = -1;
 
-       /* Discard Ctrl-something chars */
-       if (i >= 0 && i < ' ' && i != 0x0d && i != 8)
+       /* Discard Ctrl-something chars.
+        * (checking only lower 32 bits is a size optimization:
+        * upper 32 bits are used only by KEYCODE_CURSOR_POS)
+        */
+       if (key >= 0 && key < ' ' && key != 0x0d && key != 8)
                goto again;
-       return i;
+
+       return key64;
 }
 
 static char* less_gets(int sz)
@@ -1426,6 +1530,9 @@ static void keypress_process(int keypress)
                break;
 #endif
        case 'r': case 'R':
+               /* TODO: (1) also bind ^R, ^L to this?
+                * (2) re-measure window size?
+                */
                buffer_print();
                break;
        /*case 'R':
@@ -1501,7 +1608,8 @@ static void sigwinch_handler(int sig UNUSED_PARAM)
 int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int less_main(int argc, char **argv)
 {
-       int keypress;
+       char *tty_name;
+       int tty_fd;
 
        INIT_G();
 
@@ -1532,10 +1640,28 @@ int less_main(int argc, char **argv)
        if (option_mask32 & FLAG_TILDE)
                empty_line_marker = "";
 
-       kbd_fd = open(CURRENT_TTY, O_RDONLY);
-       if (kbd_fd < 0)
-               return bb_cat(argv);
-       ndelay_on(kbd_fd);
+       /* Some versions of less can survive w/o controlling tty,
+        * try to do the same. This also allows to specify an alternative
+        * tty via "less 1<>TTY".
+        * We don't try to use STDOUT_FILENO directly,
+        * since we want to set this fd to non-blocking mode,
+        * and not bother with restoring it on exit.
+        */
+       tty_name = xmalloc_ttyname(STDOUT_FILENO);
+       if (tty_name) {
+               tty_fd = open(tty_name, O_RDONLY);
+               free(tty_name);
+               if (tty_fd < 0)
+                       goto try_ctty;
+       } else {
+               /* Try controlling tty */
+ try_ctty:
+               tty_fd = open(CURRENT_TTY, O_RDONLY);
+               if (tty_fd < 0)
+                       return bb_cat(argv);
+       }
+       ndelay_on(tty_fd);
+       kbd_fd = tty_fd; /* save in a global */
 
        tcgetattr(kbd_fd, &term_orig);
        term_less = term_orig;
@@ -1545,7 +1671,7 @@ int less_main(int argc, char **argv)
        term_less.c_cc[VMIN] = 1;
        term_less.c_cc[VTIME] = 0;
 
-       get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
+       IF_FEATURE_LESS_ASK_TERMINAL(G.winsize_err =) get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
        /* 20: two tabstops + 4 */
        if (width < 20 || max_displayed_line < 3)
                return bb_cat(argv);
@@ -1560,11 +1686,14 @@ int less_main(int argc, char **argv)
        buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
        reinitialize();
        while (1) {
+               int64_t keypress;
+
 #if ENABLE_FEATURE_LESS_WINCH
                while (WINCH_COUNTER) {
  again:
                        winch_counter--;
-                       get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
+                       IF_FEATURE_LESS_ASK_TERMINAL(G.winsize_err =) get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
+ IF_FEATURE_LESS_ASK_TERMINAL(got_size:)
                        /* 20: two tabstops + 4 */
                        if (width < 20)
                                width = 20;
@@ -1584,8 +1713,18 @@ int less_main(int argc, char **argv)
                        /* This took some time. Loop back and check,
                         * were there another SIGWINCH? */
                }
-#endif
                keypress = less_getch(-1); /* -1: do not position cursor */
+# if ENABLE_FEATURE_LESS_ASK_TERMINAL
+               if ((int32_t)keypress == KEYCODE_CURSOR_POS) {
+                       uint32_t rc = (keypress >> 32);
+                       width = (rc & 0x7fff);
+                       max_displayed_line = ((rc >> 16) & 0x7fff);
+                       goto got_size;
+               }
+# endif
+#else
+               keypress = less_getch(-1); /* -1: do not position cursor */
+#endif
                keypress_process(keypress);
        }
 }
index abf5057..c945a13 100644 (file)
@@ -7,6 +7,66 @@
  * known bugs: can't deal with alpha ranges
  */
 
+//usage:#if ENABLE_FEATURE_MAKEDEVS_LEAF
+//usage:#define makedevs_trivial_usage
+//usage:       "NAME TYPE MAJOR MINOR FIRST LAST [s]"
+//usage:#define makedevs_full_usage "\n\n"
+//usage:       "Create a range of block or character special files"
+//usage:     "\n"
+//usage:     "\nTYPE is:"
+//usage:     "\n       b       Block device"
+//usage:     "\n       c       Character device"
+//usage:     "\n       f       FIFO, MAJOR and MINOR are ignored"
+//usage:     "\n"
+//usage:     "\nFIRST..LAST specify numbers appended to NAME."
+//usage:     "\nIf 's' is the last argument, the base device is created as well."
+//usage:     "\n"
+//usage:     "\nExamples:"
+//usage:     "\n       makedevs /dev/ttyS c 4 66 2 63   ->  ttyS2-ttyS63"
+//usage:     "\n       makedevs /dev/hda b 3 0 0 8 s    ->  hda,hda1-hda8"
+//usage:
+//usage:#define makedevs_example_usage
+//usage:       "# makedevs /dev/ttyS c 4 66 2 63\n"
+//usage:       "[creates ttyS2-ttyS63]\n"
+//usage:       "# makedevs /dev/hda b 3 0 0 8 s\n"
+//usage:       "[creates hda,hda1-hda8]\n"
+//usage:#endif
+//usage:
+//usage:#if ENABLE_FEATURE_MAKEDEVS_TABLE
+//usage:#define makedevs_trivial_usage
+//usage:       "[-d device_table] rootdir"
+//usage:#define makedevs_full_usage "\n\n"
+//usage:       "Create a range of special files as specified in a device table.\n"
+//usage:       "Device table entries take the form of:\n"
+//usage:       "<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>\n"
+//usage:       "Where name is the file name, type can be one of:\n"
+//usage:       "       f       Regular file\n"
+//usage:       "       d       Directory\n"
+//usage:       "       c       Character device\n"
+//usage:       "       b       Block device\n"
+//usage:       "       p       Fifo (named pipe)\n"
+//usage:       "uid is the user id for the target file, gid is the group id for the\n"
+//usage:       "target file. The rest of the entries (major, minor, etc) apply to\n"
+//usage:       "to device special files. A '-' may be used for blank entries."
+//usage:
+//usage:#define makedevs_example_usage
+//usage:       "For example:\n"
+//usage:       "<name>    <type> <mode><uid><gid><major><minor><start><inc><count>\n"
+//usage:       "/dev         d   755    0    0    -      -      -      -    -\n"
+//usage:       "/dev/console c   666    0    0    5      1      -      -    -\n"
+//usage:       "/dev/null    c   666    0    0    1      3      0      0    -\n"
+//usage:       "/dev/zero    c   666    0    0    1      5      0      0    -\n"
+//usage:       "/dev/hda     b   640    0    0    3      0      0      0    -\n"
+//usage:       "/dev/hda     b   640    0    0    3      1      1      1    15\n\n"
+//usage:       "Will Produce:\n"
+//usage:       "/dev\n"
+//usage:       "/dev/console\n"
+//usage:       "/dev/null\n"
+//usage:       "/dev/zero\n"
+//usage:       "/dev/hda\n"
+//usage:       "/dev/hda[0-15]\n"
+//usage:#endif
+
 #include "libbb.h"
 
 #if ENABLE_FEATURE_MAKEDEVS_LEAF
@@ -36,10 +96,10 @@ int makedevs_main(int argc, char **argv)
        basedev = argv[1];
        buf = xasprintf("%s%u", argv[1], (unsigned)-1);
        type = argv[2];
-       Smajor = xatoi_u(argv[3]);
-       Sminor = xatoi_u(argv[4]);
-       S = xatoi_u(argv[5]);
-       E = xatoi_u(argv[6]);
+       Smajor = xatoi_positive(argv[3]);
+       Sminor = xatoi_positive(argv[4]);
+       S = xatoi_positive(argv[5]);
+       E = xatoi_positive(argv[6]);
        nodname = argv[7] ? basedev : buf;
 
        mode = 0660;
@@ -76,7 +136,7 @@ int makedevs_main(int argc, char **argv)
 
 #elif ENABLE_FEATURE_MAKEDEVS_TABLE
 
-/* Licensed under the GPL v2 or later, see the file LICENSE in this tarball. */
+/* Licensed under GPLv2 or later, see file LICENSE in this source tree. */
 
 int makedevs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int makedevs_main(int argc UNUSED_PARAM, char **argv)
@@ -121,7 +181,7 @@ int makedevs_main(int argc UNUSED_PARAM, char **argv)
 
                if ((2 > sscanf(line, "%40s %c %o %40s %40s %u %u %u %u %u",
                                        name, &type, &mode, user, group,
-                                       &major, &minor, &start, &increment, &count))
+                                       &major, &minor, &start, &increment, &count))
                 || ((unsigned)(major | minor | start | count | increment) > 255)
                ) {
                        bb_error_msg("invalid line %d: '%s'", linenum, line);
index a4ff274..d3e832b 100644 (file)
@@ -1,8 +1,15 @@
 /* mini man implementation for busybox
  * Copyright (C) 2008 Denys Vlasenko <vda.linux@googlemail.com>
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#define man_trivial_usage
+//usage:       "[-aw] [MANPAGE]..."
+//usage:#define man_full_usage "\n\n"
+//usage:       "Format and display manual page\n"
+//usage:     "\n       -a      Display all pages"
+//usage:     "\n       -w      Show page locations"
+
 #include "libbb.h"
 
 enum {
@@ -23,16 +30,6 @@ echo ".pl \n(nlu+10"
 
 */
 
-#if ENABLE_FEATURE_SEAMLESS_LZMA
-#define Z_SUFFIX ".lzma"
-#elif ENABLE_FEATURE_SEAMLESS_BZ2
-#define Z_SUFFIX ".bz2"
-#elif ENABLE_FEATURE_SEAMLESS_GZ
-#define Z_SUFFIX ".gz"
-#else
-#define Z_SUFFIX ""
-#endif
-
 static int show_manpage(const char *pager, char *man_filename, int man, int level);
 
 static int run_pipe(const char *pager, char *man_filename, int man, int level)
@@ -95,7 +92,7 @@ static int run_pipe(const char *pager, char *man_filename, int man, int level)
 
                /* Links do not have .gz extensions, even if manpage
                 * is compressed */
-               man_filename = xasprintf("%s/%s" Z_SUFFIX, man_filename, linkname);
+               man_filename = xasprintf("%s/%s", man_filename, linkname);
                free(line);
                /* Note: we leak "new" man_filename string as well... */
                if (show_manpage(pager, man_filename, man, level + 1))
@@ -117,37 +114,36 @@ static int run_pipe(const char *pager, char *man_filename, int man, int level)
        return 1;
 }
 
-/* man_filename is of the form "/dir/dir/dir/name.s" Z_SUFFIX */
+/* man_filename is of the form "/dir/dir/dir/name.s" */
 static int show_manpage(const char *pager, char *man_filename, int man, int level)
 {
-#if ENABLE_FEATURE_SEAMLESS_LZMA
-       if (run_pipe(pager, man_filename, man, level))
-               return 1;
+#if SEAMLESS_COMPRESSION
+       /* We leak this allocation... */
+       char *filename_with_zext = xasprintf("%s.lzma", man_filename);
+       char *ext = strrchr(filename_with_zext, '.') + 1;
 #endif
 
-#if ENABLE_FEATURE_SEAMLESS_BZ2
 #if ENABLE_FEATURE_SEAMLESS_LZMA
-       strcpy(strrchr(man_filename, '.') + 1, "bz2");
-#endif
-       if (run_pipe(pager, man_filename, man, level))
+       if (run_pipe(pager, filename_with_zext, man, level))
                return 1;
 #endif
-
-#if ENABLE_FEATURE_SEAMLESS_GZ
-#if ENABLE_FEATURE_SEAMLESS_LZMA || ENABLE_FEATURE_SEAMLESS_BZ2
-       strcpy(strrchr(man_filename, '.') + 1, "gz");
-#endif
-       if (run_pipe(pager, man_filename, man, level))
+#if ENABLE_FEATURE_SEAMLESS_XZ
+       strcpy(ext, "xz");
+       if (run_pipe(pager, filename_with_zext, man, level))
                return 1;
 #endif
-
-#if ENABLE_FEATURE_SEAMLESS_LZMA || ENABLE_FEATURE_SEAMLESS_BZ2 || ENABLE_FEATURE_SEAMLESS_GZ
-       *strrchr(man_filename, '.') = '\0';
+#if ENABLE_FEATURE_SEAMLESS_BZ2
+       strcpy(ext, "bz2");
+       if (run_pipe(pager, filename_with_zext, man, level))
+               return 1;
 #endif
-       if (run_pipe(pager, man_filename, man, level))
+#if ENABLE_FEATURE_SEAMLESS_GZ
+       strcpy(ext, "gz");
+       if (run_pipe(pager, filename_with_zext, man, level))
                return 1;
+#endif
 
-       return 0;
+       return run_pipe(pager, man_filename, man, level);
 }
 
 int man_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -166,7 +162,7 @@ int man_main(int argc UNUSED_PARAM, char **argv)
        opt = getopt32(argv, "+aw");
        argv += optind;
 
-       sec_list = xstrdup("1:2:3:4:5:6:7:8:9");
+       sec_list = xstrdup("0p:1:1p:2:3:3p:4:5:6:7:8:9");
        /* Last valid man_path_list[] is [0x10] */
        count_mp = 0;
        man_path_list = xzalloc(0x11 * sizeof(man_path_list[0]));
@@ -182,16 +178,21 @@ int man_main(int argc UNUSED_PARAM, char **argv)
                        pager = "more";
        }
 
-       /* Parse man.conf[ig] */
+       /* Parse man.conf[ig] or man_db.conf */
        /* man version 1.6f uses man.config */
+       /* man-db implementation of man uses man_db.conf */
        parser = config_open2("/etc/man.config", fopen_for_read);
        if (!parser)
                parser = config_open2("/etc/man.conf", fopen_for_read);
+       if (!parser)
+               parser = config_open2("/etc/man_db.conf", fopen_for_read);
 
        while (config_read(parser, token, 2, 0, "# \t", PARSE_NORMAL)) {
                if (!token[1])
                        continue;
-               if (strcmp("MANPATH", token[0]) == 0) {
+               if (strcmp("MANDATORY_MANPATH"+10, token[0]) == 0 /* "MANPATH"? */
+                || strcmp("MANDATORY_MANPATH", token[0]) == 0
+               ) {
                        char *path = token[1];
                        while (*path) {
                                char *next_path;
@@ -250,7 +251,7 @@ int man_main(int argc UNUSED_PARAM, char **argv)
                                /* Search for cat, then man page */
                                while (cat0man1 < 2) {
                                        int found_here;
-                                       man_filename = xasprintf("%s/%s%.*s/%s.%.*s" Z_SUFFIX,
+                                       man_filename = xasprintf("%s/%s%.*s/%s.%.*s",
                                                        cur_path,
                                                        "cat\0man" + (cat0man1 * 4),
                                                        sect_len, cur_sect,
index 78863d4..5e29a1a 100644 (file)
@@ -5,8 +5,19 @@
  *
  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define microcom_trivial_usage
+//usage:       "[-d DELAY] [-t TIMEOUT] [-s SPEED] [-X] TTY"
+//usage:#define microcom_full_usage "\n\n"
+//usage:       "Copy bytes for stdin to TTY and from TTY to stdout\n"
+//usage:     "\n       -d      Wait up to DELAY ms for TTY output before sending every"
+//usage:     "\n               next byte to it"
+//usage:     "\n       -t      Exit if both stdin and TTY are silent for TIMEOUT ms"
+//usage:     "\n       -s      Set serial line to SPEED"
+//usage:     "\n       -X      Disable special meaning of NUL and Ctrl-X from stdin"
+
 #include "libbb.h"
 
 // set raw tty mode
index a35c389..7041f7c 100644 (file)
@@ -4,11 +4,26 @@
  *
  * Copyright (C) 2005 Bernhard Reutner-Fischer
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Based on sysvinit's mountpoint
  */
 
+//usage:#define mountpoint_trivial_usage
+//usage:       "[-q] <[-dn] DIR | -x DEVICE>"
+//usage:#define mountpoint_full_usage "\n\n"
+//usage:       "Check if the directory is a mountpoint\n"
+//usage:     "\n       -q      Quiet"
+//usage:     "\n       -d      Print major/minor device number of the filesystem"
+//usage:     "\n       -n      Print device name of the filesystem"
+//usage:     "\n       -x      Print major/minor device number of the blockdevice"
+//usage:
+//usage:#define mountpoint_example_usage
+//usage:       "$ mountpoint /proc\n"
+//usage:       "/proc is not a mountpoint\n"
+//usage:       "$ mountpoint /sys\n"
+//usage:       "/sys is a mountpoint\n"
+
 #include "libbb.h"
 
 int mountpoint_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
index 586373d..20afd3a 100644 (file)
@@ -1,8 +1,20 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define mt_trivial_usage
+//usage:       "[-f device] opcode value"
+//usage:#define mt_full_usage "\n\n"
+//usage:       "Control magnetic tape drive operation\n"
+//usage:       "\n"
+//usage:       "Available Opcodes:\n"
+//usage:       "\n"
+//usage:       "bsf bsfm bsr bss datacompression drvbuffer eof eom erase\n"
+//usage:       "fsf fsfm fsr fss load lock mkpart nop offline ras1 ras2\n"
+//usage:       "ras3 reset retension rewind rewoffline seek setblk setdensity\n"
+//usage:       "setpart tell unload unlock weof wset"
+
 #include "libbb.h"
 #include <sys/mtio.h>
 
@@ -106,9 +118,9 @@ int mt_main(int argc UNUSED_PARAM, char **argv)
 
        op.mt_op = opcode_value[idx];
        if (argv[2])
-               op.mt_count = xatoi_u(argv[2]);
+               op.mt_count = xatoi_positive(argv[2]);
        else
-               op.mt_count = 1;                /* One, not zero, right? */
+               op.mt_count = 1;  /* One, not zero, right? */
 
        switch (opcode_value[idx]) {
                case MTWEOF:
diff --git a/miscutils/nandwrite.c b/miscutils/nandwrite.c
new file mode 100644 (file)
index 0000000..e3f9b56
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * nandwrite and nanddump ported to busybox from mtd-utils
+ *
+ * Author: Baruch Siach <baruch@tkos.co.il>, Orex Computed Radiography
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ *
+ * TODO: add support for large (>4GB) MTD devices
+ */
+
+//config:config NANDWRITE
+//config:      bool "nandwrite"
+//config:      default y
+//config:      select PLATFORM_LINUX
+//config:      help
+//config:        Write to the specified MTD device, with bad blocks awareness
+//config:
+//config:config NANDDUMP
+//config:      bool "nanddump"
+//config:      default y
+//config:      select PLATFORM_LINUX
+//config:      help
+//config:        Dump the content of raw NAND chip
+
+//applet:IF_NANDWRITE(APPLET(nandwrite, BB_DIR_USR_SBIN, BB_SUID_DROP))
+//applet:IF_NANDDUMP(APPLET_ODDNAME(nanddump, nandwrite, BB_DIR_USR_SBIN, BB_SUID_DROP, nanddump))
+
+//kbuild:lib-$(CONFIG_NANDWRITE) += nandwrite.o
+//kbuild:lib-$(CONFIG_NANDDUMP) += nandwrite.o
+
+//usage:#define nandwrite_trivial_usage
+//usage:       "[-p] [-s ADDR] MTD_DEVICE [FILE]"
+//usage:#define nandwrite_full_usage "\n\n"
+//usage:       "Write to MTD_DEVICE\n"
+//usage:     "\n       -p      Pad to page size"
+//usage:     "\n       -s ADDR Start address"
+
+//usage:#define nanddump_trivial_usage
+//usage:       "[-o] [-b] [-s ADDR] [-l LEN] [-f FILE] MTD_DEVICE"
+//usage:#define nanddump_full_usage "\n\n"
+//usage:       "Dump MTD_DEVICE\n"
+//usage:     "\n       -o      Dump oob data"
+//usage:     "\n       -b      Omit bad block from the dump"
+//usage:     "\n       -s ADDR Start address"
+//usage:     "\n       -l LEN  Length"
+//usage:     "\n       -f FILE Dump to file ('-' for stdout)"
+
+#include "libbb.h"
+#include <mtd/mtd-user.h>
+
+#define IS_NANDDUMP  (ENABLE_NANDDUMP && (!ENABLE_NANDWRITE || (applet_name[4] == 'd')))
+#define IS_NANDWRITE (ENABLE_NANDWRITE && (!ENABLE_NANDDUMP || (applet_name[4] != 'd')))
+
+#define OPT_p  (1 << 0) /* nandwrite only */
+#define OPT_o  (1 << 0) /* nanddump only */
+#define OPT_s  (1 << 1)
+#define OPT_b  (1 << 2)
+#define OPT_f  (1 << 3)
+#define OPT_l  (1 << 4)
+
+/* helper for writing out 0xff for bad blocks pad */
+static void dump_bad(struct mtd_info_user *meminfo, unsigned len, int oob)
+{
+       unsigned char buf[meminfo->writesize];
+       unsigned count;
+
+       /* round len to the next page */
+       len = (len | ~(meminfo->writesize - 1)) + 1;
+
+       memset(buf, 0xff, sizeof(buf));
+       for (count = 0; count < len; count += meminfo->writesize) {
+               xwrite(STDOUT_FILENO, buf, meminfo->writesize);
+               if (oob)
+                       xwrite(STDOUT_FILENO, buf, meminfo->oobsize);
+       }
+}
+
+static unsigned next_good_eraseblock(int fd, struct mtd_info_user *meminfo,
+               unsigned block_offset)
+{
+       while (1) {
+               loff_t offs;
+
+               if (block_offset >= meminfo->size) {
+                       if (IS_NANDWRITE)
+                               bb_error_msg_and_die("not enough space in MTD device");
+                       return block_offset; /* let the caller exit */
+               }
+               offs = block_offset;
+               if (xioctl(fd, MEMGETBADBLOCK, &offs) == 0)
+                       return block_offset;
+               /* ioctl returned 1 => "bad block" */
+               if (IS_NANDWRITE)
+                       printf("Skipping bad block at 0x%08x\n", block_offset);
+               block_offset += meminfo->erasesize;
+       }
+}
+
+int nandwrite_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int nandwrite_main(int argc UNUSED_PARAM, char **argv)
+{
+       /* Buffer for OOB data */
+       unsigned char *oobbuf;
+       unsigned opts;
+       int fd;
+       ssize_t cnt;
+       unsigned mtdoffset, meminfo_writesize, blockstart, limit;
+       unsigned end_addr = ~0;
+       struct mtd_info_user meminfo;
+       struct mtd_oob_buf oob;
+       unsigned char *filebuf;
+       const char *opt_s = "0", *opt_f = "-", *opt_l;
+
+       if (IS_NANDDUMP) {
+               opt_complementary = "=1";
+               opts = getopt32(argv, "os:bf:l:", &opt_s, &opt_f, &opt_l);
+       } else { /* nandwrite */
+               opt_complementary = "-1:?2";
+               opts = getopt32(argv, "ps:", &opt_s);
+       }
+       argv += optind;
+
+       if (IS_NANDWRITE && argv[1])
+               opt_f = argv[1];
+       if (!LONE_DASH(opt_f)) {
+               int tmp_fd = xopen(opt_f,
+                       IS_NANDDUMP ? O_WRONLY | O_TRUNC | O_CREAT : O_RDONLY
+               );
+               xmove_fd(tmp_fd, IS_NANDDUMP ? STDOUT_FILENO : STDIN_FILENO);
+       }
+
+       fd = xopen(argv[0], IS_NANDWRITE ? O_RDWR : O_RDONLY);
+       xioctl(fd, MEMGETINFO, &meminfo);
+
+       mtdoffset = xstrtou(opt_s, 0);
+       if (IS_NANDDUMP && (opts & OPT_l)) {
+               unsigned length = xstrtou(opt_l, 0);
+               if (length < meminfo.size - mtdoffset)
+                       end_addr = mtdoffset + length;
+       }
+
+       /* Pull it into a CPU register (hopefully) - smaller code that way */
+       meminfo_writesize = meminfo.writesize;
+
+       if (mtdoffset & (meminfo_writesize - 1))
+               bb_error_msg_and_die("start address is not page aligned");
+
+       filebuf = xmalloc(meminfo_writesize);
+       oobbuf = xmalloc(meminfo.oobsize);
+
+       oob.start  = 0;
+       oob.length = meminfo.oobsize;
+       oob.ptr    = oobbuf;
+
+       blockstart = mtdoffset & ~(meminfo.erasesize - 1);
+       if (blockstart != mtdoffset) {
+               unsigned tmp;
+               /* mtdoffset is in the middle of an erase block, verify that
+                * this block is OK. Advance mtdoffset only if this block is
+                * bad.
+                */
+               tmp = next_good_eraseblock(fd, &meminfo, blockstart);
+               if (tmp != blockstart) {
+                       /* bad block(s), advance mtdoffset */
+                       if (IS_NANDDUMP && !(opts & OPT_b)) {
+                               int bad_len = MIN(tmp, end_addr) - mtdoffset;
+                               dump_bad(&meminfo, bad_len, opts & OPT_o);
+                       }
+                       mtdoffset = tmp;
+               }
+       }
+
+       cnt = -1;
+       limit = MIN(meminfo.size, end_addr);
+       while (mtdoffset < limit) {
+               int input_fd = IS_NANDWRITE ? STDIN_FILENO : fd;
+               int output_fd = IS_NANDWRITE ? fd : STDOUT_FILENO;
+
+               blockstart = mtdoffset & ~(meminfo.erasesize - 1);
+               if (blockstart == mtdoffset) {
+                       /* starting a new eraseblock */
+                       mtdoffset = next_good_eraseblock(fd, &meminfo, blockstart);
+                       if (IS_NANDWRITE)
+                               printf("Writing at 0x%08x\n", mtdoffset);
+                       else if (mtdoffset > blockstart && !(opts & OPT_b)) {
+                               int bad_len = MIN(mtdoffset, limit) - blockstart;
+                               dump_bad(&meminfo, bad_len, opts & OPT_o);
+                       }
+                       if (mtdoffset >= limit)
+                               break;
+               }
+               xlseek(fd, mtdoffset, SEEK_SET);
+
+               /* get some more data from input */
+               cnt = full_read(input_fd, filebuf, meminfo_writesize);
+               if (cnt == 0) {
+                       /* even with -p, we do not pad past the end of input
+                        * (-p only zero-pads last incomplete page)
+                        */
+                       break;
+               }
+               if (cnt < meminfo_writesize) {
+                       if (IS_NANDDUMP)
+                               bb_error_msg_and_die("short read");
+                       if (!(opts & OPT_p))
+                               bb_error_msg_and_die("input size is not rounded up to page size, "
+                                               "use -p to zero pad");
+                       /* zero pad to end of write block */
+                       memset(filebuf + cnt, 0, meminfo_writesize - cnt);
+               }
+               xwrite(output_fd, filebuf, meminfo_writesize);
+
+               if (IS_NANDDUMP && (opts & OPT_o)) {
+                       /* Dump OOB data */
+                       oob.start = mtdoffset;
+                       xioctl(fd, MEMREADOOB, &oob);
+                       xwrite(output_fd, oobbuf, meminfo.oobsize);
+               }
+
+               mtdoffset += meminfo_writesize;
+               if (cnt < meminfo_writesize)
+                       break;
+       }
+
+       if (IS_NANDWRITE && cnt != 0) {
+               /* We filled entire MTD, but did we reach EOF on input? */
+               if (full_read(STDIN_FILENO, filebuf, meminfo_writesize) != 0) {
+                       /* no */
+                       bb_error_msg_and_die("not enough space in MTD device");
+               }
+       }
+
+       if (ENABLE_FEATURE_CLEAN_UP) {
+               free(filebuf);
+               close(fd);
+       }
+
+       return EXIT_SUCCESS;
+}
index 113e49f..b72d890 100644 (file)
@@ -4,10 +4,18 @@
  *
  * Copyright (C) 2006 Bernhard Reutner-Fischer
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  */
 
+//usage:#define raidautorun_trivial_usage
+//usage:       "DEVICE"
+//usage:#define raidautorun_full_usage "\n\n"
+//usage:       "Tell the kernel to automatically search and start RAID arrays"
+//usage:
+//usage:#define raidautorun_example_usage
+//usage:       "$ raidautorun /dev/md0"
+
 #include "libbb.h"
 
 #include <linux/major.h>
index f3b21a2..e22aaa4 100644 (file)
@@ -7,9 +7,14 @@
  *
  * Copyright (C) 2006  Michael Opdenacker <michael@free-electrons.com>
  *
- * Licensed under GPLv2 or later, see file License in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define readahead_trivial_usage
+//usage:       "[FILE]..."
+//usage:#define readahead_full_usage "\n\n"
+//usage:       "Preload FILEs to RAM"
+
 #include "libbb.h"
 
 int readahead_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
index 0f5817b..7411b6f 100644 (file)
@@ -4,8 +4,38 @@
 *
 * Copyright (C) 2010  Malek Degachi <malek-degachi@laposte.net>
 *
-* Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+* Licensed under GPLv2 or later, see file LICENSE in this source tree.
 */
+
+//config:config RFKILL
+//config:      bool "rfkill"
+//config:      default n # doesn't build on Ubuntu 9.04
+//config:      select PLATFORM_LINUX
+//config:      help
+//config:        Enable/disable wireless devices.
+//config:
+//config:        rfkill list : list all wireless devices
+//config:        rfkill list bluetooth : list all bluetooth devices
+//config:        rfkill list 1 : list device corresponding to the given index
+//config:        rfkill block|unblock wlan : block/unblock all wlan(wifi) devices
+//config:
+
+//applet:IF_RFKILL(APPLET(rfkill, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_RFKILL) += rfkill.o
+
+//usage:#define rfkill_trivial_usage
+//usage:       "COMMAND [INDEX|TYPE]"
+//usage:#define rfkill_full_usage "\n\n"
+//usage:       "Enable/disable wireless devices\n"
+//usage:       "\nCommands:"
+//usage:     "\n       list [INDEX|TYPE]       List current state"
+//usage:     "\n       block INDEX|TYPE        Disable device"
+//usage:     "\n       unblock INDEX|TYPE      Enable device"
+//usage:     "\n"
+//usage:     "\n       TYPE: all, wlan(wifi), bluetooth, uwb(ultrawideband),"
+//usage:     "\n               wimax, wwan, gps, fm"
+
 #include "libbb.h"
 #include <linux/rfkill.h>
 
@@ -53,7 +83,7 @@ int rfkill_main(int argc UNUSED_PARAM, char **argv)
                        rf_name = "uwb";
                rf_type = index_in_strings(rfkill_types, rf_name);
                if (rf_type < 0) {
-                       rf_idx = xatoi_u(rf_name);
+                       rf_idx = xatoi_positive(rf_name);
                }
        }
 
index 83b5a77..76231df 100644 (file)
@@ -1,18 +1,30 @@
 /* vi: set sw=4 ts=4: */
 /*
- * runlevel    Prints out the previous and the current runlevel.
+ * Prints out the previous and the current runlevel.
  *
- * Version:    @(#)runlevel  1.20  16-Apr-1997  MvS
+ * Version: @(#)runlevel  1.20  16-Apr-1997  MvS
  *
- *             This file is part of the sysvinit suite,
- *             Copyright 1991-1997 Miquel van Smoorenburg.
+ * This file is part of the sysvinit suite,
+ * Copyright 1991-1997 Miquel van Smoorenburg.
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * initially busyboxified by Bernhard Reutner-Fischer
  */
+
+//usage:#define runlevel_trivial_usage
+//usage:       "[FILE]"
+//usage:#define runlevel_full_usage "\n\n"
+//usage:       "Find the current and previous system runlevel\n"
+//usage:       "\n"
+//usage:       "If no utmp FILE exists or if no runlevel record can be found,\n"
+//usage:       "print \"unknown\""
+//usage:
+//usage:#define runlevel_example_usage
+//usage:       "$ runlevel /var/run/utmp\n"
+//usage:       "N 2"
+
 #include "libbb.h"
-#include <utmp.h>
 
 int runlevel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int runlevel_main(int argc UNUSED_PARAM, char **argv)
index 4c5d5a1..1dffb59 100644 (file)
  *
  * Copyright (C) 2001 Hewlett-Packard Laboratories
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * This was originally written for blob and then adapted for busybox.
  */
 
+//usage:#define rx_trivial_usage
+//usage:       "FILE"
+//usage:#define rx_full_usage "\n\n"
+//usage:       "Receive a file using the xmodem protocol"
+//usage:
+//usage:#define rx_example_usage
+//usage:       "$ rx /tmp/foo\n"
+
 #include "libbb.h"
 
 #define SOH 0x01
@@ -100,12 +108,10 @@ static int receive(/*int read_fd, */int file_fd)
                        }
                }
                /* Write previously received block */
-               if (blockLength) {
-                       errno = 0;
-                       if (full_write(file_fd, blockBuf, blockLength) != blockLength) {
-                               bb_perror_msg("can't write to file");
-                               goto fatal;
-                       }
+               errno = 0;
+               if (full_write(file_fd, blockBuf, blockLength) != blockLength) {
+                       bb_perror_msg(bb_msg_write_error);
+                       goto fatal;
                }
 
                timeout = TIMEOUT;
@@ -147,23 +153,20 @@ static int receive(/*int read_fd, */int file_fd)
                        blockBuf[i] = cc;
                }
 
+               cksum_or_crc = read_byte(TIMEOUT);
+               if (cksum_or_crc < 0)
+                       goto timeout;
                if (do_crc) {
-                       cksum_or_crc = read_byte(TIMEOUT);
-                       if (cksum_or_crc < 0)
-                               goto timeout;
                        cksum_or_crc = (cksum_or_crc << 8) | read_byte(TIMEOUT);
                        if (cksum_or_crc < 0)
                                goto timeout;
-               } else {
-                       cksum_or_crc = read_byte(TIMEOUT);
-                       if (cksum_or_crc < 0)
-                               goto timeout;
                }
 
                if (blockNo == ((wantBlockNo - 1) & 0xff)) {
                        /* a repeat of the last block is ok, just ignore it. */
                        /* this also ignores the initial block 0 which is */
                        /* meta data. */
+                       blockLength = 0;
                        goto next;
                }
                if (blockNo != (wantBlockNo & 0xff)) {
@@ -190,8 +193,8 @@ static int receive(/*int read_fd, */int file_fd)
                }
                if (cksum_or_crc != expected) {
                        bb_error_msg(do_crc ? "crc error, expected 0x%04x, got 0x%04x"
-                                          : "checksum error, expected 0x%02x, got 0x%02x",
-                                       expected, cksum_or_crc);
+                                       : "checksum error, expected 0x%02x, got 0x%02x",
+                               expected, cksum_or_crc);
                        goto error;
                }
 
@@ -204,6 +207,7 @@ static int receive(/*int read_fd, */int file_fd)
                continue;
  error:
  timeout:
+               blockLength = 0;
                errors++;
                if (errors == MAXERRORS) {
                        /* Abort */
diff --git a/miscutils/setserial.c b/miscutils/setserial.c
new file mode 100644 (file)
index 0000000..dfed330
--- /dev/null
@@ -0,0 +1,763 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * setserial implementation for busybox
+ *
+ *
+ * Copyright (C) 2011 Marek Bečka <yuen@klacno.sk>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//config:config SETSERIAL
+//config:      bool "setserial"
+//config:      default y
+//config:      select PLATFORM_LINUX
+//config:      help
+//config:        Retrieve or set Linux serial port.
+
+//applet:IF_SETSERIAL(APPLET(setserial, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SETSERIAL) += setserial.o
+
+#include "libbb.h"
+#include <assert.h>
+
+#ifndef PORT_UNKNOWN
+# define PORT_UNKNOWN            0
+#endif
+#ifndef PORT_8250
+# define PORT_8250               1
+#endif
+#ifndef PORT_16450
+# define PORT_16450              2
+#endif
+#ifndef PORT_16550
+# define PORT_16550              3
+#endif
+#ifndef PORT_16550A
+# define PORT_16550A             4
+#endif
+#ifndef PORT_CIRRUS
+# define PORT_CIRRUS             5
+#endif
+#ifndef PORT_16650
+# define PORT_16650              6
+#endif
+#ifndef PORT_16650V2
+# define PORT_16650V2            7
+#endif
+#ifndef PORT_16750
+# define PORT_16750              8
+#endif
+#ifndef PORT_STARTECH
+# define PORT_STARTECH           9
+#endif
+#ifndef PORT_16C950
+# define PORT_16C950            10
+#endif
+#ifndef PORT_16654
+# define PORT_16654             11
+#endif
+#ifndef PORT_16850
+# define PORT_16850             12
+#endif
+#ifndef PORT_RSA
+# define PORT_RSA               13
+#endif
+#ifndef PORT_NS16550A
+# define PORT_NS16550A          14
+#endif
+#ifndef PORT_XSCALE
+# define PORT_XSCALE            15
+#endif
+#ifndef PORT_RM9000
+# define PORT_RM9000            16
+#endif
+#ifndef PORT_OCTEON
+# define PORT_OCTEON            17
+#endif
+#ifndef PORT_AR7
+# define PORT_AR7               18
+#endif
+#ifndef PORT_U6_16550A
+# define PORT_U6_16550A         19
+#endif
+
+#ifndef ASYNCB_HUP_NOTIFY
+# define ASYNCB_HUP_NOTIFY       0
+#endif
+#ifndef ASYNCB_FOURPORT
+# define ASYNCB_FOURPORT         1
+#endif
+#ifndef ASYNCB_SAK
+# define ASYNCB_SAK              2
+#endif
+#ifndef ASYNCB_SPLIT_TERMIOS
+# define ASYNCB_SPLIT_TERMIOS    3
+#endif
+#ifndef ASYNCB_SPD_HI
+# define ASYNCB_SPD_HI           4
+#endif
+#ifndef ASYNCB_SPD_VHI
+# define ASYNCB_SPD_VHI          5
+#endif
+#ifndef ASYNCB_SKIP_TEST
+# define ASYNCB_SKIP_TEST        6
+#endif
+#ifndef ASYNCB_AUTO_IRQ
+# define ASYNCB_AUTO_IRQ         7
+#endif
+#ifndef ASYNCB_SESSION_LOCKOUT
+# define ASYNCB_SESSION_LOCKOUT  8
+#endif
+#ifndef ASYNCB_PGRP_LOCKOUT
+# define ASYNCB_PGRP_LOCKOUT     9
+#endif
+#ifndef ASYNCB_CALLOUT_NOHUP
+# define ASYNCB_CALLOUT_NOHUP   10
+#endif
+#ifndef ASYNCB_SPD_SHI
+# define ASYNCB_SPD_SHI         12
+#endif
+#ifndef ASYNCB_LOW_LATENCY
+# define ASYNCB_LOW_LATENCY     13
+#endif
+#ifndef ASYNCB_BUGGY_UART
+# define ASYNCB_BUGGY_UART      14
+#endif
+
+#ifndef ASYNC_HUP_NOTIFY
+# define ASYNC_HUP_NOTIFY       (1U << ASYNCB_HUP_NOTIFY)
+#endif
+#ifndef ASYNC_FOURPORT
+# define ASYNC_FOURPORT         (1U << ASYNCB_FOURPORT)
+#endif
+#ifndef ASYNC_SAK
+# define ASYNC_SAK              (1U << ASYNCB_SAK)
+#endif
+#ifndef ASYNC_SPLIT_TERMIOS
+# define ASYNC_SPLIT_TERMIOS    (1U << ASYNCB_SPLIT_TERMIOS)
+#endif
+#ifndef ASYNC_SPD_HI
+# define ASYNC_SPD_HI           (1U << ASYNCB_SPD_HI)
+#endif
+#ifndef ASYNC_SPD_VHI
+# define ASYNC_SPD_VHI          (1U << ASYNCB_SPD_VHI)
+#endif
+#ifndef ASYNC_SKIP_TEST
+# define ASYNC_SKIP_TEST        (1U << ASYNCB_SKIP_TEST)
+#endif
+#ifndef ASYNC_AUTO_IRQ
+# define ASYNC_AUTO_IRQ         (1U << ASYNCB_AUTO_IRQ)
+#endif
+#ifndef ASYNC_SESSION_LOCKOUT
+# define ASYNC_SESSION_LOCKOUT  (1U << ASYNCB_SESSION_LOCKOUT)
+#endif
+#ifndef ASYNC_PGRP_LOCKOUT
+# define ASYNC_PGRP_LOCKOUT     (1U << ASYNCB_PGRP_LOCKOUT)
+#endif
+#ifndef ASYNC_CALLOUT_NOHUP
+# define ASYNC_CALLOUT_NOHUP    (1U << ASYNCB_CALLOUT_NOHUP)
+#endif
+#ifndef ASYNC_SPD_SHI
+# define ASYNC_SPD_SHI          (1U << ASYNCB_SPD_SHI)
+#endif
+#ifndef ASYNC_LOW_LATENCY
+# define ASYNC_LOW_LATENCY      (1U << ASYNCB_LOW_LATENCY)
+#endif
+#ifndef ASYNC_BUGGY_UART
+# define ASYNC_BUGGY_UART       (1U << ASYNCB_BUGGY_UART)
+#endif
+
+#ifndef ASYNC_SPD_CUST
+# define ASYNC_SPD_CUST         (ASYNC_SPD_HI|ASYNC_SPD_VHI)
+#endif
+#ifndef ASYNC_SPD_WARP
+# define ASYNC_SPD_WARP         (ASYNC_SPD_HI|ASYNC_SPD_SHI)
+#endif
+#ifndef ASYNC_SPD_MASK
+# define ASYNC_SPD_MASK         (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI)
+#endif
+
+#ifndef ASYNC_CLOSING_WAIT_INF
+# define ASYNC_CLOSING_WAIT_INF         0
+#endif
+#ifndef ASYNC_CLOSING_WAIT_NONE
+# define ASYNC_CLOSING_WAIT_NONE        65535
+#endif
+
+#ifndef _LINUX_SERIAL_H
+struct serial_struct {
+       int     type;
+       int     line;
+       unsigned int    port;
+       int     irq;
+       int     flags;
+       int     xmit_fifo_size;
+       int     custom_divisor;
+       int     baud_base;
+       unsigned short  close_delay;
+       char    io_type;
+       char    reserved_char[1];
+       int     hub6;
+       unsigned short  closing_wait; /* time to wait before closing */
+       unsigned short  closing_wait2; /* no longer used... */
+       unsigned char   *iomem_base;
+       unsigned short  iomem_reg_shift;
+       unsigned int    port_high;
+       unsigned long   iomap_base;     /* cookie passed into ioremap */
+};
+#endif
+
+//usage:#define setserial_trivial_usage
+//usage:       "[-gabGvzV] DEVICE [PARAMETER [ARG]]..."
+//usage:#define setserial_full_usage "\n\n"
+//usage:       "Request or set Linux serial port information\n"
+//usage:       "\n"
+//usage:       "       -g      Interpret parameters as list of devices for reporting\n"
+//usage:       "       -a      Print all available information\n"
+//usage:       "       -b      Print summary information\n"
+//usage:       "       -G      Print in form which can be fed back\n"
+//usage:       "               to setserial as command line parameters\n"
+//usage:       "       -z      Zero out serial flags before setting\n"
+//usage:       "       -v      Verbose\n"
+//usage:       "\n"
+//usage:       "Parameters: (* = takes an argument, ^ = can be turned off by preceding ^)\n"
+//usage:       "       *port, *irq, *divisor, *uart, *baud_base, *close_delay, *closing_wait,\n"
+//usage:       "       ^fourport, ^auto_irq, ^skip_test, ^sak, ^session_lockout, ^pgrp_lockout,\n"
+//usage:       "       ^callout_nohup, ^split_termios, ^hup_notify, ^low_latency, autoconfig,\n"
+//usage:       "       spd_normal, spd_hi, spd_vhi, spd_shi, spd_warp, spd_cust\n"
+//usage:       "\n"
+//usage:       "UART types:\n"
+//usage:       "       unknown, 8250, 16450, 16550, 16550A, Cirrus, 16650, 16650V2, 16750,\n"
+//usage:       "       16950, 16954, 16654, 16850, RSA, NS16550A, XSCALE, RM9000, OCTEON, AR7,\n"
+//usage:       "       U6_16550A"
+
+#define OPT_PRINT_SUMMARY       (1 << 0)
+#define OPT_PRINT_FEDBACK       (1 << 1)
+#define OPT_PRINT_ALL           (1 << 2)
+#define OPT_VERBOSE             (1 << 3)
+#define OPT_ZERO                (1 << 4)
+#define OPT_GET                 (1 << 5)
+
+#define OPT_MODE_MASK \
+       (OPT_PRINT_ALL | OPT_PRINT_SUMMARY | OPT_PRINT_FEDBACK)
+
+enum print_mode
+{
+       PRINT_NORMAL  = 0,
+       PRINT_SUMMARY = (1 << 0),
+       PRINT_FEDBACK = (1 << 1),
+       PRINT_ALL     = (1 << 2),
+};
+
+#define CTL_SET                 (1 << 0)
+#define CTL_CONFIG              (1 << 1)
+#define CTL_GET                 (1 << 2)
+#define CTL_CLOSE               (1 << 3)
+#define CTL_NODIE               (1 << 4)
+
+static const char serial_types[] =
+       "unknown\0"             /* 0 */
+       "8250\0"                /* 1 */
+       "16450\0"               /* 2 */
+       "16550\0"               /* 3 */
+       "16550A\0"              /* 4 */
+       "Cirrus\0"              /* 5 */
+       "16650\0"               /* 6 */
+       "16650V2\0"             /* 7 */
+       "16750\0"               /* 8 */
+       "16950\0"               /* 9 UNIMPLEMENTED: also know as "16950/954" */
+       "16954\0"               /* 10 */
+       "16654\0"               /* 11 */
+       "16850\0"               /* 12 */
+       "RSA\0"                 /* 13 */
+#ifndef SETSERIAL_BASE
+       "NS16550A\0"            /* 14 */
+       "XSCALE\0"              /* 15 */
+       "RM9000\0"              /* 16 */
+       "OCTEON\0"              /* 17 */
+       "AR7\0"                 /* 18 */
+       "U6_16550A\0"           /* 19 */
+#endif
+;
+
+#ifndef SETSERIAL_BASE
+# define MAX_SERIAL_TYPE       19
+#else
+# define MAX_SERIAL_TYPE       13
+#endif
+
+static const char commands[] =
+       "spd_normal\0"
+       "spd_hi\0"
+       "spd_vhi\0"
+       "spd_shi\0"
+       "spd_warp\0"
+       "spd_cust\0"
+
+       "sak\0"
+       "fourport\0"
+       "hup_notify\0"
+       "skip_test\0"
+       "auto_irq\0"
+       "split_termios\0"
+       "session_lockout\0"
+       "pgrp_lockout\0"
+       "callout_nohup\0"
+       "low_latency\0"
+
+       "port\0"
+       "irq\0"
+       "divisor\0"
+       "uart\0"
+       "baud_base\0"
+       "close_delay\0"
+       "closing_wait\0"
+
+       "autoconfig\0"
+;
+
+enum
+{
+       CMD_SPD_NORMAL = 0,
+       CMD_SPD_HI,
+       CMD_SPD_VHI,
+       CMD_SPD_SHI,
+       CMD_SPD_WARP,
+       CMD_SPD_CUST,
+
+       CMD_FLAG_SAK,
+       CMD_FLAG_FOURPORT,
+       CMD_FLAG_NUP_NOTIFY,
+       CMD_FLAG_SKIP_TEST,
+       CMD_FLAG_AUTO_IRQ,
+       CMD_FLAG_SPLIT_TERMIOS,
+       CMD_FLAG_SESSION_LOCKOUT,
+       CMD_FLAG_PGRP_LOCKOUT,
+       CMD_FLAG_CALLOUT_NOHUP,
+       CMD_FLAG_LOW_LATENCY,
+
+       CMD_PORT,
+       CMD_IRQ,
+       CMD_DIVISOR,
+       CMD_UART,
+       CMD_BASE,
+       CMD_DELAY,
+       CMD_WAIT,
+
+       CMD_AUTOCONFIG,
+
+       CMD_FLAG_FIRST = CMD_FLAG_SAK,
+       CMD_FLAG_LAST  = CMD_FLAG_LOW_LATENCY,
+};
+
+static bool cmd_noprint(int cmd)
+{
+       return (cmd >= CMD_FLAG_SKIP_TEST && cmd <= CMD_FLAG_CALLOUT_NOHUP);
+}
+
+static bool cmd_is_flag(int cmd)
+{
+       return (cmd >= CMD_FLAG_FIRST && cmd <= CMD_FLAG_LAST);
+}
+
+static bool cmd_need_arg(int cmd)
+{
+       return (cmd >= CMD_PORT && cmd <= CMD_WAIT);
+}
+
+#define ALL_SPD ( \
+       ASYNC_SPD_HI | ASYNC_SPD_VHI | ASYNC_SPD_SHI | \
+       ASYNC_SPD_WARP | ASYNC_SPD_CUST \
+       )
+
+#define ALL_FLAGS ( \
+       ASYNC_SAK | ASYNC_FOURPORT | ASYNC_HUP_NOTIFY | \
+       ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ | ASYNC_SPLIT_TERMIOS | \
+       ASYNC_SESSION_LOCKOUT | ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP | \
+       ASYNC_LOW_LATENCY \
+       )
+
+#if (ALL_SPD | ALL_FLAGS) > 0xffff
+# error "Unexpected flags size"
+#endif
+
+static const uint16_t setbits[CMD_FLAG_LAST + 1] =
+{
+       0,
+       ASYNC_SPD_HI,
+       ASYNC_SPD_VHI,
+       ASYNC_SPD_SHI,
+       ASYNC_SPD_WARP,
+       ASYNC_SPD_CUST,
+
+       ASYNC_SAK,
+       ASYNC_FOURPORT,
+       ASYNC_HUP_NOTIFY,
+       ASYNC_SKIP_TEST,
+       ASYNC_AUTO_IRQ,
+       ASYNC_SPLIT_TERMIOS,
+       ASYNC_SESSION_LOCKOUT,
+       ASYNC_PGRP_LOCKOUT,
+       ASYNC_CALLOUT_NOHUP,
+       ASYNC_LOW_LATENCY
+};
+
+static const char STR_INFINITE[] = "infinite";
+static const char STR_NONE[] = "none";
+
+static const char *uart_type(int type)
+{
+       if (type > MAX_SERIAL_TYPE)
+               return "undefined";
+
+       return nth_string(serial_types, type);
+}
+
+/* libbb candidate */
+static int index_in_strings_case_insensitive(const char *strings, const char *key)
+{
+       int idx = 0;
+
+       while (*strings) {
+               if (strcasecmp(strings, key) == 0) {
+                       return idx;
+               }
+               strings += strlen(strings) + 1; /* skip NUL */
+               idx++;
+       }
+       return -1;
+}
+
+static int uart_id(const char *name)
+{
+       return index_in_strings_case_insensitive(serial_types, name);
+}
+
+static const char *get_spd(int flags, enum print_mode mode)
+{
+       int idx;
+
+       switch (flags & ASYNC_SPD_MASK) {
+       case ASYNC_SPD_HI:
+               idx = CMD_SPD_HI;
+               break;
+       case ASYNC_SPD_VHI:
+               idx = CMD_SPD_VHI;
+               break;
+       case ASYNC_SPD_SHI:
+               idx = CMD_SPD_SHI;
+               break;
+       case ASYNC_SPD_WARP:
+               idx = CMD_SPD_WARP;
+               break;
+       case ASYNC_SPD_CUST:
+               idx = CMD_SPD_CUST;
+               break;
+       default:
+               if (mode < PRINT_FEDBACK)
+                       return NULL;
+               idx = CMD_SPD_NORMAL;
+       }
+
+       return nth_string(commands, idx);
+}
+
+static int get_numeric(const char *arg)
+{
+       return bb_strtol(arg, NULL, 0);
+}
+
+static int get_wait(const char *arg)
+{
+       if (strcasecmp(arg, STR_NONE) == 0)
+               return ASYNC_CLOSING_WAIT_NONE;
+
+       if (strcasecmp(arg, STR_INFINITE) == 0)
+               return ASYNC_CLOSING_WAIT_INF;
+
+       return get_numeric(arg);
+}
+
+static int get_uart(const char *arg)
+{
+       int uart = uart_id(arg);
+
+       if (uart < 0)
+               bb_error_msg_and_die("illegal UART type: %s", arg);
+
+       return uart;
+}
+
+static int serial_open(const char *dev, bool quiet)
+{
+       int fd;
+
+       fd = device_open(dev, O_RDWR | O_NONBLOCK);
+       if (fd < 0 && !quiet)
+               bb_simple_perror_msg(dev);
+
+       return fd;
+}
+
+static int serial_ctl(int fd, int ops, struct serial_struct *serinfo)
+{
+       int ret = 0;
+       const char *err;
+
+       if (ops & CTL_SET) {
+               ret = ioctl(fd, TIOCSSERIAL, serinfo);
+               if (ret < 0) {
+                       err = "can't set serial info";
+                       goto fail;
+               }
+       }
+
+       if (ops & CTL_CONFIG) {
+               ret = ioctl(fd, TIOCSERCONFIG);
+               if (ret < 0) {
+                       err = "can't autoconfigure port";
+                       goto fail;
+               }
+       }
+
+       if (ops & CTL_GET) {
+               ret = ioctl(fd, TIOCGSERIAL, serinfo);
+               if (ret < 0) {
+                       err = "can't get serial info";
+                       goto fail;
+               }
+       }
+ nodie:
+       if (ops & CTL_CLOSE)
+               close(fd);
+
+       return ret;
+ fail:
+       bb_simple_perror_msg(err);
+       if (ops & CTL_NODIE)
+               goto nodie;
+       exit(EXIT_FAILURE);
+}
+
+static void print_flag(const char **prefix, const char *flag)
+{
+       printf("%s%s", *prefix, flag);
+       *prefix = " ";
+}
+
+static void print_serial_flags(int serial_flags, enum print_mode mode,
+                               const char *prefix, const char *postfix)
+{
+       int i;
+       const char *spd, *pr;
+
+       pr = prefix;
+
+       spd = get_spd(serial_flags, mode);
+       if (spd)
+               print_flag(&pr, spd);
+
+       for (i = CMD_FLAG_FIRST; i <= CMD_FLAG_LAST; i++) {
+               if ((serial_flags & setbits[i])
+                && (mode > PRINT_SUMMARY || !cmd_noprint(i))
+               ) {
+                       print_flag(&pr, nth_string(commands, i));
+               }
+       }
+
+       puts(pr == prefix ? "" : postfix);
+}
+
+static void print_closing_wait(unsigned int closing_wait)
+{
+       switch (closing_wait) {
+       case ASYNC_CLOSING_WAIT_NONE:
+               puts(STR_NONE);
+               break;
+       case ASYNC_CLOSING_WAIT_INF:
+               puts(STR_INFINITE);
+               break;
+       default:
+               printf("%u\n", closing_wait);
+       }
+}
+
+static void serial_get(const char *device, enum print_mode mode)
+{
+       int fd, ret;
+       const char *uart, *prefix, *postfix;
+       struct serial_struct serinfo;
+
+       fd = serial_open(device, /*quiet:*/ mode == PRINT_SUMMARY);
+       if (fd < 0)
+               return;
+
+       ret = serial_ctl(fd, CTL_GET | CTL_CLOSE | CTL_NODIE, &serinfo);
+       if (ret < 0)
+               return;
+
+       uart = uart_type(serinfo.type);
+       prefix = ", Flags: ";
+       postfix = "";
+
+       switch (mode) {
+       case PRINT_NORMAL:
+               printf("%s, UART: %s, Port: 0x%.4x, IRQ: %d",
+                       device, uart, serinfo.port, serinfo.irq);
+               break;
+       case PRINT_SUMMARY:
+               if (!serinfo.type)
+                       return;
+               printf("%s at 0x%.4x (irq = %d) is a %s",
+                       device, serinfo.port, serinfo.irq, uart);
+               prefix = " (";
+               postfix = ")";
+               break;
+       case PRINT_FEDBACK:
+               printf("%s uart %s port 0x%.4x irq %d baud_base %d", device,
+                       uart, serinfo.port, serinfo.irq, serinfo.baud_base);
+               prefix = " ";
+               break;
+       case PRINT_ALL:
+               printf("%s, Line %d, UART: %s, Port: 0x%.4x, IRQ: %d\n",
+                       device, serinfo.line, uart, serinfo.port, serinfo.irq);
+               printf("\tBaud_base: %d, close_delay: %u, divisor: %d\n",
+                       serinfo.baud_base, serinfo.close_delay,
+                       serinfo.custom_divisor);
+               printf("\tclosing_wait: ");
+               print_closing_wait(serinfo.closing_wait);
+               prefix = "\tFlags: ";
+               postfix = "\n";
+               break;
+       default:
+               assert(0);
+       }
+
+       print_serial_flags(serinfo.flags, mode, prefix, postfix);
+}
+
+static int find_cmd(const char *cmd)
+{
+       int idx;
+
+       idx = index_in_strings_case_insensitive(commands, cmd);
+       if (idx < 0)
+               bb_error_msg_and_die("invalid flag: %s", cmd);
+
+       return idx;
+}
+
+static void serial_set(char **arg, int opts)
+{
+       struct serial_struct serinfo;
+       int cmd;
+       const char *word;
+       int fd;
+
+       fd = serial_open(*arg++, /*quiet:*/ false);
+       if (fd < 0)
+               exit(201);
+
+       serial_ctl(fd, CTL_GET, &serinfo);
+
+       if (opts & OPT_ZERO)
+               serinfo.flags = 0;
+
+       while (*arg) {
+               int invert;
+
+               word = *arg++;
+               invert = (*word == '^');
+               word += invert;
+
+               cmd = find_cmd(word);
+
+               if (*arg == NULL && cmd_need_arg(cmd))
+                       bb_error_msg_and_die(bb_msg_requires_arg, word);
+
+               if (invert && !cmd_is_flag(cmd))
+                       bb_error_msg_and_die("can't invert %s", word);
+
+               switch (cmd) {
+               case CMD_SPD_NORMAL:
+               case CMD_SPD_HI:
+               case CMD_SPD_VHI:
+               case CMD_SPD_SHI:
+               case CMD_SPD_WARP:
+               case CMD_SPD_CUST:
+                       serinfo.flags &= ~ASYNC_SPD_MASK;
+                       /* fallthrough */
+               case CMD_FLAG_SAK:
+               case CMD_FLAG_FOURPORT:
+               case CMD_FLAG_NUP_NOTIFY:
+               case CMD_FLAG_SKIP_TEST:
+               case CMD_FLAG_AUTO_IRQ:
+               case CMD_FLAG_SPLIT_TERMIOS:
+               case CMD_FLAG_SESSION_LOCKOUT:
+               case CMD_FLAG_PGRP_LOCKOUT:
+               case CMD_FLAG_CALLOUT_NOHUP:
+               case CMD_FLAG_LOW_LATENCY:
+                       if (invert)
+                               serinfo.flags &= ~setbits[cmd];
+                       else
+                               serinfo.flags |= setbits[cmd];
+                       break;
+               case CMD_PORT:
+                       serinfo.port = get_numeric(*arg++);
+                       break;
+               case CMD_IRQ:
+                       serinfo.irq = get_numeric(*arg++);
+                       break;
+               case CMD_DIVISOR:
+                       serinfo.custom_divisor = get_numeric(*arg++);
+                       break;
+               case CMD_UART:
+                       serinfo.type = get_uart(*arg++);
+                       break;
+               case CMD_BASE:
+                       serinfo.baud_base = get_numeric(*arg++);
+                       break;
+               case CMD_DELAY:
+                       serinfo.close_delay = get_numeric(*arg++);
+                       break;
+               case CMD_WAIT:
+                       serinfo.closing_wait = get_wait(*arg++);
+                       break;
+               case CMD_AUTOCONFIG:
+                       serial_ctl(fd, CTL_SET | CTL_CONFIG | CTL_GET, &serinfo);
+                       break;
+               default:
+                       assert(0);
+               }
+       }
+
+       serial_ctl(fd, CTL_SET | CTL_CLOSE, &serinfo);
+}
+
+int setserial_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int setserial_main(int argc UNUSED_PARAM, char **argv)
+{
+       int opts;
+
+       opt_complementary = "-1:b-aG:G-ab:a-bG";
+       opts = getopt32(argv, "bGavzg");
+       argv += optind;
+
+       if (!argv[1]) /* one arg only? */
+               opts |= OPT_GET;
+
+       if (!(opts & OPT_GET)) {
+               serial_set(argv, opts);
+               argv[1] = NULL;
+       }
+
+       if (opts & (OPT_VERBOSE | OPT_GET)) {
+               do {
+                       serial_get(*argv++, opts & OPT_MODE_MASK);
+               } while (*argv);
+       }
+
+       return EXIT_SUCCESS;
+}
index c573fae..637081b 100644 (file)
  * - busyboxed
  */
 
+//usage:#define setsid_trivial_usage
+//usage:       "PROG ARGS"
+//usage:#define setsid_full_usage "\n\n"
+//usage:       "Run PROG in a new session. PROG will have no controlling terminal\n"
+//usage:       "and will not be affected by keyboard signals (Ctrl-C etc).\n"
+//usage:       "See setsid(2) for details."
+
 #include "libbb.h"
 
 int setsid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -24,7 +31,17 @@ int setsid_main(int argc UNUSED_PARAM, char **argv)
 
        /* setsid() is allowed only when we are not a process group leader.
         * Otherwise our PID serves as PGID of some existing process group
-        * and cannot be used as PGID of a new process group. */
+        * and cannot be used as PGID of a new process group.
+        *
+        * Example: setsid() below fails when run alone in interactive shell:
+        *  $ setsid PROG
+        * because shell's child (setsid) is put in a new process group.
+        * But doesn't fail if shell is not interactive
+        * (and therefore doesn't create process groups for pipes),
+        * or if setsid is not the first process in the process group:
+        *  $ true | setsid PROG
+        * or if setsid is executed in backquotes (`setsid PROG`)...
+        */
        if (setsid() < 0) {
                pid_t pid = fork_or_rexec(argv);
                if (pid != 0) {
@@ -36,7 +53,7 @@ int setsid_main(int argc UNUSED_PARAM, char **argv)
                         * However, the code is larger and upstream
                         * does not do such trick.
                         */
-                       exit(EXIT_SUCCESS);
+                       return EXIT_SUCCESS;
                }
 
                /* child */
index b4c5854..9f50182 100644 (file)
@@ -4,15 +4,24 @@
  *
  * Copyright 2003 Tito Ragusa <farmatito@tiscali.it>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define strings_trivial_usage
+//usage:       "[-afo] [-n LEN] [FILE]..."
+//usage:#define strings_full_usage "\n\n"
+//usage:       "Display printable strings in a binary file\n"
+//usage:     "\n       -a      Scan whole file (default)"
+//usage:     "\n       -f      Precede strings with filenames"
+//usage:     "\n       -n LEN  At least LEN characters form a string (default 4)"
+//usage:     "\n       -o      Precede strings with decimal offsets"
+
 #include "libbb.h"
 
-#define WHOLE_FILE             1
-#define PRINT_NAME             2
-#define PRINT_OFFSET   4
-#define SIZE                   8
+#define WHOLE_FILE    1
+#define PRINT_NAME    2
+#define PRINT_OFFSET  4
+#define SIZE          8
 
 int strings_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int strings_main(int argc UNUSED_PARAM, char **argv)
index 08198d5..4a9e323 100644 (file)
@@ -3,9 +3,26 @@
  * taskset - retrieve or set a processes' CPU affinity
  * Copyright (c) 2006 Bernhard Reutner-Fischer
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define taskset_trivial_usage
+//usage:       "[-p] [MASK] [PID | PROG ARGS]"
+//usage:#define taskset_full_usage "\n\n"
+//usage:       "Set or get CPU affinity\n"
+//usage:     "\n       -p      Operate on an existing PID"
+//usage:
+//usage:#define taskset_example_usage
+//usage:       "$ taskset 0x7 ./dgemm_test&\n"
+//usage:       "$ taskset -p 0x1 $!\n"
+//usage:       "pid 4790's current affinity mask: 7\n"
+//usage:       "pid 4790's new affinity mask: 1\n"
+//usage:       "$ taskset 0x7 /bin/sh -c './taskset -p 0x1 $$'\n"
+//usage:       "pid 6671's current affinity mask: 1\n"
+//usage:       "pid 6671's new affinity mask: 1\n"
+//usage:       "$ taskset -p 1\n"
+//usage:       "pid 1's current affinity mask: 3\n"
+
 #include <sched.h>
 #include "libbb.h"
 
index 9facc36..19b0b44 100644 (file)
@@ -2,14 +2,21 @@
 /* 'time' utility to display resource usage of processes.
    Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc.
 
-   Licensed under GPL version 2, see file LICENSE in this tarball for details.
+   Licensed under GPLv2, see file LICENSE in this source tree.
 */
 /* Originally written by David Keppel <pardo@cs.washington.edu>.
    Heavily modified by David MacKenzie <djm@gnu.ai.mit.edu>.
    Heavily modified for busybox by Erik Andersen <andersen@codepoet.org>
 */
 
+//usage:#define time_trivial_usage
+//usage:       "[-v] PROG ARGS"
+//usage:#define time_full_usage "\n\n"
+//usage:       "Run PROG, display resource usage when it exits\n"
+//usage:     "\n       -v      Verbose"
+
 #include "libbb.h"
+#include <sys/resource.h> /* getrusage */
 
 /* Information on the resources used by a child process.  */
 typedef struct {
@@ -63,7 +70,7 @@ static void resuse_end(pid_t pid, resource_t *resp)
        pid_t caught;
 
        /* Ignore signals, but don't ignore the children.  When wait3
-          returns the child process, set the time the command finished. */
+        * returns the child process, set the time the command finished. */
        while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
                if (caught == -1 && errno != EINTR) {
                        bb_perror_msg("wait");
index 48b8d8f..9d56593 100644 (file)
  * rewrite  14-11-2008 vda
  */
 
+//usage:#define timeout_trivial_usage
+//usage:       "[-t SECS] [-s SIG] PROG ARGS"
+//usage:#define timeout_full_usage "\n\n"
+//usage:       "Runs PROG. Sends SIG to it if it is not gone in SECS seconds.\n"
+//usage:       "Defaults: SECS: 10, SIG: TERM."
+
 #include "libbb.h"
 
 int timeout_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -83,7 +89,7 @@ int timeout_main(int argc UNUSED_PARAM, char **argv)
                bb_daemonize_or_rexec(0, argv);
                /* Here we are grandchild. Sleep, then kill grandparent */
  grandchild:
-               /* Just sleep(NUGE_NUM); kill(parent) may kill wrong process! */
+               /* Just sleep(HUGE_NUM); kill(parent) may kill wrong process! */
                while (1) {
                        sleep(1);
                        if (--timeout <= 0)
index ca9a2ec..d2d48d0 100644 (file)
@@ -7,8 +7,14 @@
  *
  * Copyright (C) 2007 by Denys Vlasenko <vda.linux@googlemail.com>
  *
- * Licensed under the GPL v2, see the file LICENSE in this tarball.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define ttysize_trivial_usage
+//usage:       "[w] [h]"
+//usage:#define ttysize_full_usage "\n\n"
+//usage:       "Print dimension(s) of stdin's terminal, on error return 80x25"
+
 #include "libbb.h"
 
 int ttysize_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
diff --git a/miscutils/ubi_attach_detach.c b/miscutils/ubi_attach_detach.c
deleted file mode 100644 (file)
index 7b92a8a..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Ported to busybox from mtd-utils.
- *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
- */
-
-//applet:IF_UBIATTACH(APPLET_ODDNAME(ubiattach, ubi_attach_detach, _BB_DIR_USR_SBIN, _BB_SUID_DROP, ubiattach))
-//applet:IF_UBIDETACH(APPLET_ODDNAME(ubidetach, ubi_attach_detach, _BB_DIR_USR_SBIN, _BB_SUID_DROP, ubidetach))
-
-//kbuild:lib-$(CONFIG_UBIATTACH)   += ubi_attach_detach.o
-//kbuild:lib-$(CONFIG_UBIDETACH)   += ubi_attach_detach.o
-
-//config:config UBIATTACH
-//config:      bool "ubiattach"
-//config:      default n
-//config:      help
-//config:        Attach MTD device to an UBI device.
-//config:
-//config:config UBIDETACH
-//config:      bool "ubidetach"
-//config:      default n
-//config:      help
-//config:        Detach MTD device from an UBI device.
-
-#include "libbb.h"
-#include <mtd/ubi-user.h>
-
-#define OPTION_M       (1 << 0)
-#define OPTION_D       (1 << 1)
-
-#define do_attach (ENABLE_UBIATTACH && \
-               (!ENABLE_UBIDETACH || (applet_name[3] == 'a')))
-
-//usage:#define ubiattach_trivial_usage
-//usage:       "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV"
-//usage:#define ubiattach_full_usage "\n\n"
-//usage:       "Attach MTD device to UBI\n"
-//usage:     "\nOptions:"
-//usage:     "\n       -m MTD_NUM      MTD device number to attach"
-//usage:     "\n       -d UBI_NUM      UBI device number to assign"
-//usage:
-//usage:#define ubidetach_trivial_usage
-//usage:       "-d UBI_NUM UBI_CTRL_DEV"
-//usage:#define ubidetach_full_usage "\n\n"
-//usage:       "Detach MTD device from UBI\n"
-//usage:     "\nOptions:"
-//usage:     "\n       -d UBI_NUM      UBI device number"
-
-int ubi_attach_detach_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int ubi_attach_detach_main(int argc UNUSED_PARAM, char **argv)
-{
-       unsigned opts;
-       char *ubi_ctrl;
-       //struct stat st;
-       struct ubi_attach_req req;
-       int fd;
-       int mtd_num;
-       int dev_num = UBI_DEV_NUM_AUTO;
-
-       opt_complementary = "=1:m+:d+";
-       opts = getopt32(argv, "m:d:", &mtd_num, &dev_num);
-       ubi_ctrl = argv[optind];
-
-       fd = xopen(ubi_ctrl, O_RDWR);
-       //fstat(fd, &st);
-       //if (!S_ISCHR(st.st_mode))
-       //      bb_error_msg_and_die("'%s' is not a char device", ubi_ctrl);
-
-       if (do_attach) {
-               if (!(opts & OPTION_M))
-                       bb_error_msg_and_die("%s device not specified", "MTD");
-
-               memset(&req, 0, sizeof(req));
-               req.mtd_num = mtd_num;
-               req.ubi_num = dev_num;
-
-               xioctl(fd, UBI_IOCATT, &req);
-       } else { /* detach */
-               if (!(opts & OPTION_D))
-                       bb_error_msg_and_die("%s device not specified", "UBI");
-
-               xioctl(fd, UBI_IOCDET, &dev_num);
-       }
-
-       if (ENABLE_FEATURE_CLEAN_UP)
-               close(fd);
-
-       return EXIT_SUCCESS;
-}
diff --git a/miscutils/ubi_tools.c b/miscutils/ubi_tools.c
new file mode 100644 (file)
index 0000000..b713935
--- /dev/null
@@ -0,0 +1,327 @@
+/* Ported to busybox from mtd-utils.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//config:config UBIATTACH
+//config:      bool "ubiattach"
+//config:      default y
+//config:      select PLATFORM_LINUX
+//config:      help
+//config:        Attach MTD device to an UBI device.
+//config:
+//config:config UBIDETACH
+//config:      bool "ubidetach"
+//config:      default y
+//config:      select PLATFORM_LINUX
+//config:      help
+//config:        Detach MTD device from an UBI device.
+//config:
+//config:config UBIMKVOL
+//config:      bool "ubimkvol"
+//config:      default y
+//config:      select PLATFORM_LINUX
+//config:      help
+//config:        Create a UBI volume.
+//config:
+//config:config UBIRMVOL
+//config:      bool "ubirmvol"
+//config:      default y
+//config:      select PLATFORM_LINUX
+//config:      help
+//config:        Delete a UBI volume.
+//config:
+//config:config UBIRSVOL
+//config:      bool "ubirsvol"
+//config:      default y
+//config:      select PLATFORM_LINUX
+//config:      help
+//config:        Resize a UBI volume.
+//config:
+//config:config UBIUPDATEVOL
+//config:      bool "ubiupdatevol"
+//config:      default y
+//config:      select PLATFORM_LINUX
+//config:      help
+//config:        Update a UBI volume.
+
+//applet:IF_UBIATTACH(APPLET_ODDNAME(ubiattach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiattach))
+//applet:IF_UBIDETACH(APPLET_ODDNAME(ubidetach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubidetach))
+//applet:IF_UBIMKVOL(APPLET_ODDNAME(ubimkvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubimkvol))
+//applet:IF_UBIRMVOL(APPLET_ODDNAME(ubirmvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirmvol))
+//applet:IF_UBIRSVOL(APPLET_ODDNAME(ubirsvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirsvol))
+//applet:IF_UBIUPDATEVOL(APPLET_ODDNAME(ubiupdatevol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiupdatevol))
+
+//kbuild:lib-$(CONFIG_UBIATTACH) += ubi_tools.o
+//kbuild:lib-$(CONFIG_UBIDETACH) += ubi_tools.o
+//kbuild:lib-$(CONFIG_UBIMKVOL)  += ubi_tools.o
+//kbuild:lib-$(CONFIG_UBIRMVOL)  += ubi_tools.o
+//kbuild:lib-$(CONFIG_UBIRSVOL)  += ubi_tools.o
+//kbuild:lib-$(CONFIG_UBIUPDATEVOL) += ubi_tools.o
+
+#include "libbb.h"
+/* Some versions of kernel have broken headers, need this hack */
+#ifndef __packed
+# define __packed __attribute__((packed))
+#endif
+#include <mtd/ubi-user.h>
+
+#define do_attach (ENABLE_UBIATTACH && applet_name[3] == 'a')
+#define do_detach (ENABLE_UBIDETACH && applet_name[3] == 'd')
+#define do_mkvol  (ENABLE_UBIMKVOL  && applet_name[3] == 'm')
+#define do_rmvol  (ENABLE_UBIRMVOL  && applet_name[4] == 'm')
+#define do_rsvol  (ENABLE_UBIRSVOL  && applet_name[4] == 's')
+#define do_update (ENABLE_UBIUPDATEVOL && applet_name[3] == 'u')
+
+static unsigned get_num_from_file(const char *path, unsigned max, const char *errmsg)
+{
+       char buf[sizeof(long long)*3];
+       unsigned long long num;
+
+       if (open_read_close(path, buf, sizeof(buf)) < 0)
+               bb_perror_msg_and_die(errmsg, path);
+       /* It can be \n terminated, xatoull won't work well */
+       if (sscanf(buf, "%llu", &num) != 1 || num > max)
+               bb_error_msg_and_die(errmsg, path);
+       return num;
+}
+
+/* To prevent malloc(1G) accidents */
+#define MAX_SANE_ERASEBLOCK (16*1024*1024)
+
+int ubi_tools_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
+{
+       static const struct suffix_mult size_suffixes[] = {
+               { "KiB", 1024 },
+               { "MiB", 1024*1024 },
+               { "GiB", 1024*1024*1024 },
+               { "", 0 }
+       };
+
+       unsigned opts;
+       char *ubi_ctrl;
+       int fd;
+       int mtd_num;
+       int dev_num = UBI_DEV_NUM_AUTO;
+       int vol_id = UBI_VOL_NUM_AUTO;
+       char *vol_name;
+       unsigned long long size_bytes = size_bytes; /* for compiler */
+       char *size_bytes_str;
+       int alignment = 1;
+       char *type;
+       union {
+               struct ubi_attach_req attach_req;
+               struct ubi_mkvol_req  mkvol_req;
+               struct ubi_rsvol_req  rsvol_req;
+       } req_structs;
+#define attach_req req_structs.attach_req
+#define mkvol_req  req_structs.mkvol_req
+#define rsvol_req  req_structs.rsvol_req
+       char path[sizeof("/sys/class/ubi/ubi%d_%d/usable_eb_size")
+                               + 2 * sizeof(int)*3 + /*just in case:*/ 16];
+#define path_sys_class_ubi_ubi (path + sizeof("/sys/class/ubi/ubi")-1)
+
+       strcpy(path, "/sys/class/ubi/ubi");
+       memset(&req_structs, 0, sizeof(req_structs));
+
+       if (do_mkvol) {
+               opt_complementary = "-1:d+:n+:a+";
+               opts = getopt32(argv, "md:n:N:s:a:t:",
+                               &dev_num, &vol_id,
+                               &vol_name, &size_bytes_str, &alignment, &type
+                       );
+       } else {
+               opt_complementary = "-1:m+:d+:n+:a+";
+               opts = getopt32(argv, "m:d:n:N:s:a:t:",
+                               &mtd_num, &dev_num, &vol_id,
+                               &vol_name, &size_bytes_str, &alignment, &type
+               );
+       }
+#define OPTION_m  (1 << 0)
+#define OPTION_d  (1 << 1)
+#define OPTION_n  (1 << 2)
+#define OPTION_N  (1 << 3)
+#define OPTION_s  (1 << 4)
+#define OPTION_a  (1 << 5)
+#define OPTION_t  (1 << 6)
+
+       if (opts & OPTION_s)
+               size_bytes = xatoull_sfx(size_bytes_str, size_suffixes);
+       argv += optind;
+       ubi_ctrl = *argv++;
+
+       fd = xopen(ubi_ctrl, O_RDWR);
+       //xfstat(fd, &st, ubi_ctrl);
+       //if (!S_ISCHR(st.st_mode))
+       //      bb_error_msg_and_die("%s: not a char device", ubi_ctrl);
+
+//usage:#define ubiattach_trivial_usage
+//usage:       "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV"
+//usage:#define ubiattach_full_usage "\n\n"
+//usage:       "Attach MTD device to UBI\n"
+//usage:     "\n       -m MTD_NUM      MTD device number to attach"
+//usage:     "\n       -d UBI_NUM      UBI device number to assign"
+       if (do_attach) {
+               if (!(opts & OPTION_m))
+                       bb_error_msg_and_die("%s device not specified", "MTD");
+
+               attach_req.mtd_num = mtd_num;
+               attach_req.ubi_num = dev_num;
+
+               xioctl(fd, UBI_IOCATT, &attach_req);
+       } else
+
+//usage:#define ubidetach_trivial_usage
+//usage:       "-d UBI_NUM UBI_CTRL_DEV"
+//usage:#define ubidetach_full_usage "\n\n"
+//usage:       "Detach MTD device from UBI\n"
+//usage:     "\n       -d UBI_NUM      UBI device number"
+       if (do_detach) {
+               if (!(opts & OPTION_d))
+                       bb_error_msg_and_die("%s device not specified", "UBI");
+
+               /* FIXME? kernel expects int32_t* here: */
+               xioctl(fd, UBI_IOCDET, &dev_num);
+       } else
+
+//usage:#define ubimkvol_trivial_usage
+//usage:       "UBI_DEVICE -N NAME [-s SIZE | -m]"
+//usage:#define ubimkvol_full_usage "\n\n"
+//usage:       "Create UBI volume\n"
+//usage:     "\n       -a ALIGNMENT    Volume alignment (default 1)"
+//usage:     "\n       -m              Set volume size to maximum available"
+//usage:     "\n       -n VOLID        Volume ID. If not specified,"
+//usage:     "\n                       assigned automatically"
+//usage:     "\n       -N NAME         Volume name"
+//usage:     "\n       -s SIZE         Size in bytes"
+//usage:     "\n       -t TYPE         Volume type (static|dynamic)"
+       if (do_mkvol) {
+               if (opts & OPTION_m) {
+                       unsigned leb_avail;
+                       unsigned leb_size;
+                       unsigned num;
+                       char *p;
+
+                       if (sscanf(ubi_ctrl, "/dev/ubi%u", &num) != 1)
+                               bb_error_msg_and_die("wrong format of UBI device name");
+
+                       p = path_sys_class_ubi_ubi + sprintf(path_sys_class_ubi_ubi, "%u/", num);
+
+                       strcpy(p, "avail_eraseblocks");
+                       leb_avail = get_num_from_file(path, UINT_MAX, "Can't get available eraseblocks from '%s'");
+
+                       strcpy(p, "eraseblock_size");
+                       leb_size = get_num_from_file(path, MAX_SANE_ERASEBLOCK, "Can't get eraseblock size from '%s'");
+
+                       size_bytes = leb_avail * (unsigned long long)leb_size;
+                       //if (size_bytes <= 0)
+                       //      bb_error_msg_and_die("%s invalid maximum size calculated", "UBI");
+               } else
+               if (!(opts & OPTION_s))
+                       bb_error_msg_and_die("size not specified");
+
+               if (!(opts & OPTION_N))
+                       bb_error_msg_and_die("name not specified");
+
+               mkvol_req.vol_id = vol_id;
+               mkvol_req.vol_type = UBI_DYNAMIC_VOLUME;
+               if ((opts & OPTION_t) && type[0] == 's')
+                       mkvol_req.vol_type = UBI_STATIC_VOLUME;
+               mkvol_req.alignment = alignment;
+               mkvol_req.bytes = size_bytes; /* signed int64_t */
+               strncpy(mkvol_req.name, vol_name, UBI_MAX_VOLUME_NAME);
+               mkvol_req.name_len = strlen(vol_name);
+               if (mkvol_req.name_len > UBI_MAX_VOLUME_NAME)
+                       bb_error_msg_and_die("volume name too long: '%s'", vol_name);
+
+               xioctl(fd, UBI_IOCMKVOL, &mkvol_req);
+       } else
+
+//usage:#define ubirmvol_trivial_usage
+//usage:       "UBI_DEVICE -n VOLID"
+//usage:#define ubirmvol_full_usage "\n\n"
+//usage:       "Remove UBI volume\n"
+//usage:     "\n       -n VOLID        Volume ID"
+       if (do_rmvol) {
+               if (!(opts & OPTION_n))
+                       bb_error_msg_and_die("volume id not specified");
+
+               /* FIXME? kernel expects int32_t* here: */
+               xioctl(fd, UBI_IOCRMVOL, &vol_id);
+       } else
+
+//usage:#define ubirsvol_trivial_usage
+//usage:       "UBI_DEVICE -n VOLID -s SIZE"
+//usage:#define ubirsvol_full_usage "\n\n"
+//usage:       "Resize UBI volume\n"
+//usage:     "\n       -n VOLID        Volume ID"
+//usage:     "\n       -s SIZE         Size in bytes"
+       if (do_rsvol) {
+               if (!(opts & OPTION_s))
+                       bb_error_msg_and_die("size not specified");
+               if (!(opts & OPTION_n))
+                       bb_error_msg_and_die("volume id not specified");
+
+               rsvol_req.bytes = size_bytes; /* signed int64_t */
+               rsvol_req.vol_id = vol_id;
+
+               xioctl(fd, UBI_IOCRSVOL, &rsvol_req);
+       } else
+
+//usage:#define ubiupdatevol_trivial_usage
+//usage:       "UBI_DEVICE [-t | [-s SIZE] IMG_FILE]"
+//usage:#define ubiupdatevol_full_usage "\n\n"
+//usage:       "Update UBI volume\n"
+//usage:     "\n       -t      Truncate to zero size"
+//usage:     "\n       -s SIZE Size in bytes to resize to"
+       if (do_update) {
+               int64_t bytes64;
+
+               if (opts & OPTION_t) {
+                       /* truncate the volume by starting an update for size 0 */
+                       bytes64 = 0;
+                       /* this ioctl expects int64_t* parameter */
+                       xioctl(fd, UBI_IOCVOLUP, &bytes64);
+               }
+               else {
+                       struct stat st;
+                       unsigned ubinum, volnum;
+                       unsigned leb_size;
+                       ssize_t len;
+                       char *input_data;
+
+                       /* Assume that device is in normal format. */
+                       /* Removes need for scanning sysfs tree as full libubi does. */
+                       if (sscanf(ubi_ctrl, "/dev/ubi%u_%u", &ubinum, &volnum) != 2)
+                               bb_error_msg_and_die("wrong format of UBI device name");
+
+                       sprintf(path_sys_class_ubi_ubi, "%u_%u/usable_eb_size", ubinum, volnum);
+                       leb_size = get_num_from_file(path, MAX_SANE_ERASEBLOCK, "Can't get usable eraseblock size from '%s'");
+
+                       if (!(opts & OPTION_s)) {
+                               if (!*argv)
+                                       bb_show_usage();
+                               xstat(*argv, &st);
+                               size_bytes = st.st_size;
+                               xmove_fd(xopen(*argv, O_RDONLY), STDIN_FILENO);
+                       }
+
+                       bytes64 = size_bytes;
+                       /* this ioctl expects signed int64_t* parameter */
+                       xioctl(fd, UBI_IOCVOLUP, &bytes64);
+
+                       input_data = xmalloc(leb_size);
+                       while ((len = full_read(STDIN_FILENO, input_data, leb_size)) > 0) {
+                               xwrite(fd, input_data, len);
+                       }
+                       if (len < 0)
+                               bb_perror_msg_and_die("UBI volume update failed");
+               }
+       }
+
+       if (ENABLE_FEATURE_CLEAN_UP)
+               close(fd);
+
+       return EXIT_SUCCESS;
+}
index 6e86156..b50e795 100644 (file)
  * mods from distributed source (eject-2.0.13) are by
  * Matthew Stoltenberg <d3matt@gmail.com>
  */
+
+//usage:#define volname_trivial_usage
+//usage:       "[DEVICE]"
+//usage:#define volname_full_usage "\n\n"
+//usage:       "Show CD volume name of the DEVICE (default /dev/cdrom)"
+
 #include "libbb.h"
 
 int volname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
index 2dbab60..bb709ee 100644 (file)
@@ -3,19 +3,46 @@
  * wall - write a message to all logged-in users
  * Copyright (c) 2009 Bernhard Reutner-Fischer
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//config:config WALL
+//config:      bool "wall"
+//config:      default y
+//config:      depends on FEATURE_UTMP
+//config:      help
+//config:        Write a message to all users that are logged in.
+
+/* Needs to be run by root or be suid root - needs to write to /dev/TTY: */
+//applet:IF_WALL(APPLET(wall, BB_DIR_USR_BIN, BB_SUID_REQUIRE))
+
+//kbuild:lib-$(CONFIG_WALL) += wall.o
+
+//usage:#define wall_trivial_usage
+//usage:       "[FILE]"
+//usage:#define wall_full_usage "\n\n"
+//usage:       "Write content of FILE or stdin to all logged-in users"
+//usage:
+//usage:#define wall_sample_usage
+//usage:       "echo foo | wall\n"
+//usage:       "wall ./mymessage"
+
 #include "libbb.h"
-#include <utmp.h>
 
 int wall_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int wall_main(int argc UNUSED_PARAM, char **argv)
 {
        struct utmp *ut;
        char *msg;
-       int fd = argv[1] ? xopen(argv[1], O_RDONLY) : STDIN_FILENO;
+       int fd;
 
+       fd = STDIN_FILENO;
+       if (argv[1]) {
+               /* The applet is setuid.
+                * Access to the file must be under user's uid/gid.
+                */
+               fd = xopen_as_uid_gid(argv[1], O_RDONLY, getuid(), getgid());
+       }
        msg = xmalloc_read(fd, NULL);
        if (ENABLE_FEATURE_CLEAN_UP && argv[1])
                close(fd);
index 8e961f0..d3a76ed 100644 (file)
@@ -6,9 +6,19 @@
  * Copyright (C) 2006  Bernhard Reutner-Fischer <busybox@busybox.net>
  * Copyright (C) 2008  Darius Augulis <augulis.darius@gmail.com>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define watchdog_trivial_usage
+//usage:       "[-t N[ms]] [-T N[ms]] [-F] DEV"
+//usage:#define watchdog_full_usage "\n\n"
+//usage:       "Periodically write to watchdog device DEV\n"
+//usage:     "\n       -T N    Reboot after N seconds if not reset (default 60)"
+//usage:     "\n       -t N    Reset every N seconds (default 30)"
+//usage:     "\n       -F      Run in foreground"
+//usage:     "\n"
+//usage:     "\nUse 500ms to specify period in milliseconds"
+
 #include "libbb.h"
 #include "linux/types.h" /* for __u32 */
 #include "linux/watchdog.h"
@@ -21,10 +31,11 @@ static void watchdog_shutdown(int sig UNUSED_PARAM)
 {
        static const char V = 'V';
 
-       write(3, &V, 1);        /* Magic, see watchdog-api.txt in kernel */
+       remove_pidfile(CONFIG_PID_FILE_PATH "/watchdog.pid");
+       write(3, &V, 1);  /* Magic, see watchdog-api.txt in kernel */
        if (ENABLE_FEATURE_CLEAN_UP)
                close(3);
-       exit(EXIT_SUCCESS);
+       _exit(EXIT_SUCCESS);
 }
 
 int watchdog_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -85,6 +96,8 @@ int watchdog_main(int argc, char **argv)
                stimer_duration, htimer_duration * 1000);
 #endif
 
+       write_pidfile(CONFIG_PID_FILE_PATH "/watchdog.pid");
+
        while (1) {
                /*
                 * Make sure we clear the counter before sleeping,
index a7dcb3a..449ac65 100644 (file)
@@ -10,6 +10,7 @@ INSERT
 config MODPROBE_SMALL
        bool "Simplified modutils"
        default y
+       select PLATFORM_LINUX
        help
          Simplified modutils.
 
@@ -44,6 +45,7 @@ config FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
        bool "Accept module options on modprobe command line"
        default y
        depends on MODPROBE_SMALL
+       select PLATFORM_LINUX
        help
          Allow insmod and modprobe take module options from command line.
 
@@ -58,6 +60,7 @@ config INSMOD
        bool "insmod"
        default n
        depends on !MODPROBE_SMALL
+       select PLATFORM_LINUX
        help
          insmod is used to load specified modules in the running kernel.
 
@@ -65,6 +68,7 @@ config RMMOD
        bool "rmmod"
        default n
        depends on !MODPROBE_SMALL
+       select PLATFORM_LINUX
        help
          rmmod is used to unload specified modules from the kernel.
 
@@ -72,6 +76,7 @@ config LSMOD
        bool "lsmod"
        default n
        depends on !MODPROBE_SMALL
+       select PLATFORM_LINUX
        help
          lsmod is used to display a list of loaded modules.
 
@@ -79,6 +84,7 @@ config FEATURE_LSMOD_PRETTY_2_6_OUTPUT
        bool "Pretty output"
        default n
        depends on LSMOD
+       select PLATFORM_LINUX
        help
          This option makes output format of lsmod adjusted to
          the format of module-init-tools for Linux kernel 2.6.
@@ -88,6 +94,7 @@ config MODPROBE
        bool "modprobe"
        default n
        depends on !MODPROBE_SMALL
+       select PLATFORM_LINUX
        help
          Handle the loading of modules, and their dependencies on a high
          level.
@@ -96,6 +103,7 @@ config FEATURE_MODPROBE_BLACKLIST
        bool "Blacklist support"
        default n
        depends on MODPROBE
+       select PLATFORM_LINUX
        help
          Say 'y' here to enable support for the 'blacklist' command in
          modprobe.conf. This prevents the alias resolver to resolve
@@ -107,6 +115,7 @@ config DEPMOD
        bool "depmod"
        default n
        depends on !MODPROBE_SMALL
+       select PLATFORM_LINUX
        help
          depmod generates modules.dep (and potentially modules.alias
          and modules.symbols) that contain dependency information
@@ -118,6 +127,7 @@ config FEATURE_2_4_MODULES
        bool "Support version 2.2/2.4 Linux kernels"
        default n
        depends on INSMOD || RMMOD || LSMOD
+       select PLATFORM_LINUX
        help
          Support module loading for 2.2.x and 2.4.x Linux kernels.
          This increases size considerably. Say N unless you plan
@@ -127,6 +137,7 @@ config FEATURE_INSMOD_TRY_MMAP
        bool "Try to load module from a mmap'ed area"
        default n
        depends on INSMOD || MODPROBE_SMALL
+       select PLATFORM_LINUX
        help
          This option causes module loading code to try to mmap
          module first. If it does not work (for example,
@@ -143,6 +154,7 @@ config FEATURE_INSMOD_VERSION_CHECKING
        bool "Enable module version checking"
        default n
        depends on FEATURE_2_4_MODULES && (INSMOD || MODPROBE)
+       select PLATFORM_LINUX
        help
          Support checking of versions for modules. This is used to
          ensure that the kernel and module are made for each other.
@@ -151,6 +163,7 @@ config FEATURE_INSMOD_KSYMOOPS_SYMBOLS
        bool "Add module symbols to kernel symbol table"
        default n
        depends on FEATURE_2_4_MODULES && (INSMOD || MODPROBE)
+       select PLATFORM_LINUX
        help
          By adding module symbols to the kernel symbol table, Oops messages
          occuring within kernel modules can be properly debugged. By enabling
@@ -162,6 +175,7 @@ config FEATURE_INSMOD_LOADINKMEM
        bool "In kernel memory optimization (uClinux only)"
        default n
        depends on FEATURE_2_4_MODULES && (INSMOD || MODPROBE)
+       select PLATFORM_LINUX
        help
          This is a special uClinux only memory optimization that lets insmod
          load the specified kernel module directly into kernel space, reducing
@@ -172,6 +186,7 @@ config FEATURE_INSMOD_LOAD_MAP
        bool "Enable insmod load map (-m) option"
        default n
        depends on FEATURE_2_4_MODULES && INSMOD
+       select PLATFORM_LINUX
        help
          Enabling this, one would be able to get a load map
          output on stdout. This makes kernel module debugging
@@ -183,6 +198,7 @@ config FEATURE_INSMOD_LOAD_MAP_FULL
        bool "Symbols in load map"
        default y
        depends on FEATURE_INSMOD_LOAD_MAP && !MODPROBE_SMALL
+       select PLATFORM_LINUX
        help
          Without this option, -m will only output section
          load map. With this option, -m will also output
@@ -192,6 +208,7 @@ config FEATURE_CHECK_TAINTED_MODULE
        bool "Support tainted module checking with new kernels"
        default y
        depends on (LSMOD || FEATURE_2_4_MODULES) && !MODPROBE_SMALL
+       select PLATFORM_LINUX
        help
          Support checking for tainted modules. These are usually binary
          only modules that will make the linux-kernel list ignore your
@@ -202,6 +219,7 @@ config FEATURE_MODUTILS_ALIAS
        bool "Support for module.aliases file"
        default y
        depends on DEPMOD || MODPROBE
+       select PLATFORM_LINUX
        help
          Generate and parse modules.alias containing aliases for bus
          identifiers:
@@ -218,6 +236,7 @@ config FEATURE_MODUTILS_SYMBOLS
        bool "Support for module.symbols file"
        default y
        depends on DEPMOD || MODPROBE
+       select PLATFORM_LINUX
        help
          Generate and parse modules.symbols containing aliases for
          symbol_request() kernel calls, such as:
index a512f60..1a7ac87 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
index 694f9ea..aa228ec 100644 (file)
@@ -5,9 +5,11 @@
  * Copyrihgt (c) 2008 Timo Teras <timo.teras@iki.fi>
  * Copyright (c) 2008 Vladimir Dronnikov
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//applet:IF_DEPMOD(APPLET(depmod, BB_DIR_SBIN, BB_SUID_DROP))
+
 #include "libbb.h"
 #include "modutils.h"
 #include <sys/utsname.h> /* uname() */
@@ -29,7 +31,7 @@ typedef struct module_info {
 } module_info;
 
 static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARAM,
-                                 void *data, int depth UNUSED_PARAM)
+                               void *data, int depth UNUSED_PARAM)
 {
        char modname[MODULE_NAME_LEN];
        module_info **first = (module_info **) data;
@@ -93,7 +95,7 @@ static module_info *find_module(module_info *modules, const char *modname)
 }
 
 static void order_dep_list(module_info *modules, module_info *start,
-                          llist_t *add)
+                       llist_t *add)
 {
        module_info *m;
        llist_t *n;
@@ -124,7 +126,16 @@ static void xfreopen_write(const char *file, FILE *f)
                bb_perror_msg_and_die("can't open '%s'", file);
 }
 
-/* Usage:
+//usage:#if !ENABLE_MODPROBE_SMALL
+//usage:#define depmod_trivial_usage "[-n] [-b BASE] [VERSION] [MODFILES]..."
+//usage:#define depmod_full_usage "\n\n"
+//usage:       "Generate modules.dep, alias, and symbols files"
+//usage:     "\n"
+//usage:     "\n       -b BASE Use BASE/lib/modules/VERSION"
+//usage:     "\n       -n      Dry run: print files to stdout"
+//usage:#endif
+
+/* Upstream usage:
  * [-aAenv] [-C FILE or DIR] [-b BASE] [-F System.map] [VERSION] [MODFILES]...
  *     -a --all
  *             Probe all modules. Default if no MODFILES.
@@ -135,7 +146,7 @@ static void xfreopen_write(const char *file, FILE *f)
  *     -C --config FILE or DIR
  *             Path to /etc/depmod.conf or /etc/depmod.d/
  *     -e --errsyms
- *             When combined with the -F option, this reports any symbols which
+ *             When combined with the -F option, this reports any symbols
  *             which are not supplied by other modules or kernel.
  *     -F --filesyms System.map
  *     -n --dry-run
@@ -144,9 +155,16 @@ static void xfreopen_write(const char *file, FILE *f)
  *             Print to stdout all the symbols each module depends on
  *             and the module's file name which provides that symbol.
  *     -r      No-op
+ *     -u      No-op
+ *     -q      No-op
+ *
+ * So far we only support: [-n] [-b BASE] [VERSION] [MODFILES]...
+ * Accepted but ignored:
+ * -aAe
+ * -F System.map
+ * -C FILE/DIR
  *
- * So far we only support: [-rn] [-b BASE] [VERSION] [MODFILES]...
- * -aAeF are accepted but ignored. -vC are not accepted.
+ * Not accepted: -v
  */
 enum {
        //OPT_a = (1 << 0), /* All modules, ignore mods in argv */
@@ -155,7 +173,10 @@ enum {
        //OPT_e = (1 << 3), /* with -F, print unresolved symbols */
        //OPT_F = (1 << 4), /* System.map that contains the symbols */
        OPT_n = (1 << 5), /* dry-run, print to stdout only */
-       OPT_r = (1 << 6)  /* Compat dummy. Linux Makefile uses it */
+       OPT_r = (1 << 6), /* Compat dummy. Linux Makefile uses it */
+       OPT_u = (1 << 7), /* -u,--unresolved-error: ignored */
+       OPT_q = (1 << 8), /* -q,--quiet: ignored */
+       OPT_C = (1 << 9), /* -C,--config etc_modules_conf: ignored */
 };
 
 int depmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -167,7 +188,7 @@ int depmod_main(int argc UNUSED_PARAM, char **argv)
        struct utsname uts;
        int tmp;
 
-       getopt32(argv, "aAb:eF:nr", &moddir_base, NULL);
+       getopt32(argv, "aAb:eF:nruqC:", &moddir_base, NULL, NULL);
        argv += optind;
 
        /* goto modules location */
@@ -195,7 +216,7 @@ int depmod_main(int argc UNUSED_PARAM, char **argv)
                } while (*++argv);
        } else {
                recursive_action(".", ACTION_RECURSE,
-                                parse_module, NULL, &modules, 0);
+                               parse_module, NULL, &modules, 0);
        }
 
        /* Generate dependency and alias files */
index b88446c..887d9f2 100644 (file)
@@ -4,23 +4,25 @@
  *
  * Copyright (C) 2008 Timo Teras <timo.teras@iki.fi>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//applet:IF_INSMOD(APPLET(insmod, BB_DIR_SBIN, BB_SUID_DROP))
+
 #include "libbb.h"
 #include "modutils.h"
 
 /* 2.6 style insmod has no options and required filename
  * (not module name - .ko can't be omitted) */
 
+//usage:#if !ENABLE_MODPROBE_SMALL
 //usage:#define insmod_trivial_usage
 //usage:       IF_FEATURE_2_4_MODULES("[OPTIONS] MODULE ")
 //usage:       IF_NOT_FEATURE_2_4_MODULES("FILE ")
-//usage:       "[symbol=value]..."
+//usage:       "[SYMBOL=VALUE]..."
 //usage:#define insmod_full_usage "\n\n"
 //usage:       "Load the specified kernel modules into the kernel"
 //usage:       IF_FEATURE_2_4_MODULES( "\n"
-//usage:     "\nOptions:"
 //usage:     "\n       -f      Force module to load into the wrong kernel version"
 //usage:     "\n       -k      Make module autoclean-able"
 //usage:     "\n       -v      Verbose"
@@ -31,6 +33,7 @@
 //usage:       )
 //usage:     "\n       -x      Don't export externs"
 //usage:       )
+//usage:#endif
 
 int insmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int insmod_main(int argc UNUSED_PARAM, char **argv)
@@ -55,7 +58,7 @@ int insmod_main(int argc UNUSED_PARAM, char **argv)
        if (!filename)
                bb_show_usage();
 
-       rc = bb_init_module(filename, parse_cmdline_module_options(argv));
+       rc = bb_init_module(filename, parse_cmdline_module_options(argv, /*quote_spaces:*/ 0));
        if (rc)
                bb_error_msg("can't insert '%s': %s", filename, moderror(rc));
 
index 97954c7..3b3c166 100644 (file)
@@ -5,8 +5,18 @@
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//applet:IF_LSMOD(APPLET(lsmod, BB_DIR_SBIN, BB_SUID_DROP))
+
+//usage:#if !ENABLE_MODPROBE_SMALL
+//usage:#define lsmod_trivial_usage
+//usage:       ""
+//usage:#define lsmod_full_usage "\n\n"
+//usage:       "List the currently loaded kernel modules"
+//usage:#endif
+
 #include "libbb.h"
 #include "unicode.h"
 
@@ -77,7 +87,8 @@ int lsmod_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
                        // N.B. token[3] is either '-' (module is not used by others)
                        // or comma-separated list ended by comma
                        // so trimming the trailing char is just what we need!
-                       token[3][strlen(token[3])-1] = '\0';
+                       if (token[3][0])
+                               token[3][strlen(token[3]) - 1] = '\0';
 # if ENABLE_UNICODE_SUPPORT
                        {
                                uni_stat_t uni_stat;
index 454a1b3..7c978d1 100644 (file)
@@ -3,16 +3,17 @@
  * modinfo - retrieve module info
  * Copyright (c) 2008 Pascal Bellard
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
-//applet:IF_MODINFO(APPLET(modinfo, _BB_DIR_SBIN, _BB_SUID_DROP))
+//applet:IF_MODINFO(APPLET(modinfo, BB_DIR_SBIN, BB_SUID_DROP))
 
 //kbuild:lib-$(CONFIG_MODINFO) += modinfo.o modutils.o
 
 //config:config MODINFO
 //config:      bool "modinfo"
 //config:      default y
+//config:      select PLATFORM_LINUX
 //config:      help
 //config:        Show information about a Linux Kernel module
 
@@ -23,9 +24,9 @@
 
 
 enum {
-       OPT_TAGS = (1 << 6) - 1,
-       OPT_F = (1 << 6), /* field name */
-       OPT_0 = (1 << 7),  /* \0 as separator */
+       OPT_TAGS = (1 << 12) - 1, /* shortcut count */
+       OPT_F = (1 << 12), /* field name */
+       OPT_0 = (1 << 13), /* \0 as separator */
 };
 
 struct modinfo_env {
@@ -44,15 +45,21 @@ static int display(const char *data, const char *pattern, int flag)
 }
 
 static void modinfo(const char *path, const char *version,
-                       struct modinfo_env *env)
+                       const struct modinfo_env *env)
 {
        static const char *const shortcuts[] = {
                "filename",
-               "description",
-               "author",
                "license",
+               "author",
+               "description",
+               "version",
+               "alias",
+               "srcversion",
+               "depends",
+               "uts_release",
                "vermagic",
                "parm",
+               "firmware",
        };
        size_t len;
        int j, length;
@@ -80,11 +87,13 @@ static void modinfo(const char *path, const char *version,
        if (field)
                tags |= OPT_F;
        for (j = 1; (1<<j) & (OPT_TAGS + OPT_F); j++) {
-               const char *pattern = field;
-               if ((1<<j) & OPT_TAGS)
-                       pattern = shortcuts[j];
+               const char *pattern;
+
                if (!((1<<j) & tags))
                        continue;
+               pattern = field;
+               if ((1<<j) & OPT_TAGS)
+                       pattern = shortcuts[j];
                length = strlen(pattern);
                ptr = the_module;
                while (1) {
@@ -92,8 +101,11 @@ static void modinfo(const char *path, const char *version,
                        if (ptr == NULL) /* no occurance left, done */
                                break;
                        if (strncmp(ptr, pattern, length) == 0 && ptr[length] == '=') {
-                               ptr += length + 1;
-                               ptr += display(ptr, pattern, (1<<j) != tags);
+                               /* field prefixes are 0x80 or 0x00 */
+                               if ((ptr[-1] & 0x7F) == '\0') {
+                                       ptr += length + 1;
+                                       ptr += display(ptr, pattern, (1<<j) != tags);
+                               }
                        }
                        ++ptr;
                }
@@ -104,8 +116,7 @@ static void modinfo(const char *path, const char *version,
 //usage:#define modinfo_trivial_usage
 //usage:       "[-adlp0] [-F keyword] MODULE"
 //usage:#define modinfo_full_usage "\n\n"
-//usage:       "Options:"
-//usage:     "\n       -a              Shortcut for '-F author'"
+//usage:       "       -a              Shortcut for '-F author'"
 //usage:     "\n       -d              Shortcut for '-F description'"
 //usage:     "\n       -l              Shortcut for '-F license'"
 //usage:     "\n       -p              Shortcut for '-F parm'"
@@ -127,7 +138,7 @@ int modinfo_main(int argc UNUSED_PARAM, char **argv)
 
        env.field = NULL;
        opt_complementary = "-1"; /* minimum one param */
-       opts = getopt32(argv, "fdalvpF:0", &env.field);
+       opts = getopt32(argv, "nladvAsDumpF:0", &env.field);
        env.tags = opts & OPT_TAGS ? opts & OPT_TAGS : OPT_TAGS;
        argv += optind;
 
index 0e1874e..5b78363 100644 (file)
@@ -5,9 +5,15 @@
  * Copyright (c) 2008 Vladimir Dronnikov
  * Copyright (c) 2008 Bernhard Reutner-Fischer (initial depmod code)
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//applet:IF_MODPROBE_SMALL(APPLET(modprobe, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(depmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe))
+//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(insmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe))
+//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(lsmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe))
+//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(rmmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe))
+
 #include "libbb.h"
 /* After libbb.h, since it needs sys/types.h on some systems */
 #include <sys/utsname.h> /* uname() */
@@ -18,10 +24,13 @@ extern int delete_module(const char *module, unsigned flags);
 extern int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret);
 
 
-#define dbg1_error_msg(...) ((void)0)
-#define dbg2_error_msg(...) ((void)0)
-//#define dbg1_error_msg(...) bb_error_msg(__VA_ARGS__)
-//#define dbg2_error_msg(...) bb_error_msg(__VA_ARGS__)
+#if 1
+# define dbg1_error_msg(...) ((void)0)
+# define dbg2_error_msg(...) ((void)0)
+#else
+# define dbg1_error_msg(...) bb_error_msg(__VA_ARGS__)
+# define dbg2_error_msg(...) bb_error_msg(__VA_ARGS__)
+#endif
 
 #define DEPFILE_BB CONFIG_DEFAULT_DEPMOD_FILE".bb"
 
@@ -196,6 +205,7 @@ static void parse_module(module_info *info, const char *pathname)
        /* Read (possibly compressed) module */
        len = 64 * 1024 * 1024; /* 64 Mb at most */
        module_image = xmalloc_open_zipped_read_close(pathname, &len);
+       /* module_image == NULL is ok here, find_keyword handles it */
 //TODO: optimize redundant module body reads
 
        /* "alias1 symbol:sym1 alias2 symbol:sym2" */
@@ -579,24 +589,30 @@ static void process_module(char *name, const char *cmdline_options)
 
        /* rmmod? unload it by name */
        if (is_rmmod) {
-               if (delete_module(name, O_NONBLOCK | O_EXCL) != 0
-                && !(option_mask32 & OPT_q)
-               ) {
-                       bb_perror_msg("remove '%s'", name);
+               if (delete_module(name, O_NONBLOCK | O_EXCL) != 0) {
+                       if (!(option_mask32 & OPT_q))
+                               bb_perror_msg("remove '%s'", name);
                        goto ret;
                }
-               /* N.B. we do not stop here -
+
+               if (applet_name[0] == 'r') {
+                       /* rmmod: do not remove dependencies, exit */
+                       goto ret;
+               }
+
+               /* modprobe -r: we do not stop here -
                 * continue to unload modules on which the module depends:
                 * "-r --remove: option causes modprobe to remove a module.
                 * If the modules it depends on are also unused, modprobe
-                * will try to remove them, too." */
+                * will try to remove them, too."
+                */
        }
 
        if (!info) {
                /* both dirscan and find_alias found nothing */
-               if (applet_name[0] != 'd') /* it wasn't depmod */
+               if (!is_rmmod && applet_name[0] != 'd') /* it wasn't rmmod or depmod */
                        bb_error_msg("module '%s' not found", name);
-//TODO: _and_die()?
+//TODO: _and_die()? or should we continue (un)loading modules listed on cmdline?
                goto ret;
        }
 
@@ -686,17 +702,59 @@ The following options are useful for people managing distributions:
 */
 
 //usage:#if ENABLE_MODPROBE_SMALL
+
+//// Note: currently, help system shows modprobe --help text for all aliased cmds
+//// (see APPLET_ODDNAME macro definition).
+//// All other help texts defined below are not used. FIXME?
+
+//usage:#define depmod_trivial_usage NOUSAGE_STR
+//usage:#define depmod_full_usage ""
+
+//usage:#define lsmod_trivial_usage
+//usage:       ""
+//usage:#define lsmod_full_usage "\n\n"
+//usage:       "List the currently loaded kernel modules"
+
+//usage:#define insmod_trivial_usage
+//usage:       IF_FEATURE_2_4_MODULES("[OPTIONS] MODULE ")
+//usage:       IF_NOT_FEATURE_2_4_MODULES("FILE ")
+//usage:       "[SYMBOL=VALUE]..."
+//usage:#define insmod_full_usage "\n\n"
+//usage:       "Load the specified kernel modules into the kernel"
+//usage:       IF_FEATURE_2_4_MODULES( "\n"
+//usage:     "\n       -f      Force module to load into the wrong kernel version"
+//usage:     "\n       -k      Make module autoclean-able"
+//usage:     "\n       -v      Verbose"
+//usage:     "\n       -q      Quiet"
+//usage:     "\n       -L      Lock: prevent simultaneous loads"
+//usage:       IF_FEATURE_INSMOD_LOAD_MAP(
+//usage:     "\n       -m      Output load map to stdout"
+//usage:       )
+//usage:     "\n       -x      Don't export externs"
+//usage:       )
+
+//usage:#define rmmod_trivial_usage
+//usage:       "[-wfa] [MODULE]..."
+//usage:#define rmmod_full_usage "\n\n"
+//usage:       "Unload kernel modules\n"
+//usage:     "\n       -w      Wait until the module is no longer used"
+//usage:     "\n       -f      Force unload"
+//usage:     "\n       -a      Remove all unused modules (recursively)"
+//usage:
+//usage:#define rmmod_example_usage
+//usage:       "$ rmmod tulip\n"
+
 //usage:#define modprobe_trivial_usage
 //usage:       "[-qfwrsv] MODULE [symbol=value]..."
 //usage:#define modprobe_full_usage "\n\n"
-//usage:       "Options:"
-//usage:     "\n       -r      Remove MODULE (stacks) or do autoclean"
+//usage:       "       -r      Remove MODULE (stacks) or do autoclean"
 //usage:     "\n       -q      Quiet"
 //usage:     "\n       -v      Verbose"
 //usage:     "\n       -f      Force"
 //usage:     "\n       -w      Wait for unload"
 //usage:     "\n       -s      Report via syslog instead of stderr"
-//usage:#endif /* ENABLE_MODPROBE_SMALL */
+
+//usage:#endif
 
 int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int modprobe_main(int argc UNUSED_PARAM, char **argv)
@@ -758,7 +816,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
        opt_complementary = "-1";
        /* only -q (quiet) and -r (rmmod),
         * the rest are accepted and ignored (compat) */
-       getopt32(argv, "qrfsvw");
+       getopt32(argv, "qrfsvwb");
        argv += optind;
 
        /* are we rmmod? -> simulate modprobe -r */
@@ -795,13 +853,17 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
                void *map;
 
                len = MAXINT(ssize_t);
-               map = xmalloc_xopen_read_close(*argv, &len);
+               map = xmalloc_open_zipped_read_close(*argv, &len);
+               if (!map)
+                       bb_perror_msg_and_die("can't read '%s'", *argv);
                if (init_module(map, len,
                        IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(options ? options : "")
                        IF_NOT_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE("")
-                               ) != 0)
+                       ) != 0
+               ) {
                        bb_error_msg_and_die("can't insert '%s': %s",
                                        *argv, moderror(errno));
+               }
                return 0;
        }
 
@@ -811,8 +873,8 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
        /* Load/remove modules.
         * Only rmmod loops here, modprobe has only argv[0] */
        do {
-               process_module(*argv++, options);
-       } while (*argv);
+               process_module(*argv, options);
+       } while (*++argv);
 
        if (ENABLE_FEATURE_CLEAN_UP) {
                IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(free(options);)
index 0a94242..7f7446d 100644 (file)
@@ -5,13 +5,11 @@
  * Copyright (c) 2008 Timo Teras <timo.teras@iki.fi>
  * Copyright (c) 2008 Vladimir Dronnikov
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
-/* Note that unlike older versions of modules.dep/depmod (busybox and m-i-t),
- * we expect the full dependency list to be specified in modules.dep.
- * Older versions would only export the direct dependency list.
- */
+//applet:IF_MODPROBE(APPLET(modprobe, BB_DIR_SBIN, BB_SUID_DROP))
+
 #include "libbb.h"
 #include "modutils.h"
 #include <sys/utsname.h>
 //#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__)
 #define DBG(...) ((void)0)
 
+/* Note that unlike older versions of modules.dep/depmod (busybox and m-i-t),
+ * we expect the full dependency list to be specified in modules.dep.
+ * Older versions would only export the direct dependency list.
+ */
+
+
 //usage:#if !ENABLE_MODPROBE_SMALL
 //usage:#define modprobe_notes_usage
 //usage:       "modprobe can (un)load a stack of modules, passing each module options (when\n"
 //usage:       "   from the command line\n"
 //usage:
 //usage:#define modprobe_trivial_usage
-//usage:       "[-alrqvs"
-//usage:       IF_FEATURE_MODPROBE_BLACKLIST("b")
-//usage:       "] MODULE [symbol=value]..."
+//usage:       "[-alrqvsD" IF_FEATURE_MODPROBE_BLACKLIST("b") "]"
+//usage:       " MODULE [symbol=value]..."
 //usage:#define modprobe_full_usage "\n\n"
-//usage:       "Options:"
-//usage:     "\n       -a      Load multiple MODULEs"
+//usage:       "       -a      Load multiple MODULEs"
 //usage:     "\n       -l      List (MODULE is a pattern)"
 //usage:     "\n       -r      Remove MODULE (stacks) or do autoclean"
 //usage:     "\n       -q      Quiet"
 //usage:     "\n       -v      Verbose"
 //usage:     "\n       -s      Log to syslog"
+//usage:     "\n       -D      Show dependencies"
 //usage:       IF_FEATURE_MODPROBE_BLACKLIST(
 //usage:     "\n       -b      Apply blacklist to module names too"
 //usage:       )
 //usage:#endif /* !ENABLE_MODPROBE_SMALL */
 
-/* Note that usage text doesn't document various 2.4 options
- * we pull in through INSMOD_OPTS define */
-
-#define MODPROBE_COMPLEMENTARY "q-v:v-q:l--ar:a--lr:r--al"
-#define MODPROBE_OPTS  "alr" IF_FEATURE_MODPROBE_BLACKLIST("b")
+/* Note: usage text doesn't document various 2.4 options
+ * we pull in through INSMOD_OPTS define
+ * Note2: -b is always accepted, but if !FEATURE_MODPROBE_BLACKLIST,
+ * it is a no-op.
+ */
+#define MODPROBE_OPTS  "alrDb"
+/* -a and -D _are_ in fact compatible */
+#define MODPROBE_COMPLEMENTARY ("q-v:v-q:l--arD:r--alD:a--lr:D--rl")
+//#define MODPROBE_OPTS  "acd:lnrt:C:b"
 //#define MODPROBE_COMPLEMENTARY "q-v:v-q:l--acr:a--lr:r--al"
-//#define MODPROBE_OPTS  "acd:lnrt:C:" IF_FEATURE_MODPROBE_BLACKLIST("b")
 enum {
-       MODPROBE_OPT_INSERT_ALL = (INSMOD_OPT_UNUSED << 0), /* a */
-       //MODPROBE_OPT_DUMP_ONLY= (INSMOD_OPT_UNUSED << x), /* c */
-       //MODPROBE_OPT_DIRNAME  = (INSMOD_OPT_UNUSED << x), /* d */
-       MODPROBE_OPT_LIST_ONLY  = (INSMOD_OPT_UNUSED << 1), /* l */
-       //MODPROBE_OPT_SHOW_ONLY= (INSMOD_OPT_UNUSED << x), /* n */
-       MODPROBE_OPT_REMOVE     = (INSMOD_OPT_UNUSED << 2), /* r */
-       //MODPROBE_OPT_RESTRICT = (INSMOD_OPT_UNUSED << x), /* t */
-       //MODPROBE_OPT_VERONLY  = (INSMOD_OPT_UNUSED << x), /* V */
-       //MODPROBE_OPT_CONFIGFILE=(INSMOD_OPT_UNUSED << x), /* C */
-       MODPROBE_OPT_BLACKLIST  = (INSMOD_OPT_UNUSED << 3) * ENABLE_FEATURE_MODPROBE_BLACKLIST,
+       OPT_INSERT_ALL   = (INSMOD_OPT_UNUSED << 0), /* a */
+       //OPT_DUMP_ONLY  = (INSMOD_OPT_UNUSED << x), /* c */
+       //OPT_DIRNAME    = (INSMOD_OPT_UNUSED << x), /* d */
+       OPT_LIST_ONLY    = (INSMOD_OPT_UNUSED << 1), /* l */
+       //OPT_SHOW_ONLY  = (INSMOD_OPT_UNUSED << x), /* n */
+       OPT_REMOVE       = (INSMOD_OPT_UNUSED << 2), /* r */
+       //OPT_RESTRICT   = (INSMOD_OPT_UNUSED << x), /* t */
+       //OPT_VERONLY    = (INSMOD_OPT_UNUSED << x), /* V */
+       //OPT_CONFIGFILE = (INSMOD_OPT_UNUSED << x), /* C */
+       OPT_SHOW_DEPS    = (INSMOD_OPT_UNUSED << 3), /* D */
+       OPT_BLACKLIST    = (INSMOD_OPT_UNUSED << 4) * ENABLE_FEATURE_MODPROBE_BLACKLIST,
 };
+#if ENABLE_LONG_OPTS
+static const char modprobe_longopts[] ALIGN1 =
+       /* nobody asked for long opts (yet) */
+       // "all\0"          No_argument "a"
+       // "list\0"         No_argument "l"
+       // "remove\0"       No_argument "r"
+       // "quiet\0"        No_argument "q"
+       // "verbose\0"      No_argument "v"
+       // "syslog\0"       No_argument "s"
+       /* module-init-tools 3.11.1 has only long opt --show-depends
+        * but no short -D, we provide long opt for scripts which
+        * were written for 3.11.1: */
+       "show-depends\0"     No_argument "D"
+       // "use-blacklist\0" No_argument "b"
+       ;
+#endif
 
 #define MODULE_FLAG_LOADED              0x0001
 #define MODULE_FLAG_NEED_DEPS           0x0002
@@ -135,16 +158,21 @@ struct module_entry { /* I'll call it ME. */
        llist_t *deps; /* strings. modules we depend on */
 };
 
+#define DB_HASH_SIZE 256
+
 struct globals {
-       llist_t *db; /* MEs of all modules ever seen (caching for speed) */
        llist_t *probes; /* MEs of module(s) requested on cmdline */
        char *cmdline_mopts; /* module options from cmdline */
        int num_unresolved_deps;
        /* bool. "Did we have 'symbol:FOO' requested on cmdline?" */
        smallint need_symbols;
+       struct utsname uts;
+       llist_t *db[DB_HASH_SIZE]; /* MEs of all modules ever seen (caching for speed) */
 } FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-#define INIT_G() do { } while (0)
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+       SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
 
 
 static int read_config(const char *path);
@@ -164,14 +192,26 @@ static char *gather_options_str(char *opts, const char *append)
        return opts;
 }
 
+/* These three functions called many times, optimizing for speed.
+ * Users reported minute-long delays when they runn iptables repeatedly
+ * (iptables use modprobe to install needed kernel modules).
+ */
 static struct module_entry *helper_get_module(const char *module, int create)
 {
        char modname[MODULE_NAME_LEN];
        struct module_entry *e;
        llist_t *l;
+       unsigned i;
+       unsigned hash;
 
        filename2modname(module, modname);
-       for (l = G.db; l != NULL; l = l->link) {
+
+       hash = 0;
+       for (i = 0; modname[i]; i++)
+               hash = ((hash << 5) + hash) + modname[i];
+       hash %= DB_HASH_SIZE;
+
+       for (l = G.db[hash]; l; l = l->link) {
                e = (struct module_entry *) l->data;
                if (strcmp(e->modname, modname) == 0)
                        return e;
@@ -181,15 +221,15 @@ static struct module_entry *helper_get_module(const char *module, int create)
 
        e = xzalloc(sizeof(*e));
        e->modname = xstrdup(modname);
-       llist_add_to(&G.db, e);
+       llist_add_to(&G.db[hash], e);
 
        return e;
 }
-static struct module_entry *get_or_add_modentry(const char *module)
+static ALWAYS_INLINE struct module_entry *get_or_add_modentry(const char *module)
 {
        return helper_get_module(module, 1);
 }
-static struct module_entry *get_modentry(const char *module)
+static ALWAYS_INLINE struct module_entry *get_modentry(const char *module)
 {
        return helper_get_module(module, 0);
 }
@@ -199,7 +239,7 @@ static void add_probe(const char *name)
        struct module_entry *m;
 
        m = get_or_add_modentry(name);
-       if (!(option_mask32 & MODPROBE_OPT_REMOVE)
+       if (!(option_mask32 & (OPT_REMOVE | OPT_SHOW_DEPS))
         && (m->flags & MODULE_FLAG_LOADED)
        ) {
                DBG("skipping %s, it is already loaded", name);
@@ -249,7 +289,7 @@ static int FAST_FUNC config_file_action(const char *filename,
                                continue;
                        filename2modname(tokens[1], wildcard);
 
-                       for (l = G.probes; l != NULL; l = l->link) {
+                       for (l = G.probes; l; l = l->link) {
                                m = (struct module_entry *) l->data;
                                if (fnmatch(wildcard, m->modname, 0) != 0)
                                        continue;
@@ -350,10 +390,7 @@ static char *parse_and_add_kcmdline_module_options(char *options, const char *mo
  */
 static int do_modprobe(struct module_entry *m)
 {
-       struct module_entry *m2 = m2; /* for compiler */
-       char *fn, *options;
        int rc, first;
-       llist_t *l;
 
        if (!(m->flags & MODULE_FLAG_FOUND_IN_MODDEP)) {
                if (!(option_mask32 & INSMOD_OPT_SILENT))
@@ -363,20 +400,26 @@ static int do_modprobe(struct module_entry *m)
        }
        DBG("do_modprob'ing %s", m->modname);
 
-       if (!(option_mask32 & MODPROBE_OPT_REMOVE))
+       if (!(option_mask32 & OPT_REMOVE))
                m->deps = llist_rev(m->deps);
 
-       for (l = m->deps; l != NULL; l = l->link)
-               DBG("dep: %s", l->data);
+       if (0) {
+               llist_t *l;
+               for (l = m->deps; l; l = l->link)
+                       DBG("dep: %s", l->data);
+       }
 
        first = 1;
        rc = 0;
        while (m->deps) {
+               struct module_entry *m2;
+               char *fn, *options;
+
                rc = 0;
                fn = llist_pop(&m->deps); /* we leak it */
                m2 = get_or_add_modentry(fn);
 
-               if (option_mask32 & MODPROBE_OPT_REMOVE) {
+               if (option_mask32 & OPT_REMOVE) {
                        /* modprobe -r */
                        if (m2->flags & MODULE_FLAG_LOADED) {
                                rc = bb_delete_module(m2->modname, O_EXCL);
@@ -396,16 +439,27 @@ static int do_modprobe(struct module_entry *m)
                        continue;
                }
 
-               if (m2->flags & MODULE_FLAG_LOADED) {
-                       DBG("%s is already loaded, skipping", fn);
-                       continue;
-               }
-
                options = m2->options;
                m2->options = NULL;
                options = parse_and_add_kcmdline_module_options(options, m2->modname);
                if (m == m2)
                        options = gather_options_str(options, G.cmdline_mopts);
+
+               if (option_mask32 & OPT_SHOW_DEPS) {
+                       printf(options ? "insmod %s/%s/%s %s\n"
+                                       : "insmod %s/%s/%s\n",
+                               CONFIG_DEFAULT_MODULES_DIR, G.uts.release, fn,
+                               options);
+                       free(options);
+                       continue;
+               }
+
+               if (m2->flags & MODULE_FLAG_LOADED) {
+                       DBG("%s is already loaded, skipping", fn);
+                       free(options);
+                       continue;
+               }
+
                rc = bb_init_module(fn, options);
                DBG("loaded %s '%s', rc:%d", fn, options, rc);
                if (rc == EEXIST)
@@ -453,7 +507,7 @@ static void load_modules_dep(void)
 
                /* Optimization... */
                if ((m->flags & MODULE_FLAG_LOADED)
-                && !(option_mask32 & MODPROBE_OPT_REMOVE)
+                && !(option_mask32 & (OPT_REMOVE | OPT_SHOW_DEPS))
                ) {
                        DBG("skip deps of %s, it's already loaded", tokens[0]);
                        continue;
@@ -474,25 +528,31 @@ static void load_modules_dep(void)
 int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int modprobe_main(int argc UNUSED_PARAM, char **argv)
 {
-       struct utsname uts;
        int rc;
        unsigned opt;
        struct module_entry *me;
 
+       INIT_G();
+
+       IF_LONG_OPTS(applet_long_options = modprobe_longopts;)
        opt_complementary = MODPROBE_COMPLEMENTARY;
        opt = getopt32(argv, INSMOD_OPTS MODPROBE_OPTS INSMOD_ARGS);
        argv += optind;
 
        /* Goto modules location */
        xchdir(CONFIG_DEFAULT_MODULES_DIR);
-       uname(&uts);
-       xchdir(uts.release);
+       uname(&G.uts);
+       xchdir(G.uts.release);
 
-       if (opt & MODPROBE_OPT_LIST_ONLY) {
+       if (opt & OPT_LIST_ONLY) {
+               int i;
                char name[MODULE_NAME_LEN];
                char *colon, *tokens[2];
                parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read);
 
+               for (i = 0; argv[i]; i++)
+                       replace(argv[i], '-', '_');
+
                while (config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)) {
                        colon = last_char_is(tokens[0], ':');
                        if (!colon)
@@ -502,7 +562,6 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
                        if (!argv[0])
                                puts(tokens[0]);
                        else {
-                               int i;
                                for (i = 0; argv[i]; i++) {
                                        if (fnmatch(argv[i], name, 0) == 0) {
                                                puts(tokens[0]);
@@ -518,7 +577,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
                logmode = LOGMODE_SYSLOG;
 
        if (!argv[0]) {
-               if (opt & MODPROBE_OPT_REMOVE) {
+               if (opt & OPT_REMOVE) {
                        /* "modprobe -r" (w/o params).
                         * "If name is NULL, all unused modules marked
                         * autoclean will be removed".
@@ -538,7 +597,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
                config_close(parser);
        }
 
-       if (opt & (MODPROBE_OPT_INSERT_ALL | MODPROBE_OPT_REMOVE)) {
+       if (opt & (OPT_INSERT_ALL | OPT_REMOVE)) {
                /* Each argument is a module name */
                do {
                        DBG("adding module %s", *argv);
@@ -548,7 +607,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
                /* First argument is module name, rest are parameters */
                DBG("probing just module %s", *argv);
                add_probe(argv[0]);
-               G.cmdline_mopts = parse_cmdline_module_options(argv);
+               G.cmdline_mopts = parse_cmdline_module_options(argv, /*quote_spaces:*/ 1);
        }
 
        /* Happens if all requested modules are already loaded */
@@ -572,7 +631,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
                        /* This is not an alias. Literal names are blacklisted
                         * only if '-b' is given.
                         */
-                       if (!(opt & MODPROBE_OPT_BLACKLIST)
+                       if (!(opt & OPT_BLACKLIST)
                         || !(me->flags & MODULE_FLAG_BLACKLISTED)
                        ) {
                                rc |= do_modprobe(me);
@@ -589,7 +648,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
                        m2 = get_or_add_modentry(realname);
                        if (!(m2->flags & MODULE_FLAG_BLACKLISTED)
                         && (!(m2->flags & MODULE_FLAG_LOADED)
-                           || (opt & MODPROBE_OPT_REMOVE))
+                           || (opt & (OPT_REMOVE | OPT_SHOW_DEPS)))
                        ) {
 //TODO: we can pass "me" as 2nd param to do_modprobe,
 //and make do_modprobe emit more meaningful error messages
index 7f39e25..12cb75c 100644 (file)
  *   Restructured (and partly rewritten) by:
  *   Björn Ekwall <bj0rn@blox.se> February 1999
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 #include "modutils.h"
-#include <libgen.h>
 #include <sys/utsname.h>
 
 #if ENABLE_FEATURE_INSMOD_LOADINKMEM
 #define USE_SINGLE
 #endif
 
+/* NDS32 support */
+#if defined(__nds32__) || defined(__NDS32__)
+#define CONFIG_USE_GOT_ENTRIES
+#define CONFIG_GOT_ENTRY_SIZE 4
+#define CONFIG_USE_SINGLE
+
+#if defined(__NDS32_EB__)
+#define MATCH_MACHINE(x) (x == EM_NDS32)
+#define SHT_RELM    SHT_RELA
+#define Elf32_RelM  Elf32_Rela
+#define ELFCLASSM   ELFCLASS32
+#endif
+
+#if defined(__NDS32_EL__)
+#define MATCH_MACHINE(x) (x == EM_NDS32)
+#define SHT_RELM    SHT_RELA
+#define Elf32_RelM  Elf32_Rela
+#define ELFCLASSM   ELFCLASS32
+#endif
+#endif
+
 /* blackfin */
 #if defined(BFIN)
 #define MATCH_MACHINE(x) (x == EM_BLACKFIN)
@@ -2423,14 +2443,12 @@ new_process_module_arguments(struct obj_file *f, const char *options)
                        bb_error_msg_and_die("symbol for parameter %s not found", param);
 
                /* Number of parameters */
+               min = max = 1;
                if (isdigit(*pinfo)) {
-                       min = strtoul(pinfo, &pinfo, 10);
+                       min = max = strtoul(pinfo, &pinfo, 10);
                        if (*pinfo == '-')
                                max = strtoul(pinfo + 1, &pinfo, 10);
-                       else
-                               max = min;
-               } else
-                       min = max = 1;
+               }
 
                contents = f->sections[sym->secidx]->contents;
                loc = contents + sym->value;
@@ -2452,7 +2470,8 @@ new_process_module_arguments(struct obj_file *f, const char *options)
                /* Parse parameter values */
                n = 0;
                p = val;
-               while (*p != 0) {
+               while (*p) {
+                       char sv_ch;
                        char *endp;
 
                        if (++n > max)
@@ -2461,21 +2480,25 @@ new_process_module_arguments(struct obj_file *f, const char *options)
                        switch (*pinfo) {
                        case 's':
                                len = strcspn(p, ",");
-                               p[len] = 0;
+                               sv_ch = p[len];
+                               p[len] = '\0';
                                obj_string_patch(f, sym->secidx,
                                                 loc - contents, p);
                                loc += tgt_sizeof_char_p;
                                p += len;
+                               *p = sv_ch;
                                break;
                        case 'c':
                                len = strcspn(p, ",");
-                               p[len] = 0;
+                               sv_ch = p[len];
+                               p[len] = '\0';
                                if (len >= charssize)
                                        bb_error_msg_and_die("string too long for %s (max %ld)", param,
                                                             charssize - 1);
                                strcpy((char *) loc, p);
                                loc += charssize;
                                p += len;
+                               *p = sv_ch;
                                break;
                        case 'b':
                                *loc++ = strtoul(p, &endp, 0);
@@ -3540,7 +3563,7 @@ static void check_tainted_module(struct obj_file *f, const char *m_name)
                else if (errno == EACCES)
                        kernel_has_tainted = 1;
                else {
-                       perror(TAINT_FILENAME);
+                       bb_simple_perror_msg(TAINT_FILENAME);
                        kernel_has_tainted = 0;
                }
        }
index 850a868..6187ca7 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2008 by Timo Teras <timo.teras@iki.fi>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "modutils.h"
 
@@ -62,7 +62,7 @@ char* FAST_FUNC filename2modname(const char *filename, char *modname)
        return modname;
 }
 
-char* FAST_FUNC parse_cmdline_module_options(char **argv)
+char* FAST_FUNC parse_cmdline_module_options(char **argv, int quote_spaces)
 {
        char *options;
        int optlen;
@@ -70,10 +70,31 @@ char* FAST_FUNC parse_cmdline_module_options(char **argv)
        options = xzalloc(1);
        optlen = 0;
        while (*++argv) {
-               options = xrealloc(options, optlen + 2 + strlen(*argv) + 2);
-               /* Spaces handled by "" pairs, but no way of escaping quotes */
-               optlen += sprintf(options + optlen, (strchr(*argv, ' ') ? "\"%s\" " : "%s "), *argv);
+               const char *fmt;
+               const char *var;
+               const char *val;
+
+               var = *argv;
+               options = xrealloc(options, optlen + 2 + strlen(var) + 2);
+               fmt = "%.*s%s ";
+               val = strchrnul(var, '=');
+               if (quote_spaces) {
+                       /*
+                        * modprobe (module-init-tools version 3.11.1) compat:
+                        * quote only value:
+                        * var="val with spaces", not "var=val with spaces"
+                        * (note: var *name* is not checked for spaces!)
+                        */
+                       if (*val) { /* has var=val format. skip '=' */
+                               val++;
+                               if (strchr(val, ' '))
+                                       fmt = "%.*s\"%s\" ";
+                       }
+               }
+               optlen += sprintf(options + optlen, fmt, (int)(val - var), var, val);
        }
+       /* Remove trailing space. Disabled */
+       /* if (optlen != 0) options[optlen-1] = '\0'; */
        return options;
 }
 
index d46870c..5f059c7 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2008 by Timo Teras <timo.teras@iki.fi>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #ifndef MODUTILS_H
@@ -21,7 +21,7 @@ void replace(char *s, char what, char with) FAST_FUNC;
 char *replace_underscores(char *s) FAST_FUNC;
 int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC;
 char *filename2modname(const char *filename, char *modname) FAST_FUNC;
-char *parse_cmdline_module_options(char **argv) FAST_FUNC;
+char *parse_cmdline_module_options(char **argv, int quote_spaces) FAST_FUNC;
 
 /* insmod for 2.4 and modprobe's options (insmod 2.6 has no options at all): */
 #define INSMOD_OPTS \
index ee32dfd..f13ff9e 100644 (file)
@@ -5,9 +5,23 @@
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  * Copyright (C) 2008 Timo Teras <timo.teras@iki.fi>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//applet:IF_RMMOD(APPLET(rmmod, BB_DIR_SBIN, BB_SUID_DROP))
+
+//usage:#if !ENABLE_MODPROBE_SMALL
+//usage:#define rmmod_trivial_usage
+//usage:       "[-wfa] [MODULE]..."
+//usage:#define rmmod_full_usage "\n\n"
+//usage:       "Unload kernel modules\n"
+//usage:     "\n       -w      Wait until the module is no longer used"
+//usage:     "\n       -f      Force unload"
+//usage:     "\n       -a      Remove all unused modules (recursively)"
+//usage:#define rmmod_example_usage
+//usage:       "$ rmmod tulip\n"
+//usage:#endif
+
 #include "libbb.h"
 #include "modutils.h"
 
@@ -20,9 +34,9 @@ int rmmod_main(int argc UNUSED_PARAM, char **argv)
        /* Parse command line. */
        n = getopt32(argv, "wfas"); // -s ignored
        argv += optind;
-       if (n & 1)      // --wait
+       if (n & 1)  // --wait
                flags &= ~O_NONBLOCK;
-       if (n & 2)      // --force
+       if (n & 2)  // --force
                flags |= O_TRUNC;
        if (n & 4) {
                /* Unload _all_ unused modules via NULL delete_module() call */
@@ -46,7 +60,7 @@ int rmmod_main(int argc UNUSED_PARAM, char **argv)
                        filename2modname(bname, modname);
                if (bb_delete_module(modname, flags))
                        bb_error_msg_and_die("can't unload '%s': %s",
-                                            modname, moderror(errno));
+                                       modname, moderror(errno));
        }
 
        return EXIT_SUCCESS;
index 4494362..ca0ddcd 100644 (file)
@@ -51,18 +51,21 @@ config VERBOSE_RESOLUTION_ERRORS
 config ARP
        bool "arp"
        default y
+       select PLATFORM_LINUX
        help
          Manipulate the system ARP cache.
 
 config ARPING
        bool "arping"
        default y
+       select PLATFORM_LINUX
        help
          Ping hosts by ARP packets.
 
 config BRCTL
        bool "brctl"
        default y
+       select PLATFORM_LINUX
        help
          Manage ethernet bridges.
          Supports addbr/delbr and addif/delif.
@@ -79,12 +82,12 @@ config FEATURE_BRCTL_FANCY
          This adds about 600 bytes.
 
 config FEATURE_BRCTL_SHOW
-       bool "Support show, showmac and showstp"
+       bool "Support show"
        default y
        depends on BRCTL && FEATURE_BRCTL_FANCY
        help
          Add support for option which prints the current config:
-           showmacs, showstp, show
+           show
 
 config DNSD
        bool "dnsd"
@@ -95,6 +98,7 @@ config DNSD
 config ETHER_WAKE
        bool "ether-wake"
        default y
+       select PLATFORM_LINUX
        help
          Send a magic packet to wake up sleeping machines.
 
@@ -195,14 +199,22 @@ config FEATURE_HTTPD_BASIC_AUTH
        help
          Utilizes password settings from /etc/httpd.conf for basic
          authentication on a per url basis.
+         Example for httpd.conf file:
+         /adm:toor:PaSsWd
 
 config FEATURE_HTTPD_AUTH_MD5
        bool "Support MD5 crypted passwords for http Authentication"
        default y
        depends on FEATURE_HTTPD_BASIC_AUTH
        help
-         Enables basic per URL authentication from /etc/httpd.conf
-         using md5 passwords.
+         Enables encrypted passwords, and wildcard user/passwords
+         in httpd.conf file.
+         User '*' means 'any system user name is ok',
+         password of '*' means 'use system password for this user'
+         Examples:
+         /adm:toor:$1$P/eKnWXS$aI1aPGxT.dJD5SzqAKWrF0
+         /adm:root:*
+         /wiki:*:*
 
 config FEATURE_HTTPD_CGI
        bool "Support Common Gateway Interface (CGI)"
@@ -219,8 +231,8 @@ config FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
        help
          This option enables support for running scripts through an
          interpreter. Turn this on if you want PHP scripts to work
-         properly. You need to supply an additional line in your httpd
-         config file:
+         properly. You need to supply an additional line in your
+         httpd.conf file:
          *.php:/path/to/your/php
 
 config FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
@@ -266,9 +278,18 @@ config FEATURE_HTTPD_PROXY
          Then a request to /url/myfile will be forwarded to
          http://hostname[:port]/new/path/myfile.
 
+config FEATURE_HTTPD_GZIP
+       bool "Support for GZIP content encoding"
+       default y
+       depends on HTTPD
+       help
+         Makes httpd send files using GZIP content encoding if the
+         client supports it and a pre-compressed <file>.gz exists.
+
 config IFCONFIG
        bool "ifconfig"
        default y
+       select PLATFORM_LINUX
        help
          Ifconfig is used to configure the kernel-resident network interfaces.
 
@@ -316,6 +337,7 @@ config FEATURE_IFCONFIG_BROADCAST_PLUS
 config IFENSLAVE
        bool "ifenslave"
        default y
+       select PLATFORM_LINUX
        help
          Userspace application to bind several interfaces
          to a logical interface (use with kernel bonding driver).
@@ -323,6 +345,7 @@ config IFENSLAVE
 config IFPLUGD
        bool "ifplugd"
        default y
+       select PLATFORM_LINUX
        help
          Network interface plug detection daemon.
 
@@ -365,6 +388,7 @@ config FEATURE_IFUPDOWN_IP_BUILTIN
        bool "Use busybox ip applet"
        default y
        depends on FEATURE_IFUPDOWN_IP
+       select PLATFORM_LINUX
        select IP
        select FEATURE_IP_ADDRESS
        select FEATURE_IP_LINK
@@ -483,6 +507,7 @@ config FEATURE_INETD_RPC
 config IP
        bool "ip"
        default y
+       select PLATFORM_LINUX
        help
          The "ip" applet is a TCP/IP interface configuration and routing
          utility. You generally don't need "ip" to use busybox with
@@ -595,37 +620,10 @@ config FEATURE_IPCALC_LONG_OPTIONS
        help
          Support long options for the ipcalc applet.
 
-config NAMEIF
-       bool "nameif"
-       default y
-       select FEATURE_SYSLOG
-       help
-         nameif is used to rename network interface by its MAC address.
-         Renamed interfaces MUST be in the down state.
-         It is possible to use a file (default: /etc/mactab)
-         with list of new interface names and MACs.
-         Maximum interface name length: IFNAMSIZ = 16
-         File fields are separated by space or tab.
-         File format:
-         # Comment
-         new_interface_name    XX:XX:XX:XX:XX:XX
-
-config FEATURE_NAMEIF_EXTENDED
-       bool "Extended nameif"
-       default y
-       depends on NAMEIF
-       help
-         This extends the nameif syntax to support the bus_info and driver
-         checks. The syntax is compatible to the normal nameif.
-         File format:
-           new_interface_name  driver=asix bus=usb-0000:00:08.2-3
-           new_interface_name  bus=usb-0000:00:08.2-3 00:80:C8:38:91:B5
-           new_interface_name  mac=00:80:C8:38:91:B5
-           new_interface_name  00:80:C8:38:91:B5
-
 config NETSTAT
        bool "netstat"
        default y
+       select PLATFORM_LINUX
        help
          netstat prints information about the Linux networking subsystem.
 
@@ -654,6 +652,7 @@ config NSLOOKUP
 config NTPD
        bool "ntpd"
        default y
+       select PLATFORM_LINUX
        help
          The NTP client/server daemon.
 
@@ -665,28 +664,6 @@ config FEATURE_NTPD_SERVER
          Make ntpd usable as a NTP server. If you disable this option
          ntpd will be usable only as a NTP client.
 
-config PING
-       bool "ping"
-       default y
-       help
-         ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to
-         elicit an ICMP ECHO_RESPONSE from a host or gateway.
-
-config PING6
-       bool "ping6"
-       default y
-       depends on FEATURE_IPV6 && PING
-       help
-         This will give you a ping that can talk IPv6.
-
-config FEATURE_FANCY_PING
-       bool "Enable fancy ping output"
-       default y
-       depends on PING
-       help
-         Make the output from the ping applet include statistics, and at the
-         same time provide full support for ICMP packets.
-
 config PSCAN
        bool "pscan"
        default y
@@ -696,12 +673,14 @@ config PSCAN
 config ROUTE
        bool "route"
        default y
+       select PLATFORM_LINUX
        help
          Route displays or manipulates the kernel's IP routing tables.
 
 config SLATTACH
        bool "slattach"
        default y
+       select PLATFORM_LINUX
        help
          slattach is a small utility to attach network interfaces to serial
          lines.
@@ -782,7 +761,7 @@ config TELNETD
 
                  mount -t devpts devpts /dev/pts
 
-         You need to be sure that Busybox has LOGIN and
+         You need to be sure that busybox has LOGIN and
          FEATURE_SUID enabled. And finally, you should make
          certain that Busybox has been installed setuid root:
 
@@ -888,6 +867,7 @@ config TFTP_DEBUG
 config TRACEROUTE
        bool "traceroute"
        default y
+       select PLATFORM_LINUX
        help
          Utility to trace the route of IP packets.
 
@@ -924,6 +904,7 @@ config FEATURE_TRACEROUTE_USE_ICMP
 config TUNCTL
        bool "tunctl"
        default y
+       select PLATFORM_LINUX
        help
          tunctl creates or deletes tun devices.
 
@@ -956,6 +937,7 @@ config UDPSVD
 config VCONFIG
        bool "vconfig"
        default y
+       select PLATFORM_LINUX
        help
          Creates, removes, and configures VLAN interfaces
 
@@ -963,8 +945,8 @@ config WGET
        bool "wget"
        default y
        help
-         wget is a utility for non-interactive download of files from HTTP,
-         HTTPS, and FTP servers.
+         wget is a utility for non-interactive download of files from HTTP
+         and FTP servers.
 
 config FEATURE_WGET_STATUSBAR
        bool "Enable a nifty process meter (+2k)"
@@ -987,9 +969,24 @@ config FEATURE_WGET_LONG_OPTIONS
        help
          Support long options for the wget applet.
 
+config FEATURE_WGET_TIMEOUT
+       bool "Enable timeout option -T SEC"
+       default y
+       depends on WGET
+       help
+         Supports network read and connect timeouts for wget,
+         so that wget will give up and timeout, through the -T
+         command line option.
+
+         Currently only connect and network data read timeout are
+         supported (i.e., timeout is not applied to the DNS query). When
+         FEATURE_WGET_LONG_OPTIONS is also enabled, the --timeout option
+         will work in addition to -T.
+
 config ZCIP
        bool "zcip"
        default y
+       select PLATFORM_LINUX
        select FEATURE_SYSLOG
        help
          ZCIP provides ZeroConf IPv4 address selection, according to RFC 3927.
index e1a4ebc..944f27b 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
@@ -30,8 +30,6 @@ lib-$(CONFIG_NC)           += nc.o
 lib-$(CONFIG_NETSTAT)      += netstat.o
 lib-$(CONFIG_NSLOOKUP)     += nslookup.o
 lib-$(CONFIG_NTPD)         += ntpd.o
-lib-$(CONFIG_PING)         += ping.o
-lib-$(CONFIG_PING6)        += ping.o
 lib-$(CONFIG_PSCAN)        += pscan.o
 lib-$(CONFIG_ROUTE)        += route.o
 lib-$(CONFIG_SLATTACH)     += slattach.o
index 0ef267a..e79b1b6 100644 (file)
  * modified for getopt32 by Arne Bernin <arne [at] alamut.de>
  */
 
+//usage:#define arp_trivial_usage
+//usage:     "\n[-vn]  [-H HWTYPE] [-i IF] -a [HOSTNAME]"
+//usage:     "\n[-v]               [-i IF] -d HOSTNAME [pub]"
+//usage:     "\n[-v]   [-H HWTYPE] [-i IF] -s HOSTNAME HWADDR [temp]"
+//usage:     "\n[-v]   [-H HWTYPE] [-i IF] -s HOSTNAME HWADDR [netmask MASK] pub"
+//usage:     "\n[-v]   [-H HWTYPE] [-i IF] -Ds HOSTNAME IFACE [netmask MASK] pub"
+//usage:#define arp_full_usage "\n\n"
+//usage:       "Manipulate ARP cache\n"
+//usage:       "\n     -a              Display (all) hosts"
+//usage:       "\n     -d              Delete ARP entry"
+//usage:       "\n     -s              Set new entry"
+//usage:       "\n     -v              Verbose"
+//usage:       "\n     -n              Don't resolve names"
+//usage:       "\n     -i IF           Network interface"
+//usage:       "\n     -D              Read HWADDR from IFACE"
+//usage:       "\n     -A,-p AF        Protocol family"
+//usage:       "\n     -H HWTYPE       Hardware address type"
+
 #include "libbb.h"
 #include "inet_common.h"
 
@@ -195,16 +213,15 @@ static int arp_del(char **args)
 }
 
 /* Get the hardware address to a specified interface name */
-static void arp_getdevhw(char *ifname, struct sockaddr *sa,
-                                                const struct hwtype *hwt)
+static void arp_getdevhw(char *ifname, struct sockaddr *sa)
 {
        struct ifreq ifr;
        const struct hwtype *xhw;
 
        strcpy(ifr.ifr_name, ifname);
        ioctl_or_perror_and_die(sockfd, SIOCGIFHWADDR, &ifr,
-                                       "cant get HW-Address for '%s'", ifname);
-       if (hwt && (ifr.ifr_hwaddr.sa_family != hw->type)) {
+                                       "can't get HW-Address for '%s'", ifname);
+       if (hw_set && (ifr.ifr_hwaddr.sa_family != hw->type)) {
                bb_error_msg_and_die("protocol type mismatch");
        }
        memcpy(sa, &(ifr.ifr_hwaddr), sizeof(struct sockaddr));
@@ -215,8 +232,8 @@ static void arp_getdevhw(char *ifname, struct sockaddr *sa,
                        xhw = get_hwntype(-1);
                }
                bb_error_msg("device '%s' has HW address %s '%s'",
-                                        ifname, xhw->name,
-                                        xhw->print((unsigned char *) &ifr.ifr_hwaddr.sa_data));
+                               ifname, xhw->name,
+                               xhw->print((unsigned char *) &ifr.ifr_hwaddr.sa_data));
        }
 }
 
@@ -243,7 +260,7 @@ static int arp_set(char **args)
                bb_error_msg_and_die("need hardware address");
        }
        if (option_mask32 & ARP_OPT_D) {
-               arp_getdevhw(*args++, &req.arp_ha, hw_set ? hw : NULL);
+               arp_getdevhw(*args++, &req.arp_ha);
        } else {
                if (hw->input(*args++, &req.arp_ha) < 0) {
                        bb_error_msg_and_die("invalid hardware address");
@@ -327,7 +344,7 @@ static int arp_set(char **args)
 /* Print the contents of an ARP request block. */
 static void
 arp_disp(const char *name, char *ip, int type, int arp_flags,
-                char *hwa, char *mask, char *dev)
+               char *hwa, char *mask, char *dev)
 {
        static const int arp_masks[] = {
                ATF_PERM, ATF_PUBL,
@@ -410,7 +427,7 @@ static int arp_show(char *name)
                /* All these strings can't overflow
                 * because fgets above reads limited amount of data */
                num = sscanf(line, "%s 0x%x 0x%x %s %s %s\n",
-                                        ip, &type, &flags, hwa, mask, dev);
+                                       ip, &type, &flags, hwa, mask, dev);
                if (num < 4)
                        break;
 
@@ -442,12 +459,12 @@ static int arp_show(char *name)
                arp_disp(hostname, ip, type, flags, hwa, mask, dev);
        }
        if (option_mask32 & ARP_OPT_v)
-               printf("Entries: %d\tSkipped: %d\tFound: %d\n",
-                          entries, entries - shown, shown);
+               printf("Entries: %u\tSkipped: %u\tFound: %u\n",
+                               entries, entries - shown, shown);
 
        if (!shown) {
                if (hw_set || host || device[0])
-                       printf("No match found in %d entries\n", entries);
+                       printf("No match found in %u entries\n", entries);
        }
        if (ENABLE_FEATURE_CLEAN_UP) {
                free((char*)host);
@@ -459,28 +476,33 @@ static int arp_show(char *name)
 int arp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int arp_main(int argc UNUSED_PARAM, char **argv)
 {
-       const char *hw_type = "ether";
+       const char *hw_type;
        const char *protocol;
        unsigned opts;
 
        INIT_G();
 
        xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sockfd);
+
        ap = get_aftype(DFLT_AF);
-       if (!ap)
-               bb_error_msg_and_die("%s: %s not supported", DFLT_AF, "address family");
+       /* Defaults are always supported */
+       //if (!ap)
+       //      bb_error_msg_and_die("%s: %s not supported", DFLT_AF, "address family");
+       hw = get_hwtype(DFLT_HW);
+       //if (!hw)
+       //      bb_error_msg_and_die("%s: %s not supported", DFLT_HW, "hardware type");
 
        opts = getopt32(argv, "A:p:H:t:i:adnDsv", &protocol, &protocol,
                                 &hw_type, &hw_type, &device);
        argv += optind;
        if (opts & (ARP_OPT_A | ARP_OPT_p)) {
                ap = get_aftype(protocol);
-               if (ap == NULL)
+               if (!ap)
                        bb_error_msg_and_die("%s: unknown %s", protocol, "address family");
        }
-       if (opts & (ARP_OPT_A | ARP_OPT_p)) {
+       if (opts & (ARP_OPT_H | ARP_OPT_t)) {
                hw = get_hwtype(hw_type);
-               if (hw == NULL)
+               if (!hw)
                        bb_error_msg_and_die("%s: unknown %s", hw_type, "hardware type");
                hw_set = 1;
        }
@@ -489,17 +511,9 @@ int arp_main(int argc UNUSED_PARAM, char **argv)
        if (ap->af != AF_INET) {
                bb_error_msg_and_die("%s: kernel only supports 'inet'", ap->name);
        }
-
-       /* If no hw type specified get default */
-       if (!hw) {
-               hw = get_hwtype(DFLT_HW);
-               if (!hw)
-                       bb_error_msg_and_die("%s: %s not supported", DFLT_HW, "hardware type");
-       }
-
        if (hw->alen <= 0) {
                bb_error_msg_and_die("%s: %s without ARP support",
-                                                        hw->name, "hardware type");
+                               hw->name, "hardware type");
        }
 
        /* Now see what we have to do here... */
@@ -510,6 +524,7 @@ int arp_main(int argc UNUSED_PARAM, char **argv)
                        return arp_set(argv);
                return arp_del(argv);
        }
+
        //if (opts & ARP_OPT_a) - default
        return arp_show(argv[0]);
 }
index effe418..a4421ed 100644 (file)
@@ -1,13 +1,27 @@
 /* vi: set sw=4 ts=4: */
 /*
- * arping.c - Ping hosts by ARP requests/replies
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
- *
- * Author:     Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ * Author: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
  * Busybox port: Nick Fedchik <nick@fedchik.org.ua>
  */
 
+//usage:#define arping_trivial_usage
+//usage:       "[-fqbDUA] [-c CNT] [-w TIMEOUT] [-I IFACE] [-s SRC_IP] DST_IP"
+//usage:#define arping_full_usage "\n\n"
+//usage:       "Send ARP requests/replies\n"
+//usage:     "\n       -f              Quit on first ARP reply"
+//usage:     "\n       -q              Quiet"
+//usage:     "\n       -b              Keep broadcasting, don't go unicast"
+//usage:     "\n       -D              Duplicated address detection mode"
+//usage:     "\n       -U              Unsolicited ARP mode, update your neighbors"
+//usage:     "\n       -A              ARP answer mode, update your neighbors"
+//usage:     "\n       -c N            Stop after sending N ARP requests"
+//usage:     "\n       -w TIMEOUT      Time to wait for ARP reply, seconds"
+//usage:     "\n       -I IFACE        Interface to use (default eth0)"
+//usage:     "\n       -s SRC_IP       Sender IP address"
+//usage:     "\n       DST_IP          Target IP address"
+
 #include <arpa/inet.h>
 #include <net/if.h>
 #include <netinet/ether.h>
index a36ab45..207b069 100644 (file)
@@ -7,11 +7,35 @@
  * Some helper functions from bridge-utils are
  * Copyright (C) 2000 Lennert Buytenhek
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 /* This applet currently uses only the ioctl interface and no sysfs at all.
  * At the time of this writing this was considered a feature.
  */
+
+//usage:#define brctl_trivial_usage
+//usage:       "COMMAND [BRIDGE [INTERFACE]]"
+//usage:#define brctl_full_usage "\n\n"
+//usage:       "Manage ethernet bridges\n"
+//usage:     "\nCommands:"
+//usage:       IF_FEATURE_BRCTL_SHOW(
+//usage:     "\n       show                    Show a list of bridges"
+//usage:       )
+//usage:     "\n       addbr BRIDGE            Create BRIDGE"
+//usage:     "\n       delbr BRIDGE            Delete BRIDGE"
+//usage:     "\n       addif BRIDGE IFACE      Add IFACE to BRIDGE"
+//usage:     "\n       delif BRIDGE IFACE      Delete IFACE from BRIDGE"
+//usage:       IF_FEATURE_BRCTL_FANCY(
+//usage:     "\n       setageing BRIDGE TIME           Set ageing time"
+//usage:     "\n       setfd BRIDGE TIME               Set bridge forward delay"
+//usage:     "\n       sethello BRIDGE TIME            Set hello time"
+//usage:     "\n       setmaxage BRIDGE TIME           Set max message age"
+//usage:     "\n       setpathcost BRIDGE COST         Set path cost"
+//usage:     "\n       setportprio BRIDGE PRIO         Set port priority"
+//usage:     "\n       setbridgeprio BRIDGE PRIO       Set bridge priority"
+//usage:     "\n       stp BRIDGE [1/yes/on|0/no/off]  STP on/off"
+//usage:       )
+
 #include "libbb.h"
 #include <linux/sockios.h>
 #include <net/if.h>
@@ -43,7 +67,7 @@
 # include <linux/if_bridge.h>
 
 /* FIXME: These 4 funcs are not really clean and could be improved */
-static ALWAYS_INLINE void strtotimeval(struct timeval *tv,
+static ALWAYS_INLINE void bb_strtotimeval(struct timeval *tv,
                const char *time_str)
 {
        double secs;
@@ -80,7 +104,7 @@ static void jiffies_to_tv(struct timeval *tv, unsigned long jiffies)
 static unsigned long str_to_jiffies(const char *time_str)
 {
        struct timeval tv;
-       strtotimeval(&tv, time_str);
+       bb_strtotimeval(&tv, time_str);
        return tv_to_jiffies(&tv);
 }
 
@@ -105,15 +129,15 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
                "setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
                "setpathcost\0" "setportprio\0" "setbridgeprio\0"
        )
-       IF_FEATURE_BRCTL_SHOW("showmacs\0" "show\0");
+       IF_FEATURE_BRCTL_SHOW("show\0");
 
        enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif
                IF_FEATURE_BRCTL_FANCY(,
-                  ARG_stp,
-                  ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
-                  ARG_setpathcost, ARG_setportprio, ARG_setbridgeprio
+                       ARG_stp,
+                       ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
+                       ARG_setpathcost, ARG_setportprio, ARG_setbridgeprio
                )
-               IF_FEATURE_BRCTL_SHOW(, ARG_showmacs, ARG_show)
+               IF_FEATURE_BRCTL_SHOW(, ARG_show)
        };
 
        int fd;
@@ -184,7 +208,7 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
                                                tabs = 1;
                                        printf("\t\t%s\n", ifname);
                                }
-                               if (!tabs)      /* bridge has no interfaces */
+                               if (!tabs)  /* bridge has no interfaces */
                                        bb_putchar('\n');
                        }
                        goto done;
@@ -261,7 +285,7 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
                                        bb_error_msg_and_die(bb_msg_invalid_arg, *argv, "port");
                                memset(ifidx, 0, sizeof ifidx);
                                arm_ioctl(args, BRCTL_GET_PORT_LIST, (unsigned long)ifidx,
-                                                 MAX_PORTS);
+                                               MAX_PORTS);
                                xioctl(fd, SIOCDEVPRIVATE, &ifr);
                                for (i = 0; i < MAX_PORTS; i++) {
                                        if (ifidx[i] == port) {
@@ -271,7 +295,7 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
                                }
                        }
                        arg1 = port;
-                       arg2 = xatoi_u(*argv);
+                       arg2 = xatoi_positive(*argv);
                        if (key == ARG_setbridgeprio) {
                                arg1 = arg2;
                                arg2 = 0;
index 1a99040..fe98400 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (C) 2005 Odd Arild Olsen (oao at fibula dot no)
  * Copyright (C) 2003 Paul Sheer
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Odd Arild Olsen started out with the sheerdns [1] of Paul Sheer and rewrote
  * it into a shape which I believe is both easier to understand and maintain.
  * the first porting of oao' scdns to busybox also.
  */
 
+//usage:#define dnsd_trivial_usage
+//usage:       "[-dvs] [-c CONFFILE] [-t TTL_SEC] [-p PORT] [-i ADDR]"
+//usage:#define dnsd_full_usage "\n\n"
+//usage:       "Small static DNS server daemon\n"
+//usage:     "\n       -c FILE Config file"
+//usage:     "\n       -t SEC  TTL"
+//usage:     "\n       -p PORT Listen on PORT"
+//usage:     "\n       -i ADDR Listen on ADDR"
+//usage:     "\n       -d      Daemonize"
+//usage:     "\n       -v      Verbose"
+//usage:     "\n       -s      Send successful replies only. Use this if you want"
+//usage:     "\n               to use /etc/resolv.conf with two nameserver lines:"
+//usage:     "\n                       nameserver DNSD_SERVER"
+//usage:     "\n                       nameserver NORMAL_DNS_SERVER"
+
 #include "libbb.h"
 #include <syslog.h>
 
@@ -388,7 +403,7 @@ static int process_packet(struct dns_entry *conf_data,
        query_len = strlen(query_string) + 1;
        /* may be unaligned! */
        unaligned_type_class = (void *)(query_string + query_len);
-       query_len += sizeof(unaligned_type_class);
+       query_len += sizeof(*unaligned_type_class);
        /* where to append answer block */
        answb = (void *)(unaligned_type_class + 1);
 
index deeb68c..c38547d 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * ether-wake.c - Send a magic packet to wake up sleeping machines.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Author:      Donald Becker, http://www.scyld.com/"; http://www.scyld.com/wakeonlan.html
  * Busybox port: Christian Volkmann <haveaniceday@online.de>
@@ -49,9 +49,9 @@
  *     Copyright 1999-2003 Donald Becker and Scyld Computing Corporation.
  *
  *     The author may be reached as becker@scyld, or C/O
- *      Scyld Computing Corporation
- *      914 Bay Ridge Road, Suite 220
- *      Annapolis MD 21403
+ *     Scyld Computing Corporation
+ *     914 Bay Ridge Road, Suite 220
+ *     Annapolis MD 21403
  *
  *   Notes:
  *   On some systems dropping root capability allows the process to be
  *   An alternative to needing 'root' is using a UDP broadcast socket, however
  *   doing so only works with adapters configured for unicast+broadcast Rx
  *   filter.  That configuration consumes more power.
-*/
+ */
 
+//usage:#define ether_wake_trivial_usage
+//usage:       "[-b] [-i IFACE] [-p aa:bb:cc:dd[:ee:ff]/a.b.c.d] MAC"
+//usage:#define ether_wake_full_usage "\n\n"
+//usage:       "Send a magic packet to wake up sleeping machines.\n"
+//usage:       "MAC must be a station address (00:11:22:33:44:55) or\n"
+//usage:       "a hostname with a known 'ethers' entry.\n"
+//usage:     "\n       -b              Broadcast the packet"
+//usage:     "\n       -i IFACE        Interface to use (default eth0)"
+//usage:     "\n       -p PASSWORD     Append four or six byte PASSWORD to the packet"
 
+#include "libbb.h"
 #include <netpacket/packet.h>
-#include <net/ethernet.h>
 #include <netinet/ether.h>
 #include <linux/if.h>
 
-#include "libbb.h"
-
 /* Note: PF_INET, SOCK_DGRAM, IPPROTO_UDP would allow SIOCGIFHWADDR to
  * work as non-root, but we need SOCK_PACKET to specify the Ethernet
  * destination address.
@@ -106,7 +113,7 @@ void bb_debug_dump_packet(unsigned char *outpack, int pktsize)
  *    Host name
  *    IP address string
  *    MAC address string
-*/
+ */
 static void get_dest_addr(const char *hostid, struct ether_addr *eaddr)
 {
        struct ether_addr *eap;
@@ -114,10 +121,7 @@ static void get_dest_addr(const char *hostid, struct ether_addr *eaddr)
        eap = ether_aton_r(hostid, eaddr);
        if (eap) {
                bb_debug_msg("The target station address is %s\n\n", ether_ntoa(eap));
-#if !defined(__UCLIBC_MAJOR__) \
- || __UCLIBC_MAJOR__ > 0 \
- || __UCLIBC_MINOR__ > 9 \
- || (__UCLIBC_MINOR__ == 9 && __UCLIBC_SUBLEVEL__ >= 30)
+#if !defined(__UCLIBC__) || UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 30)
        } else if (ether_hostton(hostid, eaddr) == 0) {
                bb_debug_msg("Station address for hostname %s is %s\n\n", hostid, ether_ntoa(eaddr));
 #endif
@@ -126,7 +130,8 @@ static void get_dest_addr(const char *hostid, struct ether_addr *eaddr)
        }
 }
 
-static int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast)
+#define PKT_HEADER_SIZE (20 + 16*6)
+static int fill_pkt_header(unsigned char *pkt, struct ether_addr *eaddr, int broadcast)
 {
        int i;
        unsigned char *station_addr = eaddr->ether_addr_octet;
@@ -149,7 +154,7 @@ static int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast)
                memcpy(pkt, station_addr, 6); /* 20,26,32,... */
        }
 
-       return 20 + 16*6; /* length of packet */
+       return PKT_HEADER_SIZE; /* length of packet */
 }
 
 static int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd)
@@ -189,12 +194,12 @@ int ether_wake_main(int argc UNUSED_PARAM, char **argv)
        unsigned flags;
        unsigned char wol_passwd[6];
        int wol_passwd_sz = 0;
-       int s;                                          /* Raw socket */
+       int s;  /* Raw socket */
        int pktsize;
-       unsigned char outpack[1000];
+       unsigned char outpack[PKT_HEADER_SIZE + 6 /* max passwd size */ + 16 /* paranoia */];
 
        struct ether_addr eaddr;
-       struct whereto_t whereto;       /* who to wake up */
+       struct whereto_t whereto;  /* who to wake up */
 
        /* handle misc user options */
        opt_complementary = "=1";
@@ -213,7 +218,7 @@ int ether_wake_main(int argc UNUSED_PARAM, char **argv)
        get_dest_addr(argv[optind], &eaddr);
 
        /* fill out the header of the packet */
-       pktsize = get_fill(outpack, &eaddr, flags /* & 1 OPT_BROADCAST */);
+       pktsize = fill_pkt_header(outpack, &eaddr, flags /* & 1 OPT_BROADCAST */);
 
        bb_debug_dump_packet(outpack, pktsize);
 
@@ -231,9 +236,9 @@ int ether_wake_main(int argc UNUSED_PARAM, char **argv)
                {
                        unsigned char *hwaddr = if_hwaddr.ifr_hwaddr.sa_data;
                        printf("The hardware address (SIOCGIFHWADDR) of %s is type %d  "
-                                  "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n\n", ifname,
-                                  if_hwaddr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1],
-                                  hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
+                               "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n\n", ifname,
+                               if_hwaddr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1],
+                               hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
                }
 # endif
        }
index e8cae0a..33db964 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Author: Adam Tkac <vonsch@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  *
  * Only subset of FTP protocol is implemented but vast majority of clients
  * should not have any problem.
  * You have to run this daemon via inetd.
  */
 
+//usage:#define ftpd_trivial_usage
+//usage:       "[-wvS] [-t N] [-T N] [DIR]"
+//usage:#define ftpd_full_usage "\n\n"
+//usage:       "Anonymous FTP server\n"
+//usage:       "\n"
+//usage:       "ftpd should be used as an inetd service.\n"
+//usage:       "ftpd's line for inetd.conf:\n"
+//usage:       "       21 stream tcp nowait root ftpd ftpd /files/to/serve\n"
+//usage:       "It also can be ran from tcpsvd:\n"
+//usage:       "       tcpsvd -vE 0.0.0.0 21 ftpd /files/to/serve\n"
+//usage:     "\n       -w      Allow upload"
+//usage:     "\n       -v      Log errors to stderr. -vv: verbose log"
+//usage:     "\n       -S      Log errors to syslog. -SS: verbose log"
+//usage:     "\n       -t,-T   Idle and absolute timeouts"
+//usage:     "\n       DIR     Change root to this directory"
+
 #include "libbb.h"
 #include <syslog.h>
 #include <netinet/tcp.h>
@@ -206,7 +222,7 @@ cmdio_write_error(unsigned status)
 {
        *(uint32_t *) G.msg_err = status;
        xwrite(STDOUT_FILENO, G.msg_err, sizeof("NNN " MSG_ERR) - 1);
-       if (G.verbose > 1)
+       if (G.verbose > 0)
                verbose_log(G.msg_err);
 }
 #define WRITE_ERR(a) cmdio_write_error(STRNUM32sp(a))
@@ -416,7 +432,7 @@ bind_for_passive_mode(void)
        G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
        setsockopt_reuseaddr(fd);
 
-       set_nport(G.local_addr, 0);
+       set_nport(&G.local_addr->u.sa, 0);
        xbind(fd, &G.local_addr->u.sa, G.local_addr->len);
        xlisten(fd, 1);
        getsockname(fd, &G.local_addr->u.sa, &G.local_addr->len);
@@ -525,7 +541,7 @@ handle_port(void)
        G.port_addr = xdotted2sockaddr(raw, port);
 #else
        G.port_addr = get_peer_lsa(STDIN_FILENO);
-       set_nport(G.port_addr, htons(port));
+       set_nport(&G.port_addr->u.sa, htons(port));
 #endif
        WRITE_OK(FTP_PORTOK);
 }
@@ -534,7 +550,7 @@ static void
 handle_rest(void)
 {
        /* When ftp_arg == NULL simply restart from beginning */
-       G.restart_pos = G.ftp_arg ? xatoi_u(G.ftp_arg) : 0;
+       G.restart_pos = G.ftp_arg ? xatoi_positive(G.ftp_arg) : 0;
        WRITE_OK(FTP_RESTOK);
 }
 
@@ -805,7 +821,7 @@ handle_size_or_mdtm(int need_size)
                gmtime_r(&statbuf.st_mtime, &broken_out);
                sprintf(buf, STR(FTP_STATFILE_OK)" %04u%02u%02u%02u%02u%02u\r\n",
                        broken_out.tm_year + 1900,
-                       broken_out.tm_mon,
+                       broken_out.tm_mon + 1,
                        broken_out.tm_mday,
                        broken_out.tm_hour,
                        broken_out.tm_min,
@@ -911,6 +927,7 @@ handle_upload_common(int is_append, int is_unique)
         || fstat(local_file_fd, &statbuf) != 0
         || !S_ISREG(statbuf.st_mode)
        ) {
+               free(tempname);
                WRITE_ERR(FTP_UPLOADFAIL);
                if (local_file_fd >= 0)
                        goto close_local_and_bail;
@@ -1163,8 +1180,7 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv)
 #endif
 
        if (argv[optind]) {
-               xchdir(argv[optind]);
-               chroot(".");
+               xchroot(argv[optind]);
        }
 
        //umask(077); - admin can set umask before starting us
index 120ccff..8283366 100644 (file)
  * Based on wget.c by Chip Rosenthal Covad Communications
  * <chip@laserlink.net>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define ftpget_trivial_usage
+//usage:       "[OPTIONS] HOST [LOCAL_FILE] REMOTE_FILE"
+//usage:#define ftpget_full_usage "\n\n"
+//usage:       "Download a file via FTP\n"
+//usage:       IF_FEATURE_FTPGETPUT_LONG_OPTIONS(
+//usage:     "\n       -c,--continue           Continue previous transfer"
+//usage:     "\n       -v,--verbose            Verbose"
+//usage:     "\n       -u,--username USER      Username"
+//usage:     "\n       -p,--password PASS      Password"
+//usage:     "\n       -P,--port NUM           Port"
+//usage:       )
+//usage:       IF_NOT_FEATURE_FTPGETPUT_LONG_OPTIONS(
+//usage:     "\n       -c      Continue previous transfer"
+//usage:     "\n       -v      Verbose"
+//usage:     "\n       -u USER Username"
+//usage:     "\n       -p PASS Password"
+//usage:     "\n       -P NUM  Port"
+//usage:       )
+//usage:
+//usage:#define ftpput_trivial_usage
+//usage:       "[OPTIONS] HOST [REMOTE_FILE] LOCAL_FILE"
+//usage:#define ftpput_full_usage "\n\n"
+//usage:       "Upload a file to a FTP server\n"
+//usage:       IF_FEATURE_FTPGETPUT_LONG_OPTIONS(
+//usage:     "\n       -v,--verbose            Verbose"
+//usage:     "\n       -u,--username USER      Username"
+//usage:     "\n       -p,--password PASS      Password"
+//usage:     "\n       -P,--port NUM           Port"
+//usage:       )
+//usage:       IF_NOT_FEATURE_FTPGETPUT_LONG_OPTIONS(
+//usage:     "\n       -v      Verbose"
+//usage:     "\n       -u USER Username"
+//usage:     "\n       -p PASS Password"
+//usage:     "\n       -P NUM  Port number"
+//usage:       )
+
 #include "libbb.h"
 
 struct globals {
@@ -22,7 +58,7 @@ struct globals {
        FILE *control_stream;
        int verbose_flag;
        int do_continue;
-       char buf[1]; /* actually [BUFSZ] */
+       char buf[4]; /* actually [BUFSZ] */
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
 enum { BUFSZ = COMMON_BUFSIZE - offsetof(struct globals, buf) };
@@ -67,7 +103,7 @@ static int ftpcmd(const char *s1, const char *s2)
        }
 
        do {
-               strcpy(buf, "EOF");
+               strcpy(buf, "EOF"); /* for ftp_die */
                if (fgets(buf, BUFSZ - 2, control_stream) == NULL) {
                        ftp_die(NULL);
                }
@@ -151,7 +187,7 @@ TODO2: need to stop ignoring IP address in PASV response.
        *buf_ptr = '\0';
        port_num += xatoul_range(buf_ptr + 1, 0, 255) * 256;
 
-       set_nport(lsa, htons(port_num));
+       set_nport(&lsa->u.sa, htons(port_num));
        return xconnect_stream(lsa);
 }
 
@@ -278,7 +314,6 @@ static const char ftpgetput_longopts[] ALIGN1 =
 int ftpgetput_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int ftpgetput_main(int argc UNUSED_PARAM, char **argv)
 {
-       unsigned opt;
        const char *port = "ftp";
        /* socket to ftp server */
 
@@ -307,7 +342,7 @@ int ftpgetput_main(int argc UNUSED_PARAM, char **argv)
        applet_long_options = ftpgetput_longopts;
 #endif
        opt_complementary = "-2:vv:cc"; /* must have 2 to 3 params; -v and -c count */
-       opt = getopt32(argv, "cvu:p:P:", &user, &password, &port,
+       getopt32(argv, "cvu:p:P:", &user, &password, &port,
                                        &verbose_flag, &do_continue);
        argv += optind;
 
index 121ad40..b3e3522 100644 (file)
@@ -7,8 +7,26 @@
  * Adjusted by Erik Andersen <andersen@codepoet.org> to remove
  * use of long options and GNU getopt.  Improved the usage info.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define hostname_trivial_usage
+//usage:       "[OPTIONS] [HOSTNAME | -F FILE]"
+//usage:#define hostname_full_usage "\n\n"
+//usage:       "Get or set hostname or DNS domain name\n"
+//usage:     "\n       -s      Short"
+//usage:     "\n       -i      Addresses for the hostname"
+//usage:     "\n       -d      DNS domain name"
+//usage:     "\n       -f      Fully qualified domain name"
+//usage:     "\n       -F FILE Use FILE's content as hostname"
+//usage:
+//usage:#define hostname_example_usage
+//usage:       "$ hostname\n"
+//usage:       "sage\n"
+//usage:
+//usage:#define dnsdomainname_trivial_usage NOUSAGE_STR
+//usage:#define dnsdomainname_full_usage ""
+
 #include "libbb.h"
 
 static void do_sethostname(char *s, int isfile)
@@ -88,7 +106,7 @@ int hostname_main(int argc UNUSED_PARAM, char **argv)
                OPT_i = 0x4,
                OPT_s = 0x8,
                OPT_F = 0x10,
-               OPT_dfis = 0xf,
+               OPT_dfi = 0x7,
        };
 
        unsigned opts;
@@ -116,7 +134,7 @@ int hostname_main(int argc UNUSED_PARAM, char **argv)
        if (applet_name[0] == 'd') /* dnsdomainname? */
                opts = OPT_d;
 
-       if (opts & OPT_dfis) {
+       if (opts & OPT_dfi) {
                /* Cases when we need full hostname (or its part) */
                struct hostent *hp;
                char *p;
@@ -135,12 +153,15 @@ int hostname_main(int argc UNUSED_PARAM, char **argv)
                        if (hp->h_length == sizeof(struct in_addr)) {
                                struct in_addr **h_addr_list = (struct in_addr **)hp->h_addr_list;
                                while (*h_addr_list) {
-                                       printf("%s ", inet_ntoa(**h_addr_list));
+                                       printf(h_addr_list[1] ? "%s " : "%s", inet_ntoa(**h_addr_list));
                                        h_addr_list++;
                                }
                                bb_putchar('\n');
                        }
                }
+       } else if (opts & OPT_s) {
+               strchrnul(buf, '.')[0] = '\0';
+               puts(buf);
        } else if (opts & OPT_F) {
                /* Set the hostname */
                do_sethostname(hostname_str, 1);
index 8ad7e88..621d9cd 100644 (file)
@@ -5,32 +5,33 @@
  * Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org>
  * Copyright (C) 2003-2006 Vladimir Oleynik <dzo@simtreas.ru>
  *
- * simplify patch stolen from libbb without using strdup
- *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  *****************************************************************************
  *
  * Typical usage:
- *   for non root user
- * httpd -p 8080 -h $HOME/public_html
- *   or for daemon start from rc script with uid=0:
- * httpd -u www
- * This is equivalent if www user have uid=80 to
- * httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication"
- *
+ * For non root user:
+ *      httpd -p 8080 -h $HOME/public_html
+ * For daemon start from rc script with uid=0:
+ *      httpd -u www
+ * which is equivalent to (assuming user www has uid 80):
+ *      httpd -p 80 -u 80 -h $PWD -c /etc/httpd.conf -r "Web Server Authentication"
  *
- * When an url starts by "/cgi-bin/" it is assumed to be a cgi script.  The
- * server changes directory to the location of the script and executes it
+ * When an url starts with "/cgi-bin/" it is assumed to be a cgi script.
+ * The server changes directory to the location of the script and executes it
  * after setting QUERY_STRING and other environment variables.
  *
+ * If directory URL is given, no index.html is found and CGI support is enabled,
+ * cgi-bin/index.cgi will be run. Directory to list is ../$QUERY_STRING.
+ * See httpd_indexcgi.c for an example GCI code.
+ *
  * Doc:
  * "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html
  *
  * The applet can also be invoked as an url arg decoder and html text encoder
  * as follows:
- *  foo=`httpd -d $foo`           # decode "Hello%20World" as "Hello World"
- *  bar=`httpd -e "<Hello World>"`  # encode as "&#60Hello&#32World&#62"
+ *      foo=`httpd -d $foo`             # decode "Hello%20World" as "Hello World"
+ *      bar=`httpd -e "<Hello World>"`  # encode as "&#60Hello&#32World&#62"
  * Note that url encoding for arguments is not the same as html encoding for
  * presentation.  -d decodes an url-encoded argument while -e encodes in html
  * for page display.
@@ -53,6 +54,8 @@
  * /cgi-bin:foo:bar  # Require user foo, pwd bar on urls starting with /cgi-bin/
  * /adm:admin:setup  # Require user admin, pwd setup on urls starting with /adm/
  * /adm:toor:PaSsWd  # or user toor, pwd PaSsWd on urls starting with /adm/
+ * /adm:root:*       # or user root, pwd from /etc/passwd on urls starting with /adm/
+ * /wiki:*:*         # or any user from /etc/passwd with according pwd on urls starting with /wiki/
  * .au:audio/basic   # additional mime type for audio.au files
  * *.php:/path/php   # run xxx.php through an interpreter
  *
@@ -74,7 +77,7 @@
  *     D:2.3.4.        # deny from 2.3.4.0 - 2.3.4.255
  *     A:*             # (optional line added for clarity)
  *
- * If a sub directory contains a config file it is parsed and merged with
+ * If a sub directory contains config file, it is parsed and merged with
  * any existing settings as if it was appended to the original configuration.
  *
  * subdir paths are relative to the containing subdir and thus cannot
  */
  /* TODO: use TCP_CORK, parse_config() */
 
+//usage:#define httpd_trivial_usage
+//usage:       "[-ifv[v]]"
+//usage:       " [-c CONFFILE]"
+//usage:       " [-p [IP:]PORT]"
+//usage:       IF_FEATURE_HTTPD_SETUID(" [-u USER[:GRP]]")
+//usage:       IF_FEATURE_HTTPD_BASIC_AUTH(" [-r REALM]")
+//usage:       " [-h HOME]\n"
+//usage:       "or httpd -d/-e" IF_FEATURE_HTTPD_AUTH_MD5("/-m") " STRING"
+//usage:#define httpd_full_usage "\n\n"
+//usage:       "Listen for incoming HTTP requests\n"
+//usage:     "\n       -i              Inetd mode"
+//usage:     "\n       -f              Don't daemonize"
+//usage:     "\n       -v[v]           Verbose"
+//usage:     "\n       -p [IP:]PORT    Bind to IP:PORT (default *:80)"
+//usage:       IF_FEATURE_HTTPD_SETUID(
+//usage:     "\n       -u USER[:GRP]   Set uid/gid after binding to port")
+//usage:       IF_FEATURE_HTTPD_BASIC_AUTH(
+//usage:     "\n       -r REALM        Authentication Realm for Basic Authentication")
+//usage:     "\n       -h HOME         Home directory (default .)"
+//usage:     "\n       -c FILE         Configuration file (default {/etc,HOME}/httpd.conf)"
+//usage:       IF_FEATURE_HTTPD_AUTH_MD5(
+//usage:     "\n       -m STRING       MD5 crypt STRING")
+//usage:     "\n       -e STRING       HTML encode STRING"
+//usage:     "\n       -d STRING       URL decode STRING"
+
 #include "libbb.h"
+#if ENABLE_PAM
+/* PAM may include <locale.h>. We may need to undefine bbox's stub define: */
+# undef setlocale
+/* For some obscure reason, PAM is not in pam/xxx, but in security/xxx.
+ * Apparently they like to confuse people. */
+# include <security/pam_appl.h>
+# include <security/pam_misc.h>
+#endif
 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE
 # include <sys/sendfile.h>
 #endif
-
-#define DEBUG 0
-
-#define IOBUF_SIZE 8192    /* IO buffer */
-
 /* amount of buffering in a pipe */
 #ifndef PIPE_BUF
 # define PIPE_BUF 4096
 #endif
+
+#define DEBUG 0
+
+#define IOBUF_SIZE 8192
 #if PIPE_BUF >= IOBUF_SIZE
 # error "PIPE_BUF >= IOBUF_SIZE"
 #endif
 static const char DEFAULT_PATH_HTTPD_CONF[] ALIGN1 = "/etc";
 static const char HTTPD_CONF[] ALIGN1 = "httpd.conf";
 static const char HTTP_200[] ALIGN1 = "HTTP/1.0 200 OK\r\n";
+static const char index_html[] ALIGN1 = "index.html";
 
 typedef struct has_next_ptr {
        struct has_next_ptr *next;
@@ -170,7 +206,6 @@ enum {
        HTTP_PAYMENT_REQUIRED = 402,
        HTTP_BAD_GATEWAY = 502,
        HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
-       HTTP_RESPONSE_SETSIZE = 0xffffffff
 #endif
 };
 
@@ -231,14 +266,11 @@ static const struct {
 #endif
 };
 
-static const char index_html[] ALIGN1 = "index.html";
-
-
 struct globals {
        int verbose;            /* must be int (used by getopt32) */
        smallint flg_deny_all;
 
-       unsigned rmt_ip;        /* used for IP-based allow/deny rules */
+       unsigned rmt_ip;        /* used for IP-based allow/deny rules */
        time_t last_mod;
        char *rmt_ip_str;       /* for $REMOTE_ADDR and $REMOTE_PORT */
        const char *bind_addr_or_port;
@@ -274,7 +306,7 @@ struct globals {
 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
        Htaccess *script_i;     /* config script interpreters */
 #endif
-       char *iobuf;            /* [IOBUF_SIZE] */
+       char *iobuf;            /* [IOBUF_SIZE] */
 #define hdr_buf bb_common_bufsiz1
        char *hdr_ptr;
        int hdr_cnt;
@@ -284,6 +316,10 @@ struct globals {
 #if ENABLE_FEATURE_HTTPD_PROXY
        Htaccess_Proxy *proxy;
 #endif
+#if ENABLE_FEATURE_HTTPD_GZIP
+       /* client can handle gzip / we are going to send gzip */
+       smallint content_gzip;
+#endif
 };
 #define G (*ptr_to_globals)
 #define verbose           (G.verbose          )
@@ -312,7 +348,7 @@ struct globals {
 #define range_len         (G.range_len        )
 #else
 enum {
-       range_start = 0,
+       range_start = -1,
        range_end = MAXINT(off_t) - 1,
        range_len = MAXINT(off_t),
 };
@@ -326,9 +362,15 @@ enum {
 #define hdr_cnt           (G.hdr_cnt          )
 #define http_error_page   (G.http_error_page  )
 #define proxy             (G.proxy            )
+#if ENABLE_FEATURE_HTTPD_GZIP
+# define content_gzip     (G.content_gzip     )
+#else
+# define content_gzip     0
+#endif
 #define INIT_G() do { \
        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
        IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
+       IF_FEATURE_HTTPD_RANGES(range_start = -1;) \
        bind_addr_or_port = "80"; \
        index_page = index_html; \
        file_size = -1; \
@@ -754,9 +796,9 @@ static void parse_conf(const char *path, int flag)
                /* the line is not recognized */
  config_error:
                bb_error_msg("config error '%s' in '%s'", buf, filename);
-        } /* while (fgets) */
+       } /* while (fgets) */
 
-        fclose(f);
+       fclose(f);
 }
 
 #if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
@@ -777,7 +819,7 @@ static char *encodeString(const char *string)
        char *p = out;
        char ch;
 
-       while ((ch = *string++)) {
+       while ((ch = *string++) != '\0') {
                /* very simple check for what to encode */
                if (isalnum(ch))
                        *p++ = ch;
@@ -787,79 +829,7 @@ static char *encodeString(const char *string)
        *p = '\0';
        return out;
 }
-#endif          /* FEATURE_HTTPD_ENCODE_URL_STR */
-
-/*
- * Given a URL encoded string, convert it to plain ascii.
- * Since decoding always makes strings smaller, the decode is done in-place.
- * Thus, callers should xstrdup() the argument if they do not want the
- * argument modified.  The return is the original pointer, allowing this
- * function to be easily used as arguments to other functions.
- *
- * string    The first string to decode.
- * option_d  1 if called for httpd -d
- *
- * Returns a pointer to the decoded string (same as input).
- */
-static unsigned hex_to_bin(unsigned char c)
-{
-       unsigned v;
-
-       v = c - '0';
-       if (v <= 9)
-               return v;
-       /* c | 0x20: letters to lower case, non-letters
-        * to (potentially different) non-letters */
-       v = (unsigned)(c | 0x20) - 'a';
-       if (v <= 5)
-               return v + 10;
-       return ~0;
-}
-/* For testing:
-void t(char c) { printf("'%c'(%u) %u\n", c, c, hex_to_bin(c)); }
-int main() { t(0x10); t(0x20); t('0'); t('9'); t('A'); t('F'); t('a'); t('f');
-t('0'-1); t('9'+1); t('A'-1); t('F'+1); t('a'-1); t('f'+1); return 0; }
-*/
-static char *decodeString(char *orig, int option_d)
-{
-       /* note that decoded string is always shorter than original */
-       char *string = orig;
-       char *ptr = string;
-       char c;
-
-       while ((c = *ptr++) != '\0') {
-               unsigned v;
-
-               if (option_d && c == '+') {
-                       *string++ = ' ';
-                       continue;
-               }
-               if (c != '%') {
-                       *string++ = c;
-                       continue;
-               }
-               v = hex_to_bin(ptr[0]);
-               if (v > 15) {
- bad_hex:
-                       if (!option_d)
-                               return NULL;
-                       *string++ = '%';
-                       continue;
-               }
-               v = (v * 16) | hex_to_bin(ptr[1]);
-               if (v > 255)
-                       goto bad_hex;
-               if (!option_d && (v == '/' || v == '\0')) {
-                       /* caller takes it as indication of invalid
-                        * (dangerous wrt exploits) chars */
-                       return orig + 1;
-               }
-               *string++ = v;
-               ptr += 2;
-       }
-       *string = '\0';
-       return orig;
-}
+#endif
 
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
 /*
@@ -1034,10 +1004,14 @@ static void send_headers(int responseNum)
 #endif
                        "Last-Modified: %s\r\n%s %"OFF_FMT"u\r\n",
                                tmp_str,
-                               "Content-length:",
+                               content_gzip ? "Transfer-length:" : "Content-length:",
                                file_size
                );
        }
+
+       if (content_gzip)
+               len += sprintf(iobuf + len, "Content-Encoding: gzip\r\n");
+
        iobuf[len++] = '\r';
        iobuf[len++] = '\n';
        if (infoString) {
@@ -1059,6 +1033,7 @@ static void send_headers(int responseNum)
 static void send_headers_and_exit(int responseNum) NORETURN;
 static void send_headers_and_exit(int responseNum)
 {
+       IF_FEATURE_HTTPD_GZIP(content_gzip = 0;)
        send_headers(responseNum);
        log_and_exit();
 }
@@ -1129,18 +1104,31 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
 
        /* NB: breaking out of this loop jumps to log_and_exit() */
        out_cnt = 0;
+       pfd[FROM_CGI].fd = fromCgi_rd;
+       pfd[FROM_CGI].events = POLLIN;
+       pfd[TO_CGI].fd = toCgi_wr;
        while (1) {
-               memset(pfd, 0, sizeof(pfd));
-
-               pfd[FROM_CGI].fd = fromCgi_rd;
-               pfd[FROM_CGI].events = POLLIN;
-
-               if (toCgi_wr) {
-                       pfd[TO_CGI].fd = toCgi_wr;
-                       if (hdr_cnt > 0) {
-                               pfd[TO_CGI].events = POLLOUT;
-                       } else if (post_len > 0) {
-                               pfd[0].events = POLLIN;
+               /* Note: even pfd[0].events == 0 won't prevent
+                * revents == POLLHUP|POLLERR reports from closed stdin.
+                * Setting fd to -1 works: */
+               pfd[0].fd = -1;
+               pfd[0].events = POLLIN;
+               pfd[0].revents = 0; /* probably not needed, paranoia */
+
+               /* We always poll this fd, thus kernel always sets revents: */
+               /*pfd[FROM_CGI].events = POLLIN; - moved out of loop */
+               /*pfd[FROM_CGI].revents = 0; - not needed */
+
+               /* gcc-4.8.0 still doesnt fill two shorts with one insn :( */
+               /* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47059 */
+               /* hopefully one day it will... */
+               pfd[TO_CGI].events = POLLOUT;
+               pfd[TO_CGI].revents = 0; /* needed! */
+
+               if (toCgi_wr && hdr_cnt <= 0) {
+                       if (post_len > 0) {
+                               /* Expect more POST data from network */
+                               pfd[0].fd = 0;
                        } else {
                                /* post_len <= 0 && hdr_cnt <= 0:
                                 * no more POST data to CGI,
@@ -1152,7 +1140,7 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
                }
 
                /* Now wait on the set of sockets */
-               count = safe_poll(pfd, toCgi_wr ? TO_CGI+1 : FROM_CGI+1, -1);
+               count = safe_poll(pfd, hdr_cnt > 0 ? TO_CGI+1 : FROM_CGI+1, -1);
                if (count <= 0) {
 #if 0
                        if (safe_waitpid(pid, &status, WNOHANG) <= 0) {
@@ -1169,7 +1157,7 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
                }
 
                if (pfd[TO_CGI].revents) {
-                       /* hdr_cnt > 0 here due to the way pfd[TO_CGI].events set */
+                       /* hdr_cnt > 0 here due to the way poll() called */
                        /* Have data from peer and can write to CGI */
                        count = safe_write(toCgi_wr, hdr_ptr, hdr_cnt);
                        /* Doesn't happen, we dont use nonblocking IO here
@@ -1291,18 +1279,21 @@ static void setenv1(const char *name, const char *value)
  *
  * Parameters:
  * const char *url              The requested URL (with leading /).
+ * const char *orig_uri         The original URI before rewriting (if any)
  * int post_len                 Length of the POST body.
  * const char *cookie           For set HTTP_COOKIE.
  * const char *content_type     For set CONTENT_TYPE.
  */
 static void send_cgi_and_exit(
                const char *url,
+               const char *orig_uri,
                const char *request,
                int post_len,
                const char *cookie,
                const char *content_type) NORETURN;
 static void send_cgi_and_exit(
                const char *url,
+               const char *orig_uri,
                const char *request,
                int post_len,
                const char *cookie,
@@ -1310,7 +1301,7 @@ static void send_cgi_and_exit(
 {
        struct fd_pair fromCgi;  /* CGI -> httpd pipe */
        struct fd_pair toCgi;    /* httpd -> CGI pipe */
-       char *script;
+       char *script, *last_slash;
        int pid;
 
        /* Make a copy. NB: caller guarantees:
@@ -1324,22 +1315,25 @@ static void send_cgi_and_exit(
         */
 
        /* Check for [dirs/]script.cgi/PATH_INFO */
-       script = (char*)url;
+       last_slash = script = (char*)url;
        while ((script = strchr(script + 1, '/')) != NULL) {
+               int dir;
                *script = '\0';
-               if (!is_directory(url + 1, 1, NULL)) {
+               dir = is_directory(url + 1, /*followlinks:*/ 1);
+               *script = '/';
+               if (!dir) {
                        /* not directory, found script.cgi/PATH_INFO */
-                       *script = '/';
                        break;
                }
-               *script = '/'; /* is directory, find next '/' */
+               /* is directory, find next '/' */
+               last_slash = script;
        }
        setenv1("PATH_INFO", script);   /* set to /PATH_INFO or "" */
        setenv1("REQUEST_METHOD", request);
        if (g_query) {
-               putenv(xasprintf("%s=%s?%s", "REQUEST_URI", url, g_query));
+               putenv(xasprintf("%s=%s?%s", "REQUEST_URI", orig_uri, g_query));
        } else {
-               setenv1("REQUEST_URI", url);
+               setenv1("REQUEST_URI", orig_uri);
        }
        if (script != NULL)
                *script = '\0';         /* cut off /PATH_INFO */
@@ -1413,7 +1407,7 @@ static void send_cgi_and_exit(
                log_and_exit();
        }
 
-       if (!pid) {
+       if (pid == 0) {
                /* Child process */
                char *argv[3];
 
@@ -1429,11 +1423,11 @@ static void send_cgi_and_exit(
                /* dup2(1, 2); */
 
                /* Chdiring to script's dir */
-               script = strrchr(url, '/');
+               script = last_slash;
                if (script != url) { /* paranoia */
                        *script = '\0';
                        if (chdir(url + 1) != 0) {
-                               bb_perror_msg("chdir(%s)", url + 1);
+                               bb_perror_msg("can't change directory to '%s'", url + 1);
                                goto error_execing_cgi;
                        }
                        // not needed: *script = '/';
@@ -1507,7 +1501,23 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
        int fd;
        ssize_t count;
 
-       fd = open(url, O_RDONLY);
+       if (content_gzip) {
+               /* does <url>.gz exist? Then use it instead */
+               char *gzurl = xasprintf("%s.gz", url);
+               fd = open(gzurl, O_RDONLY);
+               free(gzurl);
+               if (fd != -1) {
+                       struct stat sb;
+                       fstat(fd, &sb);
+                       file_size = sb.st_size;
+                       last_mod = sb.st_mtime;
+               } else {
+                       IF_FEATURE_HTTPD_GZIP(content_gzip = 0;)
+                       fd = open(url, O_RDONLY);
+               }
+       } else {
+               fd = open(url, O_RDONLY);
+       }
        if (fd < 0) {
                if (DEBUG)
                        bb_perror_msg("can't open '%s'", url);
@@ -1590,18 +1600,21 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
                        url, found_mime_type);
 
 #if ENABLE_FEATURE_HTTPD_RANGES
-       if (what == SEND_BODY)
-               range_start = 0; /* err pages and ranges don't mix */
+       if (what == SEND_BODY /* err pages and ranges don't mix */
+        || content_gzip /* we are sending compressed page: can't do ranges */  ///why?
+       ) {
+               range_start = -1;
+       }
        range_len = MAXINT(off_t);
-       if (range_start) {
-               if (!range_end) {
+       if (range_start >= 0) {
+               if (!range_end || range_end > file_size - 1) {
                        range_end = file_size - 1;
                }
                if (range_end < range_start
                 || lseek(fd, range_start, SEEK_SET) != range_start
                ) {
                        lseek(fd, 0, SEEK_SET);
-                       range_start = 0;
+                       range_start = -1;
                } else {
                        range_len = range_end - range_start + 1;
                        send_headers(HTTP_PARTIAL_CONTENT);
@@ -1624,7 +1637,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
                                        break; /* fall back to read/write loop */
                                goto fin;
                        }
-                       IF_FEATURE_HTTPD_RANGES(range_len -= sz;)
+                       IF_FEATURE_HTTPD_RANGES(range_len -= count;)
                        if (count == 0 || range_len == 0)
                                log_and_exit();
                }
@@ -1675,6 +1688,56 @@ static int checkPermIP(void)
 }
 
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+
+# if ENABLE_PAM
+struct pam_userinfo {
+       const char *name;
+       const char *pw;
+};
+
+static int pam_talker(int num_msg,
+               const struct pam_message **msg,
+               struct pam_response **resp,
+               void *appdata_ptr)
+{
+       int i;
+       struct pam_userinfo *userinfo = (struct pam_userinfo *) appdata_ptr;
+       struct pam_response *response;
+
+       if (!resp || !msg || !userinfo)
+               return PAM_CONV_ERR;
+
+       /* allocate memory to store response */
+       response = xzalloc(num_msg * sizeof(*response));
+
+       /* copy values */
+       for (i = 0; i < num_msg; i++) {
+               const char *s;
+
+               switch (msg[i]->msg_style) {
+               case PAM_PROMPT_ECHO_ON:
+                       s = userinfo->name;
+                       break;
+               case PAM_PROMPT_ECHO_OFF:
+                       s = userinfo->pw;
+                       break;
+               case PAM_ERROR_MSG:
+               case PAM_TEXT_INFO:
+                       s = "";
+                       break;
+               default:
+                       free(response);
+                       return PAM_CONV_ERR;
+               }
+               response[i].resp = xstrdup(s);
+               if (PAM_SUCCESS != 0)
+                       response[i].resp_retcode = PAM_SUCCESS;
+       }
+       *resp = response;
+       return PAM_SUCCESS;
+}
+# endif
+
 /*
  * Config file entries are of the form "/<path>:<user>:<passwd>".
  * If config file has no prefix match for path, access is allowed.
@@ -1684,7 +1747,7 @@ static int checkPermIP(void)
  *
  * Returns 1 if user_and_passwd is OK.
  */
-static int check_user_passwd(const char *path, const char *user_and_passwd)
+static int check_user_passwd(const char *path, char *user_and_passwd)
 {
        Htaccess *cur;
        const char *prev = NULL;
@@ -1692,6 +1755,7 @@ static int check_user_passwd(const char *path, const char *user_and_passwd)
        for (cur = g_auth; cur; cur = cur->next) {
                const char *dir_prefix;
                size_t len;
+               int r;
 
                dir_prefix = cur->before_colon;
 
@@ -1707,7 +1771,8 @@ static int check_user_passwd(const char *path, const char *user_and_passwd)
                len = strlen(dir_prefix);
                if (len != 1 /* dir_prefix "/" matches all, don't need to check */
                 && (strncmp(dir_prefix, path, len) != 0
-                   || (path[len] != '/' && path[len] != '\0'))
+                   || (path[len] != '/' && path[len] != '\0')
+                   )
                ) {
                        continue;
                }
@@ -1716,38 +1781,105 @@ static int check_user_passwd(const char *path, const char *user_and_passwd)
                prev = dir_prefix;
 
                if (ENABLE_FEATURE_HTTPD_AUTH_MD5) {
-                       char *md5_passwd;
-
-                       md5_passwd = strchr(cur->after_colon, ':');
-                       if (md5_passwd && md5_passwd[1] == '$' && md5_passwd[2] == '1'
-                        && md5_passwd[3] == '$' && md5_passwd[4]
+                       char *colon_after_user;
+                       const char *passwd;
+# if ENABLE_FEATURE_SHADOWPASSWDS && !ENABLE_PAM
+                       char sp_buf[256];
+# endif
+
+                       colon_after_user = strchr(user_and_passwd, ':');
+                       if (!colon_after_user)
+                               goto bad_input;
+
+                       /* compare "user:" */
+                       if (cur->after_colon[0] != '*'
+                        && strncmp(cur->after_colon, user_and_passwd,
+                                       colon_after_user - user_and_passwd + 1) != 0
                        ) {
-                               char *encrypted;
-                               int r, user_len_p1;
-
-                               md5_passwd++;
-                               user_len_p1 = md5_passwd - cur->after_colon;
-                               /* comparing "user:" */
-                               if (strncmp(cur->after_colon, user_and_passwd, user_len_p1) != 0) {
+                               continue;
+                       }
+                       /* this cfg entry is '*' or matches username from peer */
+
+                       passwd = strchr(cur->after_colon, ':');
+                       if (!passwd)
+                               goto bad_input;
+                       passwd++;
+                       if (passwd[0] == '*') {
+# if ENABLE_PAM
+                               struct pam_userinfo userinfo;
+                               struct pam_conv conv_info = { &pam_talker, (void *) &userinfo };
+                               pam_handle_t *pamh;
+
+                               *colon_after_user = '\0';
+                               userinfo.name = user_and_passwd;
+                               userinfo.pw = colon_after_user + 1;
+                               r = pam_start("httpd", user_and_passwd, &conv_info, &pamh) != PAM_SUCCESS;
+                               if (r == 0) {
+                                       r = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS
+                                        || pam_acct_mgmt(pamh, PAM_DISALLOW_NULL_AUTHTOK)    != PAM_SUCCESS
+                                       ;
+                                       pam_end(pamh, PAM_SUCCESS);
+                               }
+                               *colon_after_user = ':';
+                               goto end_check_passwd;
+# else
+#  if ENABLE_FEATURE_SHADOWPASSWDS
+                               /* Using _r function to avoid pulling in static buffers */
+                               struct spwd spw;
+#  endif
+                               struct passwd *pw;
+
+                               *colon_after_user = '\0';
+                               pw = getpwnam(user_and_passwd);
+                               *colon_after_user = ':';
+                               if (!pw || !pw->pw_passwd)
                                        continue;
+                               passwd = pw->pw_passwd;
+#  if ENABLE_FEATURE_SHADOWPASSWDS
+                               if ((passwd[0] == 'x' || passwd[0] == '*') && !passwd[1]) {
+                                       /* getspnam_r may return 0 yet set result to NULL.
+                                        * At least glibc 2.4 does this. Be extra paranoid here. */
+                                       struct spwd *result = NULL;
+                                       r = getspnam_r(pw->pw_name, &spw, sp_buf, sizeof(sp_buf), &result);
+                                       if (r == 0 && result)
+                                               passwd = result->sp_pwdp;
                                }
+#  endif
+                               /* In this case, passwd is ALWAYS encrypted:
+                                * it came from /etc/passwd or /etc/shadow!
+                                */
+                               goto check_encrypted;
+# endif /* ENABLE_PAM */
+                       }
+                       /* Else: passwd is from httpd.conf, it is either plaintext or encrypted */
 
+                       if (passwd[0] == '$' && isdigit(passwd[1])) {
+                               char *encrypted;
+# if !ENABLE_PAM
+ check_encrypted:
+# endif
+                               /* encrypt pwd from peer and check match with local one */
                                encrypted = pw_encrypt(
-                                       user_and_passwd + user_len_p1 /* cleartext pwd from user */,
-                                       md5_passwd /*salt */, 1 /* cleanup */);
-                               r = strcmp(encrypted, md5_passwd);
+                                       /* pwd (from peer): */  colon_after_user + 1,
+                                       /* salt: */ passwd,
+                                       /* cleanup: */ 0
+                               );
+                               r = strcmp(encrypted, passwd);
                                free(encrypted);
-                               if (r == 0)
-                                       goto set_remoteuser_var; /* Ok */
-                               continue;
+                       } else {
+                               /* local passwd is from httpd.conf and it's plaintext */
+                               r = strcmp(colon_after_user + 1, passwd);
                        }
+                       goto end_check_passwd;
                }
-
+ bad_input:
                /* Comparing plaintext "user:pass" in one go */
-               if (strcmp(cur->after_colon, user_and_passwd) == 0) {
- set_remoteuser_var:
+               r = strcmp(cur->after_colon, user_and_passwd);
+ end_check_passwd:
+               if (r == 0) {
                        remoteuser = xstrndup(user_and_passwd,
-                                       strchrnul(user_and_passwd, ':') - user_and_passwd);
+                               strchrnul(user_and_passwd, ':') - user_and_passwd
+                       );
                        return 1; /* Ok */
                }
        } /* for */
@@ -1845,7 +1977,9 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
                send_headers_and_exit(HTTP_BAD_REQUEST);
 
        /* Determine type of request (GET/POST) */
-       urlp = strpbrk(iobuf, " \t");
+       // rfc2616: method and URI is separated by exactly one space
+       //urlp = strpbrk(iobuf, " \t"); - no, tab isn't allowed
+       urlp = strchr(iobuf, ' ');
        if (urlp == NULL)
                send_headers_and_exit(HTTP_BAD_REQUEST);
        *urlp++ = '\0';
@@ -1863,7 +1997,8 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
        if (strcasecmp(iobuf, request_GET) != 0)
                send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
 #endif
-       urlp = skip_whitespace(urlp);
+       // rfc2616: method and URI is separated by exactly one space
+       //urlp = skip_whitespace(urlp); - should not be necessary
        if (urlp[0] != '/')
                send_headers_and_exit(HTTP_BAD_REQUEST);
 
@@ -1886,7 +2021,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
        /* NB: urlcopy ptr is never changed after this */
 
        /* Extract url args if present */
-       g_query = NULL;
+       /* g_query = NULL; - already is */
        tptr = strchr(urlcopy, '?');
        if (tptr) {
                *tptr++ = '\0';
@@ -1894,7 +2029,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
        }
 
        /* Decode URL escape sequences */
-       tptr = decodeString(urlcopy, 0);
+       tptr = percent_decode_in_place(urlcopy, /*strict:*/ 1);
        if (tptr == NULL)
                send_headers_and_exit(HTTP_BAD_REQUEST);
        if (tptr == urlcopy + 1) {
@@ -1906,34 +2041,40 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
        /* Algorithm stolen from libbb bb_simplify_path(),
         * but don't strdup, retain trailing slash, protect root */
        urlp = tptr = urlcopy;
-       do {
+       for (;;) {
                if (*urlp == '/') {
                        /* skip duplicate (or initial) slash */
                        if (*tptr == '/') {
-                               continue;
+                               goto next_char;
                        }
                        if (*tptr == '.') {
-                               /* skip extra "/./" */
-                               if (tptr[1] == '/' || !tptr[1]) {
-                                       continue;
-                               }
-                               /* "..": be careful */
-                               if (tptr[1] == '.' && (tptr[2] == '/' || !tptr[2])) {
-                                       ++tptr;
-                                       if (urlp == urlcopy) /* protect root */
+                               if (tptr[1] == '.' && (tptr[2] == '/' || tptr[2] == '\0')) {
+                                       /* "..": be careful */
+                                       /* protect root */
+                                       if (urlp == urlcopy)
                                                send_headers_and_exit(HTTP_BAD_REQUEST);
-                                       while (*--urlp != '/') /* omit previous dir */;
+                                       /* omit previous dir */
+                                       while (*--urlp != '/')
                                                continue;
+                                       /* skip to "./" or ".<NUL>" */
+                                       tptr++;
+                               }
+                               if (tptr[1] == '/' || tptr[1] == '\0') {
+                                       /* skip extra "/./" */
+                                       goto next_char;
                                }
                        }
                }
                *++urlp = *tptr;
-       } while (*++tptr);
-       *++urlp = '\0';       /* terminate after last character */
+               if (*urlp == '\0')
+                       break;
+ next_char:
+               tptr++;
+       }
 
        /* If URL is a directory, add '/' */
        if (urlp[-1] != '/') {
-               if (is_directory(urlcopy + 1, 1, NULL)) {
+               if (is_directory(urlcopy + 1, /*followlinks:*/ 1)) {
                        found_moved_temporarily = urlcopy;
                }
        }
@@ -1947,7 +2088,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
        while (ip_allowed && (tptr = strchr(tptr + 1, '/')) != NULL) {
                /* have path1/path2 */
                *tptr = '\0';
-               if (is_directory(urlcopy + 1, 1, NULL)) {
+               if (is_directory(urlcopy + 1, /*followlinks:*/ 1)) {
                        /* may have subdir config */
                        parse_conf(urlcopy + 1, SUBDIR_PARSE);
                        ip_allowed = checkPermIP();
@@ -1964,7 +2105,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
        if (http_major_version >= '0') {
                /* Request was with "... HTTP/nXXX", and n >= 0 */
 
-               /* Read until blank line for HTTP version specified, else parse immediate */
+               /* Read until blank line */
                while (1) {
                        if (!get_line())
                                break; /* EOF or error or empty line */
@@ -1991,9 +2132,9 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
                        if ((STRNCASECMP(iobuf, "Content-length:") == 0)) {
                                /* extra read only for POST */
                                if (prequest != request_GET
-#if ENABLE_FEATURE_HTTPD_CGI
+# if ENABLE_FEATURE_HTTPD_CGI
                                 && prequest != request_HEAD
-#endif
+# endif
                                ) {
                                        tptr = skip_whitespace(iobuf + sizeof("Content-length:") - 1);
                                        if (!tptr[0])
@@ -2046,15 +2187,32 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
                                        s += sizeof("bytes=")-1;
                                        range_start = BB_STRTOOFF(s, &s, 10);
                                        if (s[0] != '-' || range_start < 0) {
-                                               range_start = 0;
+                                               range_start = -1;
                                        } else if (s[1]) {
                                                range_end = BB_STRTOOFF(s+1, NULL, 10);
                                                if (errno || range_end < range_start)
-                                                       range_start = 0;
+                                                       range_start = -1;
                                        }
                                }
                        }
 #endif
+#if ENABLE_FEATURE_HTTPD_GZIP
+                       if (STRNCASECMP(iobuf, "Accept-Encoding:") == 0) {
+                               /* Note: we do not support "gzip;q=0"
+                                * method of _disabling_ gzip
+                                * delivery. No one uses that, though */
+                               const char *s = strstr(iobuf, "gzip");
+                               if (s) {
+                                       // want more thorough checks?
+                                       //if (s[-1] == ' '
+                                       // || s[-1] == ','
+                                       // || s[-1] == ':'
+                                       //) {
+                                               content_gzip = 1;
+                                       //}
+                               }
+                       }
+#endif
                } /* while extra header reading */
        }
 
@@ -2067,10 +2225,10 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
        }
 
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
-       /* Case: no "Authorization:" was seen, but page does require passwd.
+       /* Case: no "Authorization:" was seen, but page might require passwd.
         * Check that with dummy user:pass */
        if (authorized < 0)
-               authorized = check_user_passwd(urlcopy, ":");
+               authorized = check_user_passwd(urlcopy, (char *) "");
        if (!authorized)
                send_headers_and_exit(HTTP_UNAUTHORIZED);
 #endif
@@ -2116,12 +2274,20 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
                        /* protect listing "cgi-bin/" */
                        send_headers_and_exit(HTTP_FORBIDDEN);
                }
-               send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type);
+               send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type);
        }
 #endif
 
-       if (urlp[-1] == '/')
+       if (urlp[-1] == '/') {
+               /* When index_page string is appended to <dir>/ URL, it overwrites
+                * the query string. If we fall back to call /cgi-bin/index.cgi,
+                * query string would be lost and not available to the CGI.
+                * Work around it by making a deep copy.
+                */
+               if (ENABLE_FEATURE_HTTPD_CGI)
+                       g_query = xstrdup(g_query); /* ok for NULL too */
                strcpy(urlp, index_page);
+       }
        if (stat(tptr, &sb) == 0) {
 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
                char *suffix = strrchr(tptr, '.');
@@ -2129,7 +2295,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
                        Htaccess *cur;
                        for (cur = script_i; cur; cur = cur->next) {
                                if (strcmp(cur->before_colon + 1, suffix) == 0) {
-                                       send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type);
+                                       send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type);
                                }
                        }
                }
@@ -2142,9 +2308,8 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
                /* It's a dir URL and there is no index.html
                 * Try cgi-bin/index.cgi */
                if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
-                       urlp[0] = '\0';
-                       g_query = urlcopy;
-                       send_cgi_and_exit("/cgi-bin/index.cgi", prequest, length, cookie, content_type);
+                       urlp[0] = '\0'; /* remove index_page */
+                       send_cgi_and_exit("/cgi-bin/index.cgi", urlcopy, prequest, length, cookie, content_type);
                }
        }
        /* else fall through to send_file, it errors out if open fails: */
@@ -2183,9 +2348,9 @@ static void mini_httpd(int server_socket)
                /* Wait for connections... */
                fromAddr.len = LSA_SIZEOF_SA;
                n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len);
-
                if (n < 0)
                        continue;
+
                /* set the KEEPALIVE option to cull dead connections */
                setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
 
@@ -2226,9 +2391,9 @@ static void mini_httpd_nommu(int server_socket, int argc, char **argv)
                /* Wait for connections... */
                fromAddr.len = LSA_SIZEOF_SA;
                n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len);
-
                if (n < 0)
                        continue;
+
                /* set the KEEPALIVE option to cull dead connections */
                setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
 
@@ -2243,6 +2408,7 @@ static void mini_httpd_nommu(int server_socket, int argc, char **argv)
                        /* Run a copy of ourself in inetd mode */
                        re_exec(argv_copy);
                }
+               argv_copy[0][0] &= 0x7f;
                /* parent, or vfork failed */
                close(n);
        } /* while (1) */
@@ -2336,7 +2502,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
                        , &verbose
                );
        if (opt & OPT_DECODE_URL) {
-               fputs(decodeString(url_for_decode, 1), stdout);
+               fputs(percent_decode_in_place(url_for_decode, /*strict:*/ 0), stdout);
                return 0;
        }
 #if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
@@ -2351,8 +2517,8 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
                salt[0] = '$';
                salt[1] = '1';
                salt[2] = '$';
-               crypt_make_salt(salt + 3, 4, 0);
-               puts(pw_encrypt(pass, salt, 1));
+               crypt_make_salt(salt + 3, 4);
+               puts(pw_encrypt(pass, salt, /*cleanup:*/ 0));
                return 0;
        }
 #endif
index af43380..562cd7f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2007 Denys Vlasenko <vda.linux@googlemail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /*
@@ -35,6 +35,7 @@ httpd_indexcgi.c -o index.cgi
  *   2576       4    2048    4628    1214 index.cgi.o
  */
 
+#define _GNU_SOURCE 1  /* for strchrnul */
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <errno.h>
@@ -221,20 +222,25 @@ int main(int argc, char *argv[])
        unsigned long long size_total;
        int odd;
        DIR *dirp;
-       char *QUERY_STRING;
-
-       QUERY_STRING = getenv("QUERY_STRING");
-       if (!QUERY_STRING
-        || QUERY_STRING[0] != '/'
-        || strstr(QUERY_STRING, "//")
-        || strstr(QUERY_STRING, "/../")
-        || strcmp(strrchr(QUERY_STRING, '/'), "/..") == 0
+       char *location;
+
+       location = getenv("REQUEST_URI");
+       if (!location)
+               return 1;
+
+       /* drop URL arguments if any */
+       strchrnul(location, '?')[0] = '\0';
+
+       if (location[0] != '/'
+        || strstr(location, "//")
+        || strstr(location, "/../")
+        || strcmp(strrchr(location, '/'), "/..") == 0
        ) {
                return 1;
        }
 
        if (chdir("..")
-        || (QUERY_STRING[1] && chdir(QUERY_STRING + 1))
+        || (location[1] && chdir(location + 1))
        ) {
                return 1;
        }
@@ -271,14 +277,14 @@ int main(int argc, char *argv[])
                "\r\n" /* Mandatory empty line after headers */
                "<html><head><title>Index of ");
        /* Guard against directories with &, > etc */
-       fmt_html(QUERY_STRING);
+       fmt_html(location);
        fmt_str(
                "</title>\n"
                STYLE_STR
                "</head>" "\n"
                "<body>" "\n"
                "<h1>Index of ");
-       fmt_html(QUERY_STRING);
+       fmt_html(location);
        fmt_str(
                "</h1>" "\n"
                "<table>" "\n"
index 03f2633..4bd9a6d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2009 Denys Vlasenko <vda.linux@googlemail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /*
@@ -52,9 +52,9 @@ httpd_ssi.c -o httpd_ssi
 
 static char* skip_whitespace(char *s)
 {
-        while (*s == ' ' || *s == '\t') ++s;
+       while (*s == ' ' || *s == '\t') ++s;
 
-        return s;
+       return s;
 }
 
 static char line[64 * 1024];
@@ -133,7 +133,7 @@ static void process_includes(const char *filename)
                process_includes(include_directive);
 
                /* Print everything after directive */
-               if (end) {
+               if (end) {
                        fputs(end, stdout);
                        free(end);
                }
index 1a56c1c..8984b02 100644 (file)
@@ -10,7 +10,7 @@
  * Authors of the original ifconfig was:
  *              Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*
  * IPV6 support added by Bart Visscher <magick@linux-fan.com>
  */
 
+//usage:#define ifconfig_trivial_usage
+//usage:       IF_FEATURE_IFCONFIG_STATUS("[-a]") " interface [address]"
+//usage:#define ifconfig_full_usage "\n\n"
+//usage:       "Configure a network interface\n"
+//usage:     "\n"
+//usage:       IF_FEATURE_IPV6(
+//usage:       "       [add ADDRESS[/PREFIXLEN]]\n")
+//usage:       IF_FEATURE_IPV6(
+//usage:       "       [del ADDRESS[/PREFIXLEN]]\n")
+//usage:       "       [[-]broadcast [ADDRESS]] [[-]pointopoint [ADDRESS]]\n"
+//usage:       "       [netmask ADDRESS] [dstaddr ADDRESS]\n"
+//usage:       IF_FEATURE_IFCONFIG_SLIP(
+//usage:       "       [outfill NN] [keepalive NN]\n")
+//usage:       "       " IF_FEATURE_IFCONFIG_HW("[hw ether" IF_FEATURE_HWIB("|infiniband")" ADDRESS] ") "[metric NN] [mtu NN]\n"
+//usage:       "       [[-]trailers] [[-]arp] [[-]allmulti]\n"
+//usage:       "       [multicast] [[-]promisc] [txqueuelen NN] [[-]dynamic]\n"
+//usage:       IF_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ(
+//usage:       "       [mem_start NN] [io_addr NN] [irq NN]\n")
+//usage:       "       [up|down] ..."
+
+#include "libbb.h"
+#include "inet_common.h"
 #include <net/if.h>
 #include <net/if_arp.h>
 #include <netinet/in.h>
-#if defined(__GLIBC__) && __GLIBC__ >=2 && __GLIBC_MINOR__ >= 1
-#include <netpacket/packet.h>
-#include <net/ethernet.h>
-#else
-#include <sys/types.h>
-#include <netinet/if_ether.h>
+#ifdef HAVE_NET_ETHERNET_H
+# include <net/ethernet.h>
 #endif
-#include "libbb.h"
-#include "inet_common.h"
 
 #if ENABLE_FEATURE_IFCONFIG_SLIP
-# include <net/if_slip.h>
+# include <linux/if_slip.h>
 #endif
 
 /* I don't know if this is needed for busybox or not.  Anyone? */
@@ -158,10 +174,6 @@ struct in6_ifreq {
 #define ARG_ADD_DEL      (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
 
 
-/*
- * Set up the tables.  Warning!  They must have corresponding order!
- */
-
 struct arg1opt {
        const char *name;
        unsigned short selector;
@@ -182,6 +194,10 @@ struct options {
 
 #define ifreq_offsetof(x)  offsetof(struct ifreq, x)
 
+/*
+ * Set up the tables.  Warning!  They must have corresponding order!
+ */
+
 static const struct arg1opt Arg1Opt[] = {
        { "SIFMETRIC",  SIOCSIFMETRIC,  ifreq_offsetof(ifr_metric) },
        { "SIFMTU",     SIOCSIFMTU,     ifreq_offsetof(ifr_mtu) },
@@ -204,11 +220,11 @@ static const struct arg1opt Arg1Opt[] = {
        { "SIFMAP",     SIOCSIFMAP,     ifreq_offsetof(ifr_map.base_addr) },
        { "SIFMAP",     SIOCSIFMAP,     ifreq_offsetof(ifr_map.irq) },
 #endif
-       /* Last entry if for unmatched (possibly hostname) arg. */
 #if ENABLE_FEATURE_IPV6
        { "SIFADDR",    SIOCSIFADDR,    ifreq_offsetof(ifr_addr) }, /* IPv6 version ignores the offset */
        { "DIFADDR",    SIOCDIFADDR,    ifreq_offsetof(ifr_addr) }, /* IPv6 version ignores the offset */
 #endif
+       /* Last entry is for unmatched (assumed to be hostname/address) arg. */
        { "SIFADDR",    SIOCSIFADDR,    ifreq_offsetof(ifr_addr) },
 };
 
@@ -249,16 +265,6 @@ static const struct options OptArray[] = {
        { NULL,          0,             ARG_HOSTNAME,    (IFF_UP | IFF_RUNNING) }
 };
 
-/*
- * A couple of prototypes.
- */
-#if ENABLE_FEATURE_IFCONFIG_HW
-static int in_ether(const char *bufp, struct sockaddr *sap);
-#endif
-
-/*
- * Our main function.
- */
 int ifconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int ifconfig_main(int argc UNUSED_PARAM, char **argv)
 {
@@ -314,7 +320,7 @@ int ifconfig_main(int argc UNUSED_PARAM, char **argv)
        strncpy_IFNAMSIZ(ifr.ifr_name, *argv);
 
        /* Process the remaining arguments. */
-       while (*++argv != (char *) NULL) {
+       while (*++argv != NULL) {
                p = *argv;
                mask = N_MASK;
                if (*p == '-') {        /* If the arg starts with '-'... */
@@ -340,9 +346,9 @@ int ifconfig_main(int argc UNUSED_PARAM, char **argv)
  FOUND_ARG:
                if (mask & ARG_MASK) {
                        mask = op->arg_flags;
-                       a1op = Arg1Opt + (op - OptArray);
                        if (mask & A_NETMASK & did_flags)
                                bb_show_usage();
+                       a1op = Arg1Opt + (op - OptArray);
                        if (*++argv == NULL) {
                                if (mask & A_ARG_REQ)
                                        bb_show_usage();
@@ -355,27 +361,18 @@ int ifconfig_main(int argc UNUSED_PARAM, char **argv)
 #if ENABLE_FEATURE_IFCONFIG_HW
                                        if (mask & A_CAST_RESOLVE) {
 #endif
-#if ENABLE_FEATURE_IPV6
-                                               char *prefix;
-                                               int prefix_len = 0;
-#endif
-                                               /*safe_strncpy(host, *argv, (sizeof host));*/
                                                host = *argv;
-#if ENABLE_FEATURE_IPV6
-                                               prefix = strchr(host, '/');
-                                               if (prefix) {
-                                                       prefix_len = xatou_range(prefix + 1, 0, 128);
-                                                       *prefix = '\0';
-                                               }
-#endif
+                                               if (strcmp(host, "inet") == 0)
+                                                       continue; /* compat stuff */
                                                sai.sin_family = AF_INET;
                                                sai.sin_port = 0;
-                                               if (!strcmp(host, bb_str_default)) {
+                                               if (strcmp(host, "default") == 0) {
                                                        /* Default is special, meaning 0.0.0.0. */
                                                        sai.sin_addr.s_addr = INADDR_ANY;
                                                }
 #if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
-                                               else if ((host[0] == '+' && !host[1]) && (mask & A_BROADCAST)
+                                               else if ((host[0] == '+' && !host[1])
+                                                && (mask & A_BROADCAST)
                                                 && (did_flags & (A_NETMASK|A_HOSTNAME)) == (A_NETMASK|A_HOSTNAME)
                                                ) {
                                                        /* + is special, meaning broadcast is derived. */
@@ -384,23 +381,36 @@ int ifconfig_main(int argc UNUSED_PARAM, char **argv)
 #endif
                                                else {
                                                        len_and_sockaddr *lsa;
-                                                       if (strcmp(host, "inet") == 0)
-                                                               continue; /* compat stuff */
+#if ENABLE_FEATURE_IPV6
+                                                       char *prefix;
+                                                       int prefix_len = 0;
+                                                       prefix = strchr(host, '/');
+                                                       if (prefix) {
+                                                               prefix_len = xatou_range(prefix + 1, 0, 128);
+                                                               *prefix = '\0';
+                                                       }
+ resolve:
+#endif
                                                        lsa = xhost2sockaddr(host, 0);
 #if ENABLE_FEATURE_IPV6
+                                                       if (lsa->u.sa.sa_family != AF_INET6 && prefix) {
+/* TODO: we do not support "ifconfig eth0 up 1.2.3.4/17".
+ * For now, just make it fail instead of silently ignoring "/17" part:
+ */
+                                                               *prefix = '/';
+                                                               goto resolve;
+                                                       }
                                                        if (lsa->u.sa.sa_family == AF_INET6) {
                                                                int sockfd6;
                                                                struct in6_ifreq ifr6;
 
-                                                               memcpy((char *) &ifr6.ifr6_addr,
-                                                                               (char *) &(lsa->u.sin6.sin6_addr),
-                                                                               sizeof(struct in6_addr));
-
-                                                               /* Create a channel to the NET kernel. */
                                                                sockfd6 = xsocket(AF_INET6, SOCK_DGRAM, 0);
-                                                               xioctl(sockfd6, SIOGIFINDEX, &ifr);
+                                                               xioctl(sockfd6, SIOCGIFINDEX, &ifr);
                                                                ifr6.ifr6_ifindex = ifr.ifr_ifindex;
                                                                ifr6.ifr6_prefixlen = prefix_len;
+                                                               memcpy(&ifr6.ifr6_addr,
+                                                                               &lsa->u.sin6.sin6_addr,
+                                                                               sizeof(struct in6_addr));
                                                                ioctl_or_perror_and_die(sockfd6, a1op->selector, &ifr6, "SIOC%s", a1op->name);
                                                                if (ENABLE_FEATURE_CLEAN_UP)
                                                                        free(lsa);
@@ -421,19 +431,18 @@ int ifconfig_main(int argc UNUSED_PARAM, char **argv)
 #if ENABLE_FEATURE_IFCONFIG_HW
                                        } else {        /* A_CAST_HOST_COPY_IN_ETHER */
                                                /* This is the "hw" arg case. */
-                                               smalluint hw_class= index_in_substrings("ether\0"
+                                               smalluint hw_class = index_in_substrings("ether\0"
                                                                IF_FEATURE_HWIB("infiniband\0"), *argv) + 1;
                                                if (!hw_class || !*++argv)
                                                        bb_show_usage();
-                                               /*safe_strncpy(host, *argv, sizeof(host));*/
                                                host = *argv;
                                                if (hw_class == 1 ? in_ether(host, &sa) : in_ib(host, &sa))
                                                        bb_error_msg_and_die("invalid hw-addr %s", host);
                                                p = (char *) &sa;
                                        }
 #endif
-                                       memcpy( (((char *)&ifr) + a1op->ifr_offset),
-                                                  p, sizeof(struct sockaddr));
+                                       memcpy( ((char *)&ifr) + a1op->ifr_offset,
+                                               p, sizeof(struct sockaddr));
                                } else {
                                        /* FIXME: error check?? */
                                        unsigned long i = strtoul(*argv, NULL, 0);
@@ -442,17 +451,17 @@ int ifconfig_main(int argc UNUSED_PARAM, char **argv)
                                        if (mask & A_MAP_TYPE) {
                                                xioctl(sockfd, SIOCGIFMAP, &ifr);
                                                if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR)
-                                                       *((unsigned char *) p) = i;
+                                                       *(unsigned char *) p = i;
                                                else if (mask & A_MAP_USHORT)
-                                                       *((unsigned short *) p) = i;
+                                                       *(unsigned short *) p = i;
                                                else
-                                                       *((unsigned long *) p) = i;
+                                                       *(unsigned long *) p = i;
                                        } else
 #endif
                                        if (mask & A_CAST_CHAR_PTR)
-                                               *((caddr_t *) p) = (caddr_t) i;
+                                               *(caddr_t *) p = (caddr_t) i;
                                        else    /* A_CAST_INT */
-                                               *((int *) p) = i;
+                                               *(int *) p = i;
                                }
 
                                ioctl_or_perror_and_die(sockfd, a1op->selector, &ifr, "SIOC%s", a1op->name);
@@ -478,7 +487,7 @@ int ifconfig_main(int argc UNUSED_PARAM, char **argv)
                        if (!(mask & A_SET_AFTER))
                                continue;
                        mask = N_SET;
-               }
+               } /* if (mask & ARG_MASK) */
 
                xioctl(sockfd, SIOCGIFFLAGS, &ifr);
                selector = op->selector;
@@ -493,46 +502,3 @@ int ifconfig_main(int argc UNUSED_PARAM, char **argv)
                close(sockfd);
        return 0;
 }
-
-#if ENABLE_FEATURE_IFCONFIG_HW
-/* Input an Ethernet address and convert to binary. */
-static int in_ether(const char *bufp, struct sockaddr *sap)
-{
-       char *ptr;
-       int i, j;
-       unsigned char val;
-       unsigned char c;
-
-       sap->sa_family = ARPHRD_ETHER;
-       ptr = (char *) sap->sa_data;
-
-       i = 0;
-       do {
-               j = val = 0;
-
-               /* We might get a semicolon here - not required. */
-               if (i && (*bufp == ':')) {
-                       bufp++;
-               }
-
-               do {
-                       c = *bufp;
-                       if (((unsigned char)(c - '0')) <= 9) {
-                               c -= '0';
-                       } else if (((unsigned char)((c|0x20) - 'a')) <= 5) {
-                               c = (c|0x20) - ('a'-10);
-                       } else if (j && (c == ':' || c == 0)) {
-                               break;
-                       } else {
-                               return -1;
-                       }
-                       ++bufp;
-                       val <<= 4;
-                       val += c;
-               } while (++j < 2);
-               *ptr++ = val;
-       } while (++i < ETH_ALEN);
-
-       return *bufp; /* Error if we don't end at end of string. */
-}
-#endif
index 0b3ebf7..c3be818 100644 (file)
  *         set version to 1.1.0
  */
 
+//usage:#define ifenslave_trivial_usage
+//usage:       "[-cdf] MASTER_IFACE SLAVE_IFACE..."
+//usage:#define ifenslave_full_usage "\n\n"
+//usage:       "Configure network interfaces for parallel routing\n"
+//usage:     "\n       -c,--change-active      Change active slave"
+//usage:     "\n       -d,--detach             Remove slave interface from bonding device"
+//usage:     "\n       -f,--force              Force, even if interface is not Ethernet"
+/* //usage:  "\n       -r,--receive-slave      Create a receive-only slave" */
+//usage:
+//usage:#define ifenslave_example_usage
+//usage:       "To create a bond device, simply follow these three steps:\n"
+//usage:       "- ensure that the required drivers are properly loaded:\n"
+//usage:       "  # modprobe bonding ; modprobe <3c59x|eepro100|pcnet32|tulip|...>\n"
+//usage:       "- assign an IP address to the bond device:\n"
+//usage:       "  # ifconfig bond0 <addr> netmask <mask> broadcast <bcast>\n"
+//usage:       "- attach all the interfaces you need to the bond device:\n"
+//usage:       "  # ifenslave bond0 eth0 eth1 eth2\n"
+//usage:       "  If bond0 didn't have a MAC address, it will take eth0's. Then, all\n"
+//usage:       "  interfaces attached AFTER this assignment will get the same MAC addr.\n\n"
+//usage:       "  To detach a dead interface without setting the bond device down:\n"
+//usage:       "  # ifenslave -d bond0 eth1\n\n"
+//usage:       "  To set the bond device down and automatically release all the slaves:\n"
+//usage:       "  # ifconfig bond0 down\n\n"
+//usage:       "  To change active slave:\n"
+//usage:       "  # ifenslave -c bond0 eth0\n"
+
 #include "libbb.h"
 
 /* #include <net/if.h> - no. linux/if_bonding.h pulls in linux/if.h */
@@ -244,7 +270,7 @@ static int set_if_addr(char *master_ifname, char *slave_ifname)
                if (res < 0) {
                        ifr.ifr_addr.sa_family = AF_INET;
                        memset(ifr.ifr_addr.sa_data, 0,
-                              sizeof(ifr.ifr_addr.sa_data));
+                               sizeof(ifr.ifr_addr.sa_data));
                }
 
                res = set_ifrname_and_do_ioctl(ifra[i].s_ioctl, &ifr, slave_ifname);
@@ -520,7 +546,7 @@ int ifenslave_main(int argc UNUSED_PARAM, char **argv)
 #ifdef WHY_BOTHER
        /* Neither -c[hange] nor -d[etach] -> it's "enslave" then;
         * and -f[orce] is not there too. Check that it's ethernet. */
-       if (!(opt & (OPT_d|OPT_c|OPT_f)) {
+       if (!(opt & (OPT_d|OPT_c|OPT_f))) {
                /* The family '1' is ARPHRD_ETHER for ethernet. */
                if (master.hwaddr.ifr_hwaddr.sa_family != 1) {
                        bb_error_msg_and_die(
index 8cb07db..b578f4c 100644 (file)
@@ -4,15 +4,42 @@
  *
  * Copyright (C) 2009 Maksym Kryzhanovskyy <xmaks@email.cz>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define ifplugd_trivial_usage
+//usage:       "[OPTIONS]"
+//usage:#define ifplugd_full_usage "\n\n"
+//usage:       "Network interface plug detection daemon\n"
+//usage:     "\n       -n              Don't daemonize"
+//usage:     "\n       -s              Don't log to syslog"
+//usage:     "\n       -i IFACE        Interface"
+//usage:     "\n       -f/-F           Treat link detection error as link down/link up"
+//usage:     "\n                       (otherwise exit on error)"
+//usage:     "\n       -a              Don't up interface at each link probe"
+//usage:     "\n       -M              Monitor creation/destruction of interface"
+//usage:     "\n                       (otherwise it must exist)"
+//usage:     "\n       -r PROG         Script to run"
+//usage:     "\n       -x ARG          Extra argument for script"
+//usage:     "\n       -I              Don't exit on nonzero exit code from script"
+//usage:     "\n       -p              Don't run \"up\" script on startup"
+//usage:     "\n       -q              Don't run \"down\" script on exit"
+//usage:     "\n       -l              Always run script on startup"
+//usage:     "\n       -t SECS         Poll time in seconds"
+//usage:     "\n       -u SECS         Delay before running script after link up"
+//usage:     "\n       -d SECS         Delay after link down"
+//usage:     "\n       -m MODE         API mode (mii, priv, ethtool, wlan, iff, auto)"
+//usage:     "\n       -k              Kill running daemon"
+
 #include "libbb.h"
 
 #include "fix_u32.h"
 #include <linux/if.h>
 #include <linux/mii.h>
 #include <linux/ethtool.h>
-#include <net/ethernet.h>
+#ifdef HAVE_NET_ETHERNET_H
+# include <net/ethernet.h>
+#endif
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #include <linux/sockios.h>
@@ -71,15 +98,6 @@ enum {
 # define OPTION_STR "+ansfFi:r:It:u:d:m:pqlx:M"
 #endif
 
-enum { // api mode
-       API_AUTO        = 'a',
-       API_ETHTOOL     = 'e',
-       API_MII         = 'm',
-       API_PRIVATE     = 'p',
-       API_WLAN        = 'w',
-       API_IFF         = 'i',
-};
-
 enum { // interface status
        IFSTATUS_ERR = -1,
        IFSTATUS_DOWN = 0,
@@ -95,6 +113,7 @@ struct globals {
        smallint iface_last_status;
        smallint iface_prev_status;
        smallint iface_exists;
+       smallint api_method_num;
 
        /* Used in getopt32, must have sizeof == sizeof(int) */
        unsigned poll_time;
@@ -105,9 +124,6 @@ struct globals {
        const char *api_mode;
        const char *script_name;
        const char *extra_arg;
-
-       smallint (*detect_link_func)(void);
-       smallint (*cached_detect_link_func)(void);
 };
 #define G (*ptr_to_globals)
 #define INIT_G() do { \
@@ -122,42 +138,12 @@ struct globals {
 } while (0)
 
 
-static const char *strstatus(int status)
-{
-       if (status == IFSTATUS_ERR)
-               return "error";
-       return "down\0up" + (status * 5);
-}
+/* Utility routines */
 
-static int run_script(const char *action)
+static void set_ifreq_to_ifname(struct ifreq *ifreq)
 {
-       char *env_PREVIOUS, *env_CURRENT;
-       char *argv[5];
-       int r;
-
-       bb_error_msg("executing '%s %s %s'", G.script_name, G.iface, action);
-
-       argv[0] = (char*) G.script_name;
-       argv[1] = (char*) G.iface;
-       argv[2] = (char*) action;
-       argv[3] = (char*) G.extra_arg;
-       argv[4] = NULL;
-
-       env_PREVIOUS = xasprintf("%s=%s", IFPLUGD_ENV_PREVIOUS, strstatus(G.iface_prev_status));
-       putenv(env_PREVIOUS);
-       env_CURRENT = xasprintf("%s=%s", IFPLUGD_ENV_CURRENT, strstatus(G.iface_last_status));
-       putenv(env_CURRENT);
-
-       /* r < 0 - can't exec, 0 <= r < 0x180 - exited, >=0x180 - killed by sig (r-0x180) */
-       r = spawn_and_wait(argv);
-
-       unsetenv(IFPLUGD_ENV_PREVIOUS);
-       unsetenv(IFPLUGD_ENV_CURRENT);
-       free(env_PREVIOUS);
-       free(env_CURRENT);
-
-       bb_error_msg("exit code: %d", r & 0xff);
-       return (option_mask32 & FLAG_IGNORE_RETVAL) ? 0 : r;
+       memset(ifreq, 0, sizeof(struct ifreq));
+       strncpy_IFNAMSIZ(ifreq->ifr_name, G.iface);
 }
 
 static int network_ioctl(int request, void* data, const char *errmsg)
@@ -168,95 +154,25 @@ static int network_ioctl(int request, void* data, const char *errmsg)
        return r;
 }
 
-static void set_ifreq_to_ifname(struct ifreq *ifreq)
-{
-       memset(ifreq, 0, sizeof(struct ifreq));
-       strncpy_IFNAMSIZ(ifreq->ifr_name, G.iface);
-}
-
-static void up_iface(void)
-{
-       struct ifreq ifrequest;
-
-       if (!G.iface_exists)
-               return;
-
-       set_ifreq_to_ifname(&ifrequest);
-       if (network_ioctl(SIOCGIFFLAGS, &ifrequest, "getting interface flags") < 0) {
-               G.iface_exists = 0;
-               return;
-       }
-
-       if (!(ifrequest.ifr_flags & IFF_UP)) {
-               ifrequest.ifr_flags |= IFF_UP;
-               /* Let user know we mess up with interface */
-               bb_error_msg("upping interface");
-               if (network_ioctl(SIOCSIFFLAGS, &ifrequest, "setting interface flags") < 0)
-                       xfunc_die();
-       }
-
-#if 0 /* why do we mess with IP addr? It's not our business */
-       if (network_ioctl(SIOCGIFADDR, &ifrequest, "can't get interface address") < 0) {
-       } else if (ifrequest.ifr_addr.sa_family != AF_INET) {
-               bb_perror_msg("the interface is not IP-based");
-       } else {
-               ((struct sockaddr_in*)(&ifrequest.ifr_addr))->sin_addr.s_addr = INADDR_ANY;
-               network_ioctl(SIOCSIFADDR, &ifrequest, "can't set interface address");
-       }
-       network_ioctl(SIOCGIFFLAGS, &ifrequest, "can't get interface flags");
-#endif
-}
-
-static void maybe_up_new_iface(void)
-{
-       if (!(option_mask32 & FLAG_NO_AUTO))
-               up_iface();
-
-#if 0 /* bloat */
-       struct ifreq ifrequest;
-       struct ethtool_drvinfo driver_info;
-
-       set_ifreq_to_ifname(&ifrequest);
-       driver_info.cmd = ETHTOOL_GDRVINFO;
-       ifrequest.ifr_data = &driver_info;
-       if (network_ioctl(SIOCETHTOOL, &ifrequest, NULL) == 0) {
-               char buf[sizeof("/xx:xx:xx:xx:xx:xx")];
-
-               /* Get MAC */
-               buf[0] = '\0';
-               set_ifreq_to_ifname(&ifrequest);
-               if (network_ioctl(SIOCGIFHWADDR, &ifrequest, NULL) == 0) {
-                       sprintf(buf, "/%02X:%02X:%02X:%02X:%02X:%02X",
-                               (uint8_t)(ifrequest.ifr_hwaddr.sa_data[0]),
-                               (uint8_t)(ifrequest.ifr_hwaddr.sa_data[1]),
-                               (uint8_t)(ifrequest.ifr_hwaddr.sa_data[2]),
-                               (uint8_t)(ifrequest.ifr_hwaddr.sa_data[3]),
-                               (uint8_t)(ifrequest.ifr_hwaddr.sa_data[4]),
-                               (uint8_t)(ifrequest.ifr_hwaddr.sa_data[5]));
-               }
-
-               bb_error_msg("using interface %s%s with driver<%s> (version: %s)",
-                       G.iface, buf, driver_info.driver, driver_info.version);
-       }
-#endif
-
-       G.cached_detect_link_func = NULL;
-}
+/* Link detection routines and table */
 
 static smallint detect_link_mii(void)
 {
-       struct ifreq ifreq;
-       struct mii_ioctl_data *mii = (void *)&ifreq.ifr_data;
+       /* char buffer instead of bona-fide struct avoids aliasing warning */
+       char buf[sizeof(struct ifreq)];
+       struct ifreq *const ifreq = (void *)buf;
 
-       set_ifreq_to_ifname(&ifreq);
+       struct mii_ioctl_data *mii = (void *)&ifreq->ifr_data;
+
+       set_ifreq_to_ifname(ifreq);
 
-       if (network_ioctl(SIOCGMIIPHY, &ifreq, "SIOCGMIIPHY") < 0) {
+       if (network_ioctl(SIOCGMIIPHY, ifreq, "SIOCGMIIPHY") < 0) {
                return IFSTATUS_ERR;
        }
 
        mii->reg_num = 1;
 
-       if (network_ioctl(SIOCGMIIREG, &ifreq, "SIOCGMIIREG") < 0) {
+       if (network_ioctl(SIOCGMIIREG, ifreq, "SIOCGMIIREG") < 0) {
                return IFSTATUS_ERR;
        }
 
@@ -265,18 +181,21 @@ static smallint detect_link_mii(void)
 
 static smallint detect_link_priv(void)
 {
-       struct ifreq ifreq;
-       struct mii_ioctl_data *mii = (void *)&ifreq.ifr_data;
+       /* char buffer instead of bona-fide struct avoids aliasing warning */
+       char buf[sizeof(struct ifreq)];
+       struct ifreq *const ifreq = (void *)buf;
 
-       set_ifreq_to_ifname(&ifreq);
+       struct mii_ioctl_data *mii = (void *)&ifreq->ifr_data;
 
-       if (network_ioctl(SIOCDEVPRIVATE, &ifreq, "SIOCDEVPRIVATE") < 0) {
+       set_ifreq_to_ifname(ifreq);
+
+       if (network_ioctl(SIOCDEVPRIVATE, ifreq, "SIOCDEVPRIVATE") < 0) {
                return IFSTATUS_ERR;
        }
 
        mii->reg_num = 1;
 
-       if (network_ioctl(SIOCDEVPRIVATE+1, &ifreq, "SIOCDEVPRIVATE+1") < 0) {
+       if (network_ioctl(SIOCDEVPRIVATE+1, ifreq, "SIOCDEVPRIVATE+1") < 0) {
                return IFSTATUS_ERR;
        }
 
@@ -348,40 +267,135 @@ static smallint detect_link_wlan(void)
        return IFSTATUS_UP;
 }
 
-static smallint detect_link_auto(void)
+enum { // api mode
+       API_ETHTOOL, // 'e'
+       API_MII,     // 'm'
+       API_PRIVATE, // 'p'
+       API_WLAN,    // 'w'
+       API_IFF,     // 'i'
+       API_AUTO,    // 'a'
+};
+
+static const char api_modes[] ALIGN1 = "empwia";
+
+static const struct {
+       const char *name;
+       smallint (*func)(void);
+} method_table[] = {
+       { "SIOCETHTOOL"       , &detect_link_ethtool },
+       { "SIOCGMIIPHY"       , &detect_link_mii     },
+       { "SIOCDEVPRIVATE"    , &detect_link_priv    },
+       { "wireless extension", &detect_link_wlan    },
+       { "IFF_RUNNING"       , &detect_link_iff     },
+};
+
+
+
+static const char *strstatus(int status)
 {
-       static const struct {
-               const char *name;
-               smallint (*func)(void);
-       } method[] = {
-               { "SIOCETHTOOL"       , &detect_link_ethtool },
-               { "SIOCGMIIPHY"       , &detect_link_mii     },
-               { "SIOCDEVPRIVATE"    , &detect_link_priv    },
-               { "wireless extension", &detect_link_wlan    },
-               { "IFF_RUNNING"       , &detect_link_iff     },
-       };
-       int i;
-       smallint iface_status;
-       smallint sv_logmode;
+       if (status == IFSTATUS_ERR)
+               return "error";
+       return "down\0up" + (status * 5);
+}
+
+static int run_script(const char *action)
+{
+       char *env_PREVIOUS, *env_CURRENT;
+       char *argv[5];
+       int r;
+
+       bb_error_msg("executing '%s %s %s'", G.script_name, G.iface, action);
+
+       argv[0] = (char*) G.script_name;
+       argv[1] = (char*) G.iface;
+       argv[2] = (char*) action;
+       argv[3] = (char*) G.extra_arg;
+       argv[4] = NULL;
+
+       env_PREVIOUS = xasprintf("%s=%s", IFPLUGD_ENV_PREVIOUS, strstatus(G.iface_prev_status));
+       putenv(env_PREVIOUS);
+       env_CURRENT = xasprintf("%s=%s", IFPLUGD_ENV_CURRENT, strstatus(G.iface_last_status));
+       putenv(env_CURRENT);
+
+       /* r < 0 - can't exec, 0 <= r < 0x180 - exited, >=0x180 - killed by sig (r-0x180) */
+       r = spawn_and_wait(argv);
+
+       unsetenv(IFPLUGD_ENV_PREVIOUS);
+       unsetenv(IFPLUGD_ENV_CURRENT);
+       free(env_PREVIOUS);
+       free(env_CURRENT);
+
+       bb_error_msg("exit code: %d", r & 0xff);
+       return (option_mask32 & FLAG_IGNORE_RETVAL) ? 0 : r;
+}
+
+static void up_iface(void)
+{
+       struct ifreq ifrequest;
 
-       if (G.cached_detect_link_func) {
-               iface_status = G.cached_detect_link_func();
-               if (iface_status != IFSTATUS_ERR)
-                       return iface_status;
+       if (!G.iface_exists)
+               return;
+
+       set_ifreq_to_ifname(&ifrequest);
+       if (network_ioctl(SIOCGIFFLAGS, &ifrequest, "getting interface flags") < 0) {
+               G.iface_exists = 0;
+               return;
        }
 
-       sv_logmode = logmode;
-       for (i = 0; i < ARRAY_SIZE(method); i++) {
-               logmode = LOGMODE_NONE;
-               iface_status = method[i].func();
-               logmode = sv_logmode;
-               if (iface_status != IFSTATUS_ERR) {
-                       G.cached_detect_link_func = method[i].func;
-                       bb_error_msg("using %s detection mode", method[i].name);
-                       break;
+       if (!(ifrequest.ifr_flags & IFF_UP)) {
+               ifrequest.ifr_flags |= IFF_UP;
+               /* Let user know we mess up with interface */
+               bb_error_msg("upping interface");
+               if (network_ioctl(SIOCSIFFLAGS, &ifrequest, "setting interface flags") < 0)
+                       xfunc_die();
+       }
+
+#if 0 /* why do we mess with IP addr? It's not our business */
+       if (network_ioctl(SIOCGIFADDR, &ifrequest, "can't get interface address") < 0) {
+       } else if (ifrequest.ifr_addr.sa_family != AF_INET) {
+               bb_perror_msg("the interface is not IP-based");
+       } else {
+               ((struct sockaddr_in*)(&ifrequest.ifr_addr))->sin_addr.s_addr = INADDR_ANY;
+               network_ioctl(SIOCSIFADDR, &ifrequest, "can't set interface address");
+       }
+       network_ioctl(SIOCGIFFLAGS, &ifrequest, "can't get interface flags");
+#endif
+}
+
+static void maybe_up_new_iface(void)
+{
+       if (!(option_mask32 & FLAG_NO_AUTO))
+               up_iface();
+
+#if 0 /* bloat */
+       struct ifreq ifrequest;
+       struct ethtool_drvinfo driver_info;
+
+       set_ifreq_to_ifname(&ifrequest);
+       driver_info.cmd = ETHTOOL_GDRVINFO;
+       ifrequest.ifr_data = &driver_info;
+       if (network_ioctl(SIOCETHTOOL, &ifrequest, NULL) == 0) {
+               char buf[sizeof("/xx:xx:xx:xx:xx:xx")];
+
+               /* Get MAC */
+               buf[0] = '\0';
+               set_ifreq_to_ifname(&ifrequest);
+               if (network_ioctl(SIOCGIFHWADDR, &ifrequest, NULL) == 0) {
+                       sprintf(buf, "/%02X:%02X:%02X:%02X:%02X:%02X",
+                               (uint8_t)(ifrequest.ifr_hwaddr.sa_data[0]),
+                               (uint8_t)(ifrequest.ifr_hwaddr.sa_data[1]),
+                               (uint8_t)(ifrequest.ifr_hwaddr.sa_data[2]),
+                               (uint8_t)(ifrequest.ifr_hwaddr.sa_data[3]),
+                               (uint8_t)(ifrequest.ifr_hwaddr.sa_data[4]),
+                               (uint8_t)(ifrequest.ifr_hwaddr.sa_data[5]));
                }
+
+               bb_error_msg("using interface %s%s with driver<%s> (version: %s)",
+                       G.iface, buf, driver_info.driver, driver_info.version);
        }
-       return iface_status;
+#endif
+       if (G.api_mode[0] == 'a')
+               G.api_method_num = API_AUTO;
 }
 
 static smallint detect_link(void)
@@ -398,18 +412,32 @@ static smallint detect_link(void)
        if (!(option_mask32 & FLAG_NO_AUTO))
                up_iface();
 
-       status = G.detect_link_func();
+       if (G.api_method_num == API_AUTO) {
+               int i;
+               smallint sv_logmode;
+
+               sv_logmode = logmode;
+               for (i = 0; i < ARRAY_SIZE(method_table); i++) {
+                       logmode = LOGMODE_NONE;
+                       status = method_table[i].func();
+                       logmode = sv_logmode;
+                       if (status != IFSTATUS_ERR) {
+                               G.api_method_num = i;
+                               bb_error_msg("using %s detection mode", method_table[i].name);
+                               break;
+                       }
+               }
+       } else {
+               status = method_table[G.api_method_num].func();
+       }
+
        if (status == IFSTATUS_ERR) {
                if (option_mask32 & FLAG_IGNORE_FAIL)
                        status = IFSTATUS_DOWN;
-               if (option_mask32 & FLAG_IGNORE_FAIL_POSITIVE)
+               else if (option_mask32 & FLAG_IGNORE_FAIL_POSITIVE)
                        status = IFSTATUS_UP;
-       }
-
-       if (status == IFSTATUS_ERR
-        && G.detect_link_func == detect_link_auto
-       ) {
-               bb_error_msg("can't detect link status");
+               else if (G.api_mode[0] == 'a')
+                       bb_error_msg("can't detect link status");
        }
 
        if (status != G.iface_last_status) {
@@ -423,20 +451,24 @@ static smallint detect_link(void)
 static NOINLINE int check_existence_through_netlink(void)
 {
        int iface_len;
-       char replybuf[1024];
+       /* Buffer was 1K, but on linux-3.9.9 it was reported to be too small.
+        * netlink.h: "limit to 8K to avoid MSG_TRUNC when PAGE_SIZE is very large".
+        * Note: on error returns (-1) we exit, no need to free replybuf.
+        */
+       enum { BUF_SIZE = 8 * 1024 };
+       char *replybuf = xmalloc(BUF_SIZE);
 
        iface_len = strlen(G.iface);
        while (1) {
                struct nlmsghdr *mhdr;
                ssize_t bytes;
 
-               bytes = recv(netlink_fd, &replybuf, sizeof(replybuf), MSG_DONTWAIT);
+               bytes = recv(netlink_fd, replybuf, BUF_SIZE, MSG_DONTWAIT);
                if (bytes < 0) {
                        if (errno == EAGAIN)
-                               return G.iface_exists;
+                               goto ret;
                        if (errno == EINTR)
                                continue;
-
                        bb_perror_msg("netlink: recv");
                        return -1;
                }
@@ -479,26 +511,11 @@ static NOINLINE int check_existence_through_netlink(void)
                }
        }
 
+ ret:
+       free(replybuf);
        return G.iface_exists;
 }
 
-static NOINLINE int netlink_open(void)
-{
-       int fd;
-       struct sockaddr_nl addr;
-
-       fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
-
-       memset(&addr, 0, sizeof(addr));
-       addr.nl_family = AF_NETLINK;
-       addr.nl_groups = RTMGRP_LINK;
-       addr.nl_pid = getpid();
-
-       xbind(fd, (struct sockaddr*)&addr, sizeof(addr));
-
-       return fd;
-}
-
 #if ENABLE_FEATURE_PIDFILE
 static NOINLINE pid_t read_pid(const char *filename)
 {
@@ -523,6 +540,7 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv)
        const char *iface_status_str;
        struct pollfd netlink_pollfd[1];
        unsigned opts;
+       const char *api_mode_found;
 #if ENABLE_FEATURE_PIDFILE
        char *pidfile_name;
        pid_t pid_from_pidfile;
@@ -539,12 +557,13 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv)
        applet_name = xasprintf("ifplugd(%s)", G.iface);
 
 #if ENABLE_FEATURE_PIDFILE
-       pidfile_name = xasprintf(_PATH_VARRUN"ifplugd.%s.pid", G.iface);
+       pidfile_name = xasprintf(CONFIG_PID_FILE_PATH "/ifplugd.%s.pid", G.iface);
        pid_from_pidfile = read_pid(pidfile_name);
 
        if (opts & FLAG_KILL) {
                if (pid_from_pidfile > 0)
-                       kill(pid_from_pidfile, SIGQUIT);
+                       /* Upstream tool use SIGINT for -k */
+                       kill(pid_from_pidfile, SIGINT);
                return EXIT_SUCCESS;
        }
 
@@ -552,35 +571,26 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv)
                bb_error_msg_and_die("daemon already running");
 #endif
 
-       switch (G.api_mode[0]) {
-       case API_AUTO:
-               G.detect_link_func = detect_link_auto;
-               break;
-       case API_ETHTOOL:
-               G.detect_link_func = detect_link_ethtool;
-               break;
-       case API_MII:
-               G.detect_link_func = detect_link_mii;
-               break;
-       case API_PRIVATE:
-               G.detect_link_func = detect_link_priv;
-               break;
-       case API_WLAN:
-               G.detect_link_func = detect_link_wlan;
-               break;
-       case API_IFF:
-               G.detect_link_func = detect_link_iff;
-               break;
-       default:
+       api_mode_found = strchr(api_modes, G.api_mode[0]);
+       if (!api_mode_found)
                bb_error_msg_and_die("unknown API mode '%s'", G.api_mode);
-       }
+       G.api_method_num = api_mode_found - api_modes;
 
        if (!(opts & FLAG_NO_DAEMON))
                bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
 
        xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd);
        if (opts & FLAG_MONITOR) {
-               xmove_fd(netlink_open(), netlink_fd);
+               struct sockaddr_nl addr;
+               int fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+
+               memset(&addr, 0, sizeof(addr));
+               addr.nl_family = AF_NETLINK;
+               addr.nl_groups = RTMGRP_LINK;
+               addr.nl_pid = getpid();
+
+               xbind(fd, (struct sockaddr*)&addr, sizeof(addr));
+               xmove_fd(fd, netlink_fd);
        }
 
        write_pidfile(pidfile_name);
index 69c56e8..0f0857c 100644 (file)
  *  (defined via CONFIG_IFUPDOWN_IFSTATE_PATH) and can be overridden by build
  *  configuration.
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define ifup_trivial_usage
+//usage:       "[-an"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] [-i FILE] IFACE..."
+//usage:#define ifup_full_usage "\n\n"
+//usage:       "       -a      De/configure all interfaces automatically"
+//usage:     "\n       -i FILE Use FILE for interface definitions"
+//usage:     "\n       -n      Print out what would happen, but don't do it"
+//usage:       IF_FEATURE_IFUPDOWN_MAPPING(
+//usage:     "\n               (note: doesn't disable mappings)"
+//usage:     "\n       -m      Don't run any mappings"
+//usage:       )
+//usage:     "\n       -v      Print out what would happen before doing it"
+//usage:     "\n       -f      Force de/configuration"
+//usage:
+//usage:#define ifdown_trivial_usage
+//usage:       "[-an"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] [-i FILE] IFACE..."
+//usage:#define ifdown_full_usage "\n\n"
+//usage:       "       -a      De/configure all interfaces automatically"
+//usage:     "\n       -i FILE Use FILE for interface definitions"
+//usage:     "\n       -n      Print out what would happen, but don't do it"
+//usage:       IF_FEATURE_IFUPDOWN_MAPPING(
+//usage:     "\n               (note: doesn't disable mappings)"
+//usage:     "\n       -m      Don't run any mappings"
+//usage:       )
+//usage:     "\n       -v      Print out what would happen before doing it"
+//usage:     "\n       -f      Force de/configuration"
+
 #include "libbb.h"
 /* After libbb.h, since it needs sys/types.h on some systems */
 #include <sys/utsname.h>
 #include <fnmatch.h>
 
 #define MAX_OPT_DEPTH 10
-#define EUNBALBRACK 10001
-#define EUNDEFVAR   10002
-#define EUNBALPER   10000
 
 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
 #define MAX_INTERFACE_LENGTH 10
@@ -61,7 +84,6 @@ struct mapping_defn_t {
 
        char *script;
 
-       int max_mappings;
        int n_mappings;
        char **mapping;
 };
@@ -76,7 +98,6 @@ struct interface_defn_t {
        const struct method_t *method;
 
        char *iface;
-       int max_options;
        int n_options;
        struct variable_t *option;
 };
@@ -106,11 +127,20 @@ enum {
 struct globals {
        char **my_environ;
        const char *startup_PATH;
+       char *shell;
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
 #define INIT_G() do { } while (0)
 
 
+static const char keywords_up_down[] ALIGN1 =
+       "up\0"
+       "down\0"
+       "pre-up\0"
+       "post-down\0"
+;
+
+
 #if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6
 
 static void addstr(char **bufp, const char *str, size_t str_length)
@@ -164,7 +194,7 @@ static char *get_var(const char *id, size_t idlen, struct interface_defn_t *ifd)
        return NULL;
 }
 
-#if ENABLE_FEATURE_IFUPDOWN_IP
+# if ENABLE_FEATURE_IFUPDOWN_IP
 static int count_netmask_bits(const char *dotted_quad)
 {
 //     int result;
@@ -195,12 +225,12 @@ static int count_netmask_bits(const char *dotted_quad)
        }
        return result;
 }
-#endif
+# endif
 
 static char *parse(const char *command, struct interface_defn_t *ifd)
 {
        size_t old_pos[MAX_OPT_DEPTH] = { 0 };
-       int okay[MAX_OPT_DEPTH] = { 1 };
+       smallint okay[MAX_OPT_DEPTH] = { 1 };
        int opt_depth = 1;
        char *result = NULL;
 
@@ -211,13 +241,10 @@ static char *parse(const char *command, struct interface_defn_t *ifd)
                        command++;
                        break;
                case '\\':
-                       if (command[1]) {
-                               addstr(&result, command + 1, 1);
-                               command += 2;
-                       } else {
-                               addstr(&result, command, 1);
+                       if (command[1])
                                command++;
-                       }
+                       addstr(&result, command, 1);
+                       command++;
                        break;
                case '[':
                        if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) {
@@ -226,7 +253,7 @@ static char *parse(const char *command, struct interface_defn_t *ifd)
                                opt_depth++;
                                command += 2;
                        } else {
-                               addstr(&result, "[", 1);
+                               addstr(&result, command, 1);
                                command++;
                        }
                        break;
@@ -238,7 +265,7 @@ static char *parse(const char *command, struct interface_defn_t *ifd)
                                }
                                command += 2;
                        } else {
-                               addstr(&result, "]", 1);
+                               addstr(&result, command, 1);
                                command++;
                        }
                        break;
@@ -250,7 +277,7 @@ static char *parse(const char *command, struct interface_defn_t *ifd)
                                command++;
                                nextpercent = strchr(command, '%');
                                if (!nextpercent) {
-                                       errno = EUNBALPER;
+                                       /* Unterminated %var% */
                                        free(result);
                                        return NULL;
                                }
@@ -258,17 +285,17 @@ static char *parse(const char *command, struct interface_defn_t *ifd)
                                varvalue = get_var(command, nextpercent - command, ifd);
 
                                if (varvalue) {
-#if ENABLE_FEATURE_IFUPDOWN_IP
+# if ENABLE_FEATURE_IFUPDOWN_IP
                                        /* "hwaddress <class> <address>":
                                         * unlike ifconfig, ip doesnt want <class>
                                         * (usually "ether" keyword). Skip it. */
                                        if (strncmp(command, "hwaddress", 9) == 0) {
                                                varvalue = skip_whitespace(skip_non_whitespace(varvalue));
                                        }
-#endif
+# endif
                                        addstr(&result, varvalue, strlen(varvalue));
                                } else {
-#if ENABLE_FEATURE_IFUPDOWN_IP
+# if ENABLE_FEATURE_IFUPDOWN_IP
                                        /* Sigh...  Add a special case for 'ip' to convert from
                                         * dotted quad to bit count style netmasks.  */
                                        if (strncmp(command, "bnmask", 6) == 0) {
@@ -284,7 +311,7 @@ static char *parse(const char *command, struct interface_defn_t *ifd)
                                                        }
                                                }
                                        }
-#endif
+# endif
                                        okay[opt_depth - 1] = 0;
                                }
 
@@ -295,13 +322,13 @@ static char *parse(const char *command, struct interface_defn_t *ifd)
        }
 
        if (opt_depth > 1) {
-               errno = EUNBALBRACK;
+               /* Unbalanced bracket */
                free(result);
                return NULL;
        }
 
        if (!okay[0]) {
-               errno = EUNDEFVAR;
+               /* Undefined variable and we aren't in a bracket */
                free(result);
                return NULL;
        }
@@ -329,56 +356,64 @@ static int execute(const char *command, struct interface_defn_t *ifd, execfn *ex
        }
        return 1;
 }
-#endif
+
+#endif /* FEATURE_IFUPDOWN_IPV4 || FEATURE_IFUPDOWN_IPV6 */
+
 
 #if ENABLE_FEATURE_IFUPDOWN_IPV6
+
 static int FAST_FUNC loopback_up6(struct interface_defn_t *ifd, execfn *exec)
 {
-#if ENABLE_FEATURE_IFUPDOWN_IP
+# if ENABLE_FEATURE_IFUPDOWN_IP
        int result;
        result = execute("ip addr add ::1 dev %iface%", ifd, exec);
        result += execute("ip link set %iface% up", ifd, exec);
        return ((result == 2) ? 2 : 0);
-#else
+# else
        return execute("ifconfig %iface% add ::1", ifd, exec);
-#endif
+# endif
 }
 
 static int FAST_FUNC loopback_down6(struct interface_defn_t *ifd, execfn *exec)
 {
-#if ENABLE_FEATURE_IFUPDOWN_IP
+# if ENABLE_FEATURE_IFUPDOWN_IP
        return execute("ip link set %iface% down", ifd, exec);
-#else
+# else
        return execute("ifconfig %iface% del ::1", ifd, exec);
-#endif
+# endif
+}
+
+static int FAST_FUNC manual_up_down6(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM)
+{
+       return 1;
 }
 
 static int FAST_FUNC static_up6(struct interface_defn_t *ifd, execfn *exec)
 {
        int result;
-#if ENABLE_FEATURE_IFUPDOWN_IP
+# if ENABLE_FEATURE_IFUPDOWN_IP
        result = execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd, exec);
        result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec);
        /* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */
-       result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec);
-#else
+       result += execute("[[ip route add ::/0 via %gateway%]][[ prio %metric%]]", ifd, exec);
+# else
        result = execute("ifconfig %iface%[[ media %media%]][[ hw %hwaddress%]][[ mtu %mtu%]] up", ifd, exec);
        result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec);
-       result += execute("[[route -A inet6 add ::/0 gw %gateway%]]", ifd, exec);
-#endif
+       result += execute("[[route -A inet6 add ::/0 gw %gateway%[[ metric %metric%]]]]", ifd, exec);
+# endif
        return ((result == 3) ? 3 : 0);
 }
 
 static int FAST_FUNC static_down6(struct interface_defn_t *ifd, execfn *exec)
 {
-#if ENABLE_FEATURE_IFUPDOWN_IP
+# if ENABLE_FEATURE_IFUPDOWN_IP
        return execute("ip link set %iface% down", ifd, exec);
-#else
+# else
        return execute("ifconfig %iface% down", ifd, exec);
-#endif
+# endif
 }
 
-#if ENABLE_FEATURE_IFUPDOWN_IP
+# if ENABLE_FEATURE_IFUPDOWN_IP
 static int FAST_FUNC v4tunnel_up(struct interface_defn_t *ifd, execfn *exec)
 {
        int result;
@@ -394,14 +429,15 @@ static int FAST_FUNC v4tunnel_down(struct interface_defn_t * ifd, execfn * exec)
 {
        return execute("ip tunnel del %iface%", ifd, exec);
 }
-#endif
+# endif
 
 static const struct method_t methods6[] = {
-#if ENABLE_FEATURE_IFUPDOWN_IP
-       { "v4tunnel", v4tunnel_up, v4tunnel_down, },
-#endif
-       { "static", static_up6, static_down6, },
-       { "loopback", loopback_up6, loopback_down6, },
+# if ENABLE_FEATURE_IFUPDOWN_IP
+       { "v4tunnel" , v4tunnel_up     , v4tunnel_down   , },
+# endif
+       { "static"   , static_up6      , static_down6    , },
+       { "manual"   , manual_up_down6 , manual_up_down6 , },
+       { "loopback" , loopback_up6    , loopback_down6  , },
 };
 
 static const struct address_family_t addr_inet6 = {
@@ -409,43 +445,46 @@ static const struct address_family_t addr_inet6 = {
        ARRAY_SIZE(methods6),
        methods6
 };
+
 #endif /* FEATURE_IFUPDOWN_IPV6 */
 
+
 #if ENABLE_FEATURE_IFUPDOWN_IPV4
+
 static int FAST_FUNC loopback_up(struct interface_defn_t *ifd, execfn *exec)
 {
-#if ENABLE_FEATURE_IFUPDOWN_IP
+# if ENABLE_FEATURE_IFUPDOWN_IP
        int result;
        result = execute("ip addr add 127.0.0.1/8 dev %iface%", ifd, exec);
        result += execute("ip link set %iface% up", ifd, exec);
        return ((result == 2) ? 2 : 0);
-#else
+# else
        return execute("ifconfig %iface% 127.0.0.1 up", ifd, exec);
-#endif
+# endif
 }
 
 static int FAST_FUNC loopback_down(struct interface_defn_t *ifd, execfn *exec)
 {
-#if ENABLE_FEATURE_IFUPDOWN_IP
+# if ENABLE_FEATURE_IFUPDOWN_IP
        int result;
        result = execute("ip addr flush dev %iface%", ifd, exec);
        result += execute("ip link set %iface% down", ifd, exec);
        return ((result == 2) ? 2 : 0);
-#else
+# else
        return execute("ifconfig %iface% 127.0.0.1 down", ifd, exec);
-#endif
+# endif
 }
 
 static int FAST_FUNC static_up(struct interface_defn_t *ifd, execfn *exec)
 {
        int result;
-#if ENABLE_FEATURE_IFUPDOWN_IP
+# if ENABLE_FEATURE_IFUPDOWN_IP
        result = execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] "
                        "dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd, exec);
        result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec);
-       result += execute("[[ip route add default via %gateway% dev %iface%]]", ifd, exec);
+       result += execute("[[ip route add default via %gateway% dev %iface%[[ prio %metric%]]]]", ifd, exec);
        return ((result == 3) ? 3 : 0);
-#else
+# else
        /* ifconfig said to set iface up before it processes hw %hwaddress%,
         * which then of course fails. Thus we run two separate ifconfig */
        result = execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up",
@@ -453,28 +492,28 @@ static int FAST_FUNC static_up(struct interface_defn_t *ifd, execfn *exec)
        result += execute("ifconfig %iface% %address% netmask %netmask%"
                                "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ",
                                ifd, exec);
-       result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec);
+       result += execute("[[route add default gw %gateway%[[ metric %metric%]] %iface%]]", ifd, exec);
        return ((result == 3) ? 3 : 0);
-#endif
+# endif
 }
 
 static int FAST_FUNC static_down(struct interface_defn_t *ifd, execfn *exec)
 {
        int result;
-#if ENABLE_FEATURE_IFUPDOWN_IP
+# if ENABLE_FEATURE_IFUPDOWN_IP
        result = execute("ip addr flush dev %iface%", ifd, exec);
        result += execute("ip link set %iface% down", ifd, exec);
-#else
+# else
        /* result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); */
        /* Bringing the interface down deletes the routes in itself.
           Otherwise this fails if we reference 'gateway' when using this from dhcp_down */
        result = 1;
        result += execute("ifconfig %iface% down", ifd, exec);
-#endif
+# endif
        return ((result == 2) ? 2 : 0);
 }
 
-#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
+# if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
 struct dhcp_client_t {
        const char *name;
        const char *startcmd;
@@ -483,7 +522,7 @@ struct dhcp_client_t {
 
 static const struct dhcp_client_t ext_dhcp_clients[] = {
        { "dhcpcd",
-               "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %clientid%]][[ -l %leasetime%]] %iface%",
+               "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %client%]][[ -l %leasetime%]] %iface%",
                "dhcpcd -k %iface%",
        },
        { "dhclient",
@@ -495,26 +534,26 @@ static const struct dhcp_client_t ext_dhcp_clients[] = {
                "pump -i %iface% -k",
        },
        { "udhcpc",
-               "udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %clientid%]]"
+               "udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %client%]]"
                                "[[ -s %script%]][[ %udhcpc_opts%]]",
                "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
        },
 };
-#endif /* ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCPC */
+# endif /* FEATURE_IFUPDOWN_EXTERNAL_DHCPC */
 
-#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
+# if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
 static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec)
 {
        unsigned i;
-#if ENABLE_FEATURE_IFUPDOWN_IP
+#  if ENABLE_FEATURE_IFUPDOWN_IP
        /* ip doesn't up iface when it configures it (unlike ifconfig) */
        if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec))
                return 0;
-#else
+#  else
        /* needed if we have hwaddress on dhcp iface */
        if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec))
                return 0;
-#endif
+#  endif
        for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
                if (exists_execable(ext_dhcp_clients[i].name))
                        return execute(ext_dhcp_clients[i].startcmd, ifd, exec);
@@ -522,31 +561,31 @@ static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec)
        bb_error_msg("no dhcp clients found");
        return 0;
 }
-#elif ENABLE_UDHCPC
+# elif ENABLE_UDHCPC
 static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec)
 {
-#if ENABLE_FEATURE_IFUPDOWN_IP
+#  if ENABLE_FEATURE_IFUPDOWN_IP
        /* ip doesn't up iface when it configures it (unlike ifconfig) */
        if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec))
                return 0;
-#else
+#  else
        /* needed if we have hwaddress on dhcp iface */
        if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec))
                return 0;
-#endif
+#  endif
        return execute("udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid "
-                       "-i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]][[ %udhcpc_opts%]]",
+                       "-i %iface%[[ -H %hostname%]][[ -c %client%]][[ -s %script%]][[ %udhcpc_opts%]]",
                        ifd, exec);
 }
-#else
+# else
 static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd UNUSED_PARAM,
                execfn *exec UNUSED_PARAM)
 {
        return 0; /* no dhcp support */
 }
-#endif
+# endif
 
-#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
+# if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
 static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec)
 {
        int result = 0;
@@ -569,7 +608,7 @@ static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec)
        result += static_down(ifd, exec);
        return ((result == 3) ? 3 : 0);
 }
-#elif ENABLE_UDHCPC
+# elif ENABLE_UDHCPC
 static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec)
 {
        int result;
@@ -586,13 +625,13 @@ static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec)
        result += static_down(ifd, exec);
        return ((result == 3) ? 3 : 0);
 }
-#else
+# else
 static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd UNUSED_PARAM,
                execfn *exec UNUSED_PARAM)
 {
        return 0; /* no dhcp support */
 }
-#endif
+# endif
 
 static int FAST_FUNC manual_up_down(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM)
 {
@@ -644,7 +683,8 @@ static const struct address_family_t addr_inet = {
        methods
 };
 
-#endif /* if ENABLE_FEATURE_IFUPDOWN_IPV4 */
+#endif  /* FEATURE_IFUPDOWN_IPV4 */
+
 
 /* Returns pointer to the next word, or NULL.
  * In 1st case, advances *buf to the word after this one.
@@ -703,7 +743,7 @@ static const struct method_t *get_method(const struct address_family_t *af, char
        return NULL;
 }
 
-static struct interfaces_file_t *read_interfaces(const char *filename)
+static struct interfaces_file_t *read_interfaces(const char *filename, struct interfaces_file_t *defn)
 {
        /* Let's try to be compatible.
         *
@@ -718,19 +758,25 @@ static struct interfaces_file_t *read_interfaces(const char *filename)
         * be ignored. Blank lines are ignored. Lines may be indented freely.
         * A "\" character at the very end of the line indicates the next line
         * should be treated as a continuation of the current one.
+        *
+        * Lines  beginning with "source" are used to include stanzas from
+        * other files, so configuration can be split into many files.
+        * The word "source" is followed by the path of file to be sourced.
         */
 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
        struct mapping_defn_t *currmap = NULL;
 #endif
        struct interface_defn_t *currif = NULL;
-       struct interfaces_file_t *defn;
        FILE *f;
        char *buf;
        char *first_word;
        char *rest_of_line;
        enum { NONE, IFACE, MAPPING } currently_processing = NONE;
 
-       defn = xzalloc(sizeof(*defn));
+       if (!defn)
+               defn = xzalloc(sizeof(*defn));
+
+       debug_noise("reading %s file:\n", filename);
        f = xfopen_for_read(filename);
 
        while ((buf = xmalloc_fgetline(f)) != NULL) {
@@ -763,7 +809,6 @@ static struct interfaces_file_t *read_interfaces(const char *filename)
                                currmap->match = xrealloc_vector(currmap->match, 4, currmap->n_matches);
                                currmap->match[currmap->n_matches++] = xstrdup(first_word);
                        }
-                       /*currmap->max_mappings = 0; - done by xzalloc */
                        /*currmap->n_mappings = 0;*/
                        /*currmap->mapping = NULL;*/
                        /*currmap->script = NULL;*/
@@ -842,29 +887,29 @@ static struct interfaces_file_t *read_interfaces(const char *filename)
                                debug_noise("\nauto %s\n", first_word);
                        }
                        currently_processing = NONE;
+               } else if (strcmp(first_word, "source") == 0) {
+                       read_interfaces(next_word(&rest_of_line), defn);
                } else {
                        switch (currently_processing) {
                        case IFACE:
                                if (rest_of_line[0] == '\0')
                                        bb_error_msg_and_die("option with empty value \"%s\"", buf);
 
-                               if (strcmp(first_word, "up") != 0
-                                && strcmp(first_word, "down") != 0
-                                && strcmp(first_word, "pre-up") != 0
-                                && strcmp(first_word, "post-down") != 0
-                               ) {
+                               if (strcmp(first_word, "post-up") == 0)
+                                       first_word += 5; /* "up" */
+                               else if (strcmp(first_word, "pre-down") == 0)
+                                       first_word += 4; /* "down" */
+
+                               /* If not one of "up", "down",... words... */
+                               if (index_in_strings(keywords_up_down, first_word) < 0) {
                                        int i;
                                        for (i = 0; i < currif->n_options; i++) {
                                                if (strcmp(currif->option[i].name, first_word) == 0)
                                                        bb_error_msg_and_die("duplicate option \"%s\"", buf);
                                        }
                                }
-                               if (currif->n_options >= currif->max_options) {
-                                       currif->max_options += 10;
-                                       currif->option = xrealloc(currif->option,
-                                               sizeof(*currif->option) * currif->max_options);
-                               }
                                debug_noise("\t%s=%s\n", first_word, rest_of_line);
+                               currif->option = xrealloc_vector(currif->option, 4, currif->n_options);
                                currif->option[currif->n_options].name = xstrdup(first_word);
                                currif->option[currif->n_options].value = xstrdup(rest_of_line);
                                currif->n_options++;
@@ -876,11 +921,7 @@ static struct interfaces_file_t *read_interfaces(const char *filename)
                                                bb_error_msg_and_die("duplicate script in mapping \"%s\"", buf);
                                        currmap->script = xstrdup(next_word(&rest_of_line));
                                } else if (strcmp(first_word, "map") == 0) {
-                                       if (currmap->n_mappings >= currmap->max_mappings) {
-                                               currmap->max_mappings = currmap->max_mappings * 2 + 1;
-                                               currmap->mapping = xrealloc(currmap->mapping,
-                                                       sizeof(char *) * currmap->max_mappings);
-                                       }
+                                       currmap->mapping = xrealloc_vector(currmap->mapping, 2, currmap->n_mappings);
                                        currmap->mapping[currmap->n_mappings] = xstrdup(next_word(&rest_of_line));
                                        currmap->n_mappings++;
                                } else {
@@ -901,6 +942,7 @@ static struct interfaces_file_t *read_interfaces(const char *filename)
                bb_error_msg_and_die("%s: I/O error", filename);
        }
        fclose(f);
+       debug_noise("\ndone reading %s\n\n", filename);
 
        return defn;
 }
@@ -927,7 +969,7 @@ static char *setlocalenv(const char *format, const char *name, const char *value
        return result;
 }
 
-static void set_environ(struct interface_defn_t *iface, const char *mode)
+static void set_environ(struct interface_defn_t *iface, const char *mode, const char *opt)
 {
        int i;
        char **pp;
@@ -940,15 +982,11 @@ static void set_environ(struct interface_defn_t *iface, const char *mode)
        }
 
        /* note: last element will stay NULL: */
-       G.my_environ = xzalloc(sizeof(char *) * (iface->n_options + 6));
+       G.my_environ = xzalloc(sizeof(char *) * (iface->n_options + 7));
        pp = G.my_environ;
 
        for (i = 0; i < iface->n_options; i++) {
-               if (strcmp(iface->option[i].name, "up") == 0
-                || strcmp(iface->option[i].name, "down") == 0
-                || strcmp(iface->option[i].name, "pre-up") == 0
-                || strcmp(iface->option[i].name, "post-down") == 0
-               ) {
+               if (index_in_strings(keywords_up_down, iface->option[i].name) >= 0) {
                        continue;
                }
                *pp++ = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value);
@@ -958,6 +996,7 @@ static void set_environ(struct interface_defn_t *iface, const char *mode)
        *pp++ = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name);
        *pp++ = setlocalenv("%s=%s", "METHOD", iface->method->name);
        *pp++ = setlocalenv("%s=%s", "MODE", mode);
+       *pp++ = setlocalenv("%s=%s", "PHASE", opt);
        if (G.startup_PATH)
                *pp++ = setlocalenv("%s=%s", "PATH", G.startup_PATH);
 }
@@ -973,11 +1012,10 @@ static int doit(char *str)
 
                fflush_all();
                child = vfork();
-               switch (child) {
-               case -1: /* failure */
+               if (child < 0) /* failure */
                        return 0;
-               case 0: /* child */
-                       execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, (char *) NULL, G.my_environ);
+               if (child == 0) { /* child */
+                       execle(G.shell, G.shell, "-c", str, (char *) NULL, G.my_environ);
                        _exit(127);
                }
                safe_waitpid(child, &status, 0);
@@ -1013,19 +1051,21 @@ static int check(char *str)
 static int iface_up(struct interface_defn_t *iface)
 {
        if (!iface->method->up(iface, check)) return -1;
-       set_environ(iface, "start");
+       set_environ(iface, "start", "pre-up");
        if (!execute_all(iface, "pre-up")) return 0;
        if (!iface->method->up(iface, doit)) return 0;
+       set_environ(iface, "start", "post-up");
        if (!execute_all(iface, "up")) return 0;
        return 1;
 }
 
 static int iface_down(struct interface_defn_t *iface)
 {
-       if (!iface->method->down(iface,check)) return -1;
-       set_environ(iface, "stop");
+       if (!iface->method->down(iface, check)) return -1;
+       set_environ(iface, "stop", "pre-down");
        if (!execute_all(iface, "down")) return 0;
        if (!iface->method->down(iface, doit)) return 0;
+       set_environ(iface, "stop", "post-down");
        if (!execute_all(iface, "post-down")) return 0;
        return 1;
 }
@@ -1152,6 +1192,7 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
        INIT_G();
 
        G.startup_PATH = getenv("PATH");
+       G.shell = xstrdup(get_shell_name());
 
        cmds = iface_down;
        if (applet_name[2] == 'u') {
@@ -1167,9 +1208,7 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
                if (!DO_ALL) bb_show_usage();
        }
 
-       debug_noise("reading %s file:\n", interfaces);
-       defn = read_interfaces(interfaces);
-       debug_noise("\ndone reading %s\n\n", interfaces);
+       defn = read_interfaces(interfaces, NULL);
 
        /* Create a list of interfaces to work on */
        if (DO_ALL) {
@@ -1207,13 +1246,13 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
                                /* ifup */
                                if (iface_state) {
                                        bb_error_msg("interface %s already configured", iface);
-                                       continue;
+                                       goto next;
                                }
                        } else {
                                /* ifdown */
                                if (!iface_state) {
                                        bb_error_msg("interface %s not configured", iface);
-                                       continue;
+                                       goto next;
                                }
                        }
                        llist_free(state_list, free);
@@ -1277,9 +1316,9 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
                        llist_t *state_list = read_iface_state();
                        llist_t *iface_state = find_iface_state(state_list, iface);
 
-                       if (cmds == iface_up) {
-                               char * const newiface = xasprintf("%s=%s", iface, liface);
-                               if (iface_state == NULL) {
+                       if (cmds == iface_up && !any_failures) {
+                               char *newiface = xasprintf("%s=%s", iface, liface);
+                               if (!iface_state) {
                                        llist_add_to_end(&state_list, newiface);
                                } else {
                                        free(iface_state->data);
@@ -1303,6 +1342,9 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
                        fclose(state_fp);
                        llist_free(state_list, free);
                }
+ next:
+               free(iface);
+               free(liface);
        }
 
        return any_failures;
index 7030062..584c5e5 100644 (file)
  *                      setuid()
  */
 
+//usage:#define inetd_trivial_usage
+//usage:       "[-fe] [-q N] [-R N] [CONFFILE]"
+//usage:#define inetd_full_usage "\n\n"
+//usage:       "Listen for network connections and launch programs\n"
+//usage:     "\n       -f      Run in foreground"
+//usage:     "\n       -e      Log to stderr"
+//usage:     "\n       -q N    Socket listen queue (default: 128)"
+//usage:     "\n       -R N    Pause services after N connects/min"
+//usage:     "\n               (default: 0 - disabled)"
+
 #include <syslog.h>
+#include <sys/resource.h> /* setrlimit */
+#include <sys/socket.h> /* un.h may need this */
 #include <sys/un.h>
 
 #include "libbb.h"
 
 #if ENABLE_FEATURE_INETD_RPC
-#include <rpc/rpc.h>
-#include <rpc/pmap_clnt.h>
+# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
+#  error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
+# endif
+# include <rpc/rpc.h>
+# include <rpc/pmap_clnt.h>
 #endif
 
 #if !BB_MMU
 #define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN 0
 #endif
 
-#define _PATH_INETDPID  "/var/run/inetd.pid"
-
 #define CNT_INTERVAL    60      /* servers in CNT_INTERVAL sec. */
 #define RETRYTIME       60      /* retry after bind or server fail */
 
@@ -295,7 +308,7 @@ struct globals {
        struct rlimit rlim_ofile;
        servtab_t *serv_list;
        int global_queuelen;
-       int maxsock;            /* max fd# in allsock, -1: unknown */
+       int maxsock;         /* max fd# in allsock, -1: unknown */
        /* whenever maxsock grows, prev_maxsock is set to new maxsock,
         * but if maxsock is set to -1, prev_maxsock is not changed */
        int prev_maxsock;
@@ -344,10 +357,26 @@ struct BUG_G_too_big {
        config_filename = "/etc/inetd.conf"; \
 } while (0)
 
+#if 1
+# define dbg(...) ((void)0)
+#else
+# define dbg(...) \
+do { \
+       int dbg_fd = open("inetd_debug.log", O_WRONLY | O_CREAT | O_APPEND, 0666); \
+       if (dbg_fd >= 0) { \
+               fdprintf(dbg_fd, "%d: ", getpid()); \
+               fdprintf(dbg_fd, __VA_ARGS__); \
+               close(dbg_fd); \
+       } \
+} while (0)
+#endif
+
 static void maybe_close(int fd)
 {
-       if (fd >= 0)
+       if (fd >= 0) {
                close(fd);
+               dbg("closed fd:%d\n", fd);
+       }
 }
 
 // TODO: move to libbb?
@@ -451,7 +480,9 @@ static void remove_fd_from_set(int fd)
 {
        if (fd >= 0) {
                FD_CLR(fd, &allsock);
+               dbg("stopped listening on fd:%d\n", fd);
                maxsock = -1;
+               dbg("maxsock:%d\n", maxsock);
        }
 }
 
@@ -459,8 +490,10 @@ static void add_fd_to_set(int fd)
 {
        if (fd >= 0) {
                FD_SET(fd, &allsock);
+               dbg("started listening on fd:%d\n", fd);
                if (maxsock >= 0 && fd > maxsock) {
                        prev_maxsock = maxsock = fd;
+                       dbg("maxsock:%d\n", maxsock);
                        if ((rlim_t)fd > rlim_ofile_cur - FD_MARGIN)
                                bump_nofile();
                }
@@ -479,6 +512,7 @@ static void recalculate_maxsock(void)
                        maxsock = fd;
                fd++;
        }
+       dbg("recalculated maxsock:%d\n", maxsock);
        prev_maxsock = maxsock;
        if ((rlim_t)maxsock > rlim_ofile_cur - FD_MARGIN)
                bump_nofile();
@@ -501,7 +535,7 @@ static void prepare_socket_fd(servtab_t *sep)
 
                /* zero out the port for all RPC services; let bind()
                 * find one. */
-               set_nport(sep->se_lsa, 0);
+               set_nport(&sep->se_lsa->u.sa, 0);
 
                /* for RPC services, attempt to use a reserved port
                 * if they are going to be running as root. */
@@ -536,8 +570,13 @@ static void prepare_socket_fd(servtab_t *sep)
                rearm_alarm();
                return;
        }
-       if (sep->se_socktype == SOCK_STREAM)
+
+       if (sep->se_socktype == SOCK_STREAM) {
                listen(fd, global_queuelen);
+               dbg("new sep->se_fd:%d (stream)\n", fd);
+       } else {
+               dbg("new sep->se_fd:%d (!stream)\n", fd);
+       }
 
        add_fd_to_set(fd);
        sep->se_fd = fd;
@@ -778,6 +817,12 @@ static servtab_t *parse_one_line(void)
        argc = 0;
        while ((arg = token[6+argc]) != NULL && argc < MAXARGV)
                sep->se_argv[argc++] = xstrdup(arg);
+       /* Some inetd.conf files have no argv's, not even argv[0].
+        * Fix them up.
+        * (Technically, programs can be execed with argv[0] = NULL,
+        * but many programs do not like that at all) */
+       if (argc == 0)
+               sep->se_argv[0] = xstrdup(sep->se_program);
 
        /* catch mixups. "<service> stream udp ..." == wtf */
        if (sep->se_socktype == SOCK_STREAM) {
@@ -953,7 +998,7 @@ static void reread_config_file(int sig UNUSED_PARAM)
                        }
                        if (LONE_CHAR(sep->se_local_hostname, '*')) {
                                lsa = xzalloc_lsa(sep->se_family);
-                               set_nport(lsa, port);
+                               set_nport(&lsa->u.sa, port);
                        } else {
                                lsa = host_and_af2sockaddr(sep->se_local_hostname,
                                                ntohs(port), sep->se_family);
@@ -993,7 +1038,7 @@ static void reread_config_file(int sig UNUSED_PARAM)
         * new config file doesnt have them. */
        block_CHLD_HUP_ALRM(&omask);
        sepp = &serv_list;
-       while ((sep = *sepp)) {
+       while ((sep = *sepp) != NULL) {
                if (sep->se_checked) {
                        sepp = &sep->se_next;
                        continue;
@@ -1085,7 +1130,7 @@ static void clean_up_and_exit(int sig UNUSED_PARAM)
                if (ENABLE_FEATURE_CLEAN_UP)
                        close(sep->se_fd);
        }
-       remove_pidfile(_PATH_INETDPID);
+       remove_pidfile(CONFIG_PID_FILE_PATH "/inetd.pid");
        exit(EXIT_SUCCESS);
 }
 
@@ -1134,7 +1179,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
                setgroups(1, &gid);
        }
 
-       write_pidfile(_PATH_INETDPID);
+       write_pidfile(CONFIG_PID_FILE_PATH "/inetd.pid");
 
        /* never fails under Linux (except if you pass it bad arguments) */
        getrlimit(RLIMIT_NOFILE, &rlim_ofile);
@@ -1147,12 +1192,17 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
        sigaddset(&sa.sa_mask, SIGALRM);
        sigaddset(&sa.sa_mask, SIGCHLD);
        sigaddset(&sa.sa_mask, SIGHUP);
+//FIXME: explain why no SA_RESTART
+//FIXME: retry_network_setup is unsafe to run in signal handler (many reasons)!
        sa.sa_handler = retry_network_setup;
        sigaction_set(SIGALRM, &sa);
+//FIXME: reread_config_file is unsafe to run in signal handler(many reasons)!
        sa.sa_handler = reread_config_file;
        sigaction_set(SIGHUP, &sa);
+//FIXME: reap_child is unsafe to run in signal handler (uses stdio)!
        sa.sa_handler = reap_child;
        sigaction_set(SIGCHLD, &sa);
+//FIXME: clean_up_and_exit is unsafe to run in signal handler (uses stdio)!
        sa.sa_handler = clean_up_and_exit;
        sigaction_set(SIGTERM, &sa);
        sa.sa_handler = clean_up_and_exit;
@@ -1182,11 +1232,13 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
                        }
                        continue;
                }
+               dbg("ready_fd_cnt:%d\n", ready_fd_cnt);
 
                for (sep = serv_list; ready_fd_cnt && sep; sep = sep->se_next) {
                        if (sep->se_fd == -1 || !FD_ISSET(sep->se_fd, &readable))
                                continue;
 
+                       dbg("ready fd:%d\n", sep->se_fd);
                        ready_fd_cnt--;
                        ctrl = sep->se_fd;
                        accepted_fd = -1;
@@ -1194,6 +1246,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
                        if (!sep->se_wait) {
                                if (sep->se_socktype == SOCK_STREAM) {
                                        ctrl = accepted_fd = accept(sep->se_fd, NULL, NULL);
+                                       dbg("accepted_fd:%d\n", accepted_fd);
                                        if (ctrl < 0) {
                                                if (errno != EINTR)
                                                        bb_perror_msg("accept (for %s)", sep->se_service);
@@ -1214,19 +1267,22 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
  * (can create many copies of same child, etc).
  * Parent must create and use new socket instead. */
                                        new_udp_fd = socket(sep->se_family, SOCK_DGRAM, 0);
+                                       dbg("new_udp_fd:%d\n", new_udp_fd);
                                        if (new_udp_fd < 0) { /* error: eat packet, forget about it */
  udp_err:
                                                recv(sep->se_fd, line, LINE_SIZE, MSG_DONTWAIT);
                                                continue;
                                        }
                                        setsockopt_reuseaddr(new_udp_fd);
-                                       /* TODO: better do bind after vfork in parent,
+                                       /* TODO: better do bind after fork in parent,
                                         * so that we don't have two wildcard bound sockets
                                         * even for a brief moment? */
                                        if (bind(new_udp_fd, &sep->se_lsa->u.sa, sep->se_lsa->len) < 0) {
+                                               dbg("bind(new_udp_fd) failed\n");
                                                close(new_udp_fd);
                                                goto udp_err;
                                        }
+                                       dbg("bind(new_udp_fd) succeeded\n");
                                }
                        }
 
@@ -1254,6 +1310,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
                                                        sep->se_count = 0;
                                                        rearm_alarm(); /* will revive it in RETRYTIME sec */
                                                        restore_sigmask(&omask);
+                                                       maybe_close(new_udp_fd);
                                                        maybe_close(accepted_fd);
                                                        continue; /* -> check next fd in fd set */
                                                }
@@ -1274,17 +1331,18 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
                                        bb_perror_msg("vfork"+1);
                                        sleep(1);
                                        restore_sigmask(&omask);
+                                       maybe_close(new_udp_fd);
                                        maybe_close(accepted_fd);
                                        continue; /* -> check next fd in fd set */
                                }
                                if (pid == 0)
                                        pid--; /* -1: "we did fork and we are child" */
                        }
-                       /* if pid == 0 here, we never forked */
+                       /* if pid == 0 here, we didn't fork */
 
                        if (pid > 0) { /* parent */
                                if (sep->se_wait) {
-                                       /* tcp wait: we passed listening socket to child,
+                                       /* wait: we passed socket to child,
                                         * will wait for child to terminate */
                                        sep->se_wait = pid;
                                        remove_fd_from_set(sep->se_fd);
@@ -1293,17 +1351,19 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
                                        /* udp nowait: child connected the socket,
                                         * we created and will use new, unconnected one */
                                        xmove_fd(new_udp_fd, sep->se_fd);
+                                       dbg("moved new_udp_fd:%d to sep->se_fd:%d\n", new_udp_fd, sep->se_fd);
                                }
                                restore_sigmask(&omask);
                                maybe_close(accepted_fd);
                                continue; /* -> check next fd in fd set */
                        }
 
-                       /* we are either child or didn't vfork at all */
+                       /* we are either child or didn't fork at all */
 #ifdef INETD_BUILTINS_ENABLED
                        if (sep->se_builtin) {
-                               if (pid) { /* "pid" is -1: we did vfork */
+                               if (pid) { /* "pid" is -1: we did fork */
                                        close(sep->se_fd); /* listening socket */
+                                       dbg("closed sep->se_fd:%d\n", sep->se_fd);
                                        logmode = LOGMODE_NONE; /* make xwrite etc silent */
                                }
                                restore_sigmask(&omask);
@@ -1311,7 +1371,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
                                        sep->se_builtin->bi_stream_fn(ctrl, sep);
                                else
                                        sep->se_builtin->bi_dgram_fn(ctrl, sep);
-                               if (pid) /* we did vfork */
+                               if (pid) /* we did fork */
                                        _exit(EXIT_FAILURE);
                                maybe_close(accepted_fd);
                                continue; /* -> check next fd in fd set */
@@ -1321,9 +1381,14 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
                        setsid();
                        /* "nowait" udp */
                        if (new_udp_fd >= 0) {
-                               len_and_sockaddr *lsa = xzalloc_lsa(sep->se_family);
+                               len_and_sockaddr *lsa;
+                               int r;
+
+                               close(new_udp_fd);
+                               dbg("closed new_udp_fd:%d\n", new_udp_fd);
+                               lsa = xzalloc_lsa(sep->se_family);
                                /* peek at the packet and remember peer addr */
-                               int r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT,
+                               r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT,
                                        &lsa->u.sa, &lsa->len);
                                if (r < 0)
                                        goto do_exit1;
@@ -1331,6 +1396,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
                                 * only packets from this peer will be recv'ed,
                                 * and bare write()/send() will work on it */
                                connect(ctrl, &lsa->u.sa, lsa->len);
+                               dbg("connected ctrl:%d to remote peer\n", ctrl);
                                free(lsa);
                        }
                        /* prepare env and exec program */
@@ -1348,7 +1414,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
                                bb_error_msg("non-root must run services as himself");
                                goto do_exit1;
                        }
-                       if (pwd->pw_uid) {
+                       if (pwd->pw_uid != 0) {
                                if (sep->se_group)
                                        pwd->pw_gid = grp->gr_gid;
                                /* initgroups, setgid, setuid: */
@@ -1367,6 +1433,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
                         */
                        xmove_fd(ctrl, STDIN_FILENO);
                        xdup2(STDIN_FILENO, STDOUT_FILENO);
+                       dbg("moved ctrl:%d to fd 0,1[,2]\n", ctrl);
                        /* manpages of inetd I managed to find either say
                         * that stderr is also redirected to the network,
                         * or do not talk about redirection at all (!) */
@@ -1379,6 +1446,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
                                        maybe_close(sep2->se_fd);
                        sigaction_set(SIGPIPE, &saved_pipe_handler);
                        restore_sigmask(&omask);
+                       dbg("execing:'%s'\n", sep->se_program);
                        BB_EXECVP(sep->se_program, sep->se_argv);
                        bb_perror_msg("can't execute '%s'", sep->se_program);
  do_exit1:
index 659ac36..bf7d2b1 100644 (file)
@@ -19,7 +19,7 @@
  * Author:      Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
  *              and others.  Copyright 1993 MicroWalt Corporation
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Patched to support 'add' and 'del' keywords for INET(4) addresses
  * by Mrs. Brisby <mrs.brisby@nimh.org>
  * {1.34} - 19980630 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
  *                     - gettext instead of catgets for i18n
  *          10/1998  - Andi Kleen. Use interface list primitives.
- *         20001008 - Bernd Eckenfels, Patch from RH for setting mtu
+ *          20001008 - Bernd Eckenfels, Patch from RH for setting mtu
  *                     (default AF was wrong)
  */
+
+#include "libbb.h"
+#include "inet_common.h"
 #include <net/if.h>
 #include <net/if_arp.h>
-#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION)
+#ifdef HAVE_NET_ETHERNET_H
 # include <net/ethernet.h>
-#else
-# include <linux/if_ether.h>
 #endif
-#include "libbb.h"
-#include "inet_common.h"
 
 #if ENABLE_FEATURE_HWIB
 /* #include <linux/if_infiniband.h> */
@@ -723,68 +722,15 @@ static char* FAST_FUNC ether_print(unsigned char *ptr)
        return buff;
 }
 
-static int FAST_FUNC ether_input(const char *bufp, struct sockaddr *sap);
-
 static const struct hwtype ether_hwtype = {
        .name  = "ether",
        .title = "Ethernet",
        .type  = ARPHRD_ETHER,
        .alen  = ETH_ALEN,
        .print = ether_print,
-       .input = ether_input
+       .input = in_ether
 };
 
-static unsigned hexchar2int(char c)
-{
-       if (isdigit(c))
-               return c - '0';
-       c &= ~0x20; /* a -> A */
-       if ((unsigned)(c - 'A') <= 5)
-               return c - ('A' - 10);
-       return ~0U;
-}
-
-/* Input an Ethernet address and convert to binary. */
-static int FAST_FUNC ether_input(const char *bufp, struct sockaddr *sap)
-{
-       unsigned char *ptr;
-       char c;
-       int i;
-       unsigned val;
-
-       sap->sa_family = ether_hwtype.type;
-       ptr = (unsigned char*) sap->sa_data;
-
-       i = 0;
-       while ((*bufp != '\0') && (i < ETH_ALEN)) {
-               val = hexchar2int(*bufp++) * 0x10;
-               if (val > 0xff) {
-                       errno = EINVAL;
-                       return -1;
-               }
-               c = *bufp;
-               if (c == ':' || c == 0)
-                       val >>= 4;
-               else {
-                       val |= hexchar2int(c);
-                       if (val > 0xff) {
-                               errno = EINVAL;
-                               return -1;
-                       }
-               }
-               if (c != 0)
-                       bufp++;
-               *ptr++ = (unsigned char) val;
-               i++;
-
-               /* We might get a semicolon here - not required. */
-               if (*bufp == ':') {
-                       bufp++;
-               }
-       }
-       return 0;
-}
-
 static const struct hwtype ppp_hwtype = {
        .name =         "ppp",
        .title =        "Point-to-Point Protocol",
@@ -927,9 +873,8 @@ static void print_bytes_scaled(unsigned long long ull, const char *end)
 
 static void ife_print6(struct interface *ptr)
 {
-
        FILE *f;
-       char addr6[40], devname[20];
+       char addr6[40], devname[21];
        struct sockaddr_in6 sap;
        int plen, scope, dad_status, if_idx;
        char addr6p[8][5];
@@ -952,8 +897,8 @@ static void ife_print6(struct interface *ptr)
                                          (struct sockaddr *) &sap.sin6_addr);
                        sap.sin6_family = AF_INET6;
                        printf("          inet6 addr: %s/%d",
-                                  INET6_sprint((struct sockaddr *) &sap, 1),
-                                  plen);
+                               INET6_sprint((struct sockaddr *) &sap, 1),
+                               plen);
                        printf(" Scope:");
                        switch (scope & IPV6_ADDR_SCOPE_MASK) {
                        case 0:
@@ -1021,7 +966,7 @@ static void ife_print(struct interface *ptr)
 
        if (ptr->has_ip) {
                printf("          %s addr:%s ", ap->name,
-                          ap->sprint(&ptr->addr, 1));
+                       ap->sprint(&ptr->addr, 1));
                if (ptr->flags & IFF_POINTOPOINT) {
                        printf(" P-t-P:%s ", ap->sprint(&ptr->dstaddr, 1));
                }
@@ -1104,17 +1049,17 @@ static void ife_print(struct interface *ptr)
                printf("          ");
 
                printf("RX packets:%llu errors:%lu dropped:%lu overruns:%lu frame:%lu\n",
-                          ptr->stats.rx_packets, ptr->stats.rx_errors,
-                          ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors,
-                          ptr->stats.rx_frame_errors);
+                       ptr->stats.rx_packets, ptr->stats.rx_errors,
+                       ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors,
+                       ptr->stats.rx_frame_errors);
                if (can_compress)
                        printf("             compressed:%lu\n",
-                                  ptr->stats.rx_compressed);
+                               ptr->stats.rx_compressed);
                printf("          ");
                printf("TX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n",
-                          ptr->stats.tx_packets, ptr->stats.tx_errors,
-                          ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors,
-                          ptr->stats.tx_carrier_errors);
+                       ptr->stats.tx_packets, ptr->stats.tx_errors,
+                       ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors,
+                       ptr->stats.tx_carrier_errors);
                printf("          collisions:%lu ", ptr->stats.collisions);
                if (can_compress)
                        printf("compressed:%lu ", ptr->stats.tx_compressed);
@@ -1131,13 +1076,12 @@ static void ife_print(struct interface *ptr)
                printf("          ");
                if (ptr->map.irq)
                        printf("Interrupt:%d ", ptr->map.irq);
-               if (ptr->map.base_addr >= 0x100)        /* Only print devices using it for
-                                                                                          I/O maps */
+               if (ptr->map.base_addr >= 0x100) /* Only print devices using it for I/O maps */
                        printf("Base address:0x%lx ",
-                                  (unsigned long) ptr->map.base_addr);
+                               (unsigned long) ptr->map.base_addr);
                if (ptr->map.mem_start) {
                        printf("Memory:%lx-%lx ", ptr->map.mem_start,
-                                  ptr->map.mem_end);
+                               ptr->map.mem_end);
                }
                if (ptr->map.dma)
                        printf("DMA chan:%x ", ptr->map.dma);
@@ -1170,7 +1114,7 @@ static struct interface *lookup_interface(char *name)
 
 #ifdef UNUSED
 static int for_all_interfaces(int (*doit) (struct interface *, void *),
-                                                         void *cookie)
+                                                       void *cookie)
 {
        struct interface *ife;
 
index 3a99fa3..98fe621 100644 (file)
@@ -1,16 +1,86 @@
 /* vi: set sw=4 ts=4: */
 /*
- * "ip" utility frontend.
- *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
  * Changes:
- * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
  * Bernhard Reutner-Fischer rewrote to use index_in_substr_array
  */
 
+/* would need to make the " | " optional depending on more than one selected: */
+//usage:#define ip_trivial_usage
+//usage:       "[OPTIONS] {"
+//usage:       IF_FEATURE_IP_ADDRESS("address | ")
+//usage:       IF_FEATURE_IP_ROUTE("route | ")
+//usage:       IF_FEATURE_IP_LINK("link | ")
+//usage:       IF_FEATURE_IP_TUNNEL("tunnel | ")
+//usage:       IF_FEATURE_IP_RULE("rule")
+//usage:       "} {COMMAND}"
+//usage:#define ip_full_usage "\n\n"
+//usage:       "ip [OPTIONS] OBJECT {COMMAND}\n"
+//usage:       "where OBJECT := {"
+//usage:       IF_FEATURE_IP_ADDRESS("address | ")
+//usage:       IF_FEATURE_IP_ROUTE("route | ")
+//usage:       IF_FEATURE_IP_LINK("link | ")
+//usage:       IF_FEATURE_IP_TUNNEL("tunnel | ")
+//usage:       IF_FEATURE_IP_RULE("rule")
+//usage:       "}\n"
+//usage:       "OPTIONS := { -f[amily] { inet | inet6 | link } | -o[neline] }"
+//usage:
+//usage:#define ipaddr_trivial_usage
+//usage:       "{ {add|del} IFADDR dev STRING | {show|flush}\n"
+//usage:       "               [dev STRING] [to PREFIX] }"
+//usage:#define ipaddr_full_usage "\n\n"
+//usage:       "ipaddr {add|delete} IFADDR dev STRING\n"
+//usage:       "ipaddr {show|flush} [dev STRING] [scope SCOPE-ID]\n"
+//usage:       "       [to PREFIX] [label PATTERN]\n"
+//usage:       "       IFADDR := PREFIX | ADDR peer PREFIX\n"
+//usage:       "       [broadcast ADDR] [anycast ADDR]\n"
+//usage:       "       [label STRING] [scope SCOPE-ID]\n"
+//usage:       "       SCOPE-ID := [host | link | global | NUMBER]"
+//usage:
+//usage:#define iplink_trivial_usage
+//usage:       "{ set DEVICE { up | down | arp { on | off } | show [DEVICE] }"
+//usage:#define iplink_full_usage "\n\n"
+//usage:       "iplink set DEVICE { up | down | arp | multicast { on | off } |\n"
+//usage:       "                       dynamic { on | off } |\n"
+//usage:       "                       mtu MTU }\n"
+//usage:       "iplink show [DEVICE]"
+//usage:
+//usage:#define iproute_trivial_usage
+//usage:       "{ list | flush | add | del | change | append |\n"
+//usage:       "               replace | test } ROUTE"
+//usage:#define iproute_full_usage "\n\n"
+//usage:       "iproute { list | flush } SELECTOR\n"
+//usage:       "iproute get ADDRESS [from ADDRESS iif STRING]\n"
+//usage:       "       [oif STRING] [tos TOS]\n"
+//usage:       "iproute { add | del | change | append | replace | test } ROUTE\n"
+//usage:       "       SELECTOR := [root PREFIX] [match PREFIX] [proto RTPROTO]\n"
+//usage:       "       ROUTE := [TYPE] PREFIX [tos TOS] [proto RTPROTO] [metric METRIC]"
+//usage:
+//usage:#define iprule_trivial_usage
+//usage:       "{[list | add | del] RULE}"
+//usage:#define iprule_full_usage "\n\n"
+//usage:       "iprule [list | add | del] SELECTOR ACTION\n"
+//usage:       "       SELECTOR := [from PREFIX] [to PREFIX] [tos TOS] [fwmark FWMARK]\n"
+//usage:       "                       [dev STRING] [pref NUMBER]\n"
+//usage:       "       ACTION := [table TABLE_ID] [nat ADDRESS]\n"
+//usage:       "                       [prohibit | reject | unreachable]\n"
+//usage:       "                       [realms [SRCREALM/]DSTREALM]\n"
+//usage:       "       TABLE_ID := [local | main | default | NUMBER]"
+//usage:
+//usage:#define iptunnel_trivial_usage
+//usage:       "{ add | change | del | show } [NAME]\n"
+//usage:       "       [mode { ipip | gre | sit }]\n"
+//usage:       "       [remote ADDR] [local ADDR] [ttl TTL]"
+//usage:#define iptunnel_full_usage "\n\n"
+//usage:       "iptunnel { add | change | del | show } [NAME]\n"
+//usage:       "       [mode { ipip | gre | sit }] [remote ADDR] [local ADDR]\n"
+//usage:       "       [[i|o]seq] [[i|o]key KEY] [[i|o]csum]\n"
+//usage:       "       [ttl TTL] [tos TOS] [[no]pmtudisc] [dev PHYS_DEV]"
+
 #include "libbb.h"
 
 #include "libiproute/utils.h"
  || ENABLE_FEATURE_IP_TUNNEL \
  || ENABLE_FEATURE_IP_RULE
 
-static int ip_print_help(char **argv UNUSED_PARAM)
+static int FAST_FUNC ip_print_help(char **argv UNUSED_PARAM)
 {
        bb_show_usage();
 }
 
-typedef int (*ip_func_ptr_t)(char**);
+typedef int FAST_FUNC (*ip_func_ptr_t)(char**);
 
 static int ip_do(ip_func_ptr_t ip_func, char **argv)
 {
index 265009a..3c8b8bf 100644 (file)
@@ -9,15 +9,41 @@
  * from Red Hat.  I didn't look at their source code, but there
  * is no denying that this is a loving reimplementation
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define ipcalc_trivial_usage
+//usage:       "[OPTIONS] ADDRESS[[/]NETMASK] [NETMASK]"
+//usage:#define ipcalc_full_usage "\n\n"
+//usage:       "Calculate IP network settings from a IP address\n"
+//usage:       IF_FEATURE_IPCALC_LONG_OPTIONS(
+//usage:     "\n       -b,--broadcast  Display calculated broadcast address"
+//usage:     "\n       -n,--network    Display calculated network address"
+//usage:     "\n       -m,--netmask    Display default netmask for IP"
+//usage:       IF_FEATURE_IPCALC_FANCY(
+//usage:     "\n       -p,--prefix     Display the prefix for IP/NETMASK"
+//usage:     "\n       -h,--hostname   Display first resolved host name"
+//usage:     "\n       -s,--silent     Don't ever display error messages"
+//usage:       )
+//usage:       )
+//usage:       IF_NOT_FEATURE_IPCALC_LONG_OPTIONS(
+//usage:     "\n       -b      Display calculated broadcast address"
+//usage:     "\n       -n      Display calculated network address"
+//usage:     "\n       -m      Display default netmask for IP"
+//usage:       IF_FEATURE_IPCALC_FANCY(
+//usage:     "\n       -p      Display the prefix for IP/NETMASK"
+//usage:     "\n       -h      Display first resolved host name"
+//usage:     "\n       -s      Don't ever display error messages"
+//usage:       )
+//usage:       )
+
 #include "libbb.h"
 /* After libbb.h, because on some systems it needs other includes */
 #include <arpa/inet.h>
 
-#define CLASS_A_NETMASK        ntohl(0xFF000000)
-#define CLASS_B_NETMASK        ntohl(0xFFFF0000)
-#define CLASS_C_NETMASK        ntohl(0xFFFFFF00)
+#define CLASS_A_NETMASK ntohl(0xFF000000)
+#define CLASS_B_NETMASK ntohl(0xFFFF0000)
+#define CLASS_C_NETMASK ntohl(0xFFFFFF00)
 
 static unsigned long get_netmask(unsigned long ipaddr)
 {
index 66bb371..1c6491e 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Copyright (C) 2007 Denys Vlasenko
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index f20714d..4c7e01d 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Copyright (C) 2007 Denys Vlasenko
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
index e8ba007..a41405c 100644 (file)
@@ -4,9 +4,19 @@
  *
  * Copyright (C) 2007 Denys Vlasenko
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#define fakeidentd_trivial_usage
+//usage:       "[-fiw] [-b ADDR] [STRING]"
+//usage:#define fakeidentd_full_usage "\n\n"
+//usage:       "Provide fake ident (auth) service\n"
+//usage:     "\n       -f      Run in foreground"
+//usage:     "\n       -i      Inetd mode"
+//usage:     "\n       -w      Inetd 'wait' mode"
+//usage:     "\n       -b ADDR Bind to specified address"
+//usage:     "\n       STRING  Ident answer string (default: nobody)"
+
 #include "libbb.h"
 #include <syslog.h>
 #include "isrv.h"
index b0aa50a..7c78f3c 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+# Licensed under GPLv2 or later, see file LICENSE in this source tree.
 #
 
 lib-y:=
index aef3252..30c7e59 100644 (file)
 
 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 
-extern char **ip_parse_common_args(char **argv);
-extern int print_neigh(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
-extern int ipaddr_list_or_flush(char **argv, int flush);
-extern int iproute_monitor(char **argv);
-extern void iplink_usage(void) NORETURN;
-extern void ipneigh_reset_filter(void);
+char FAST_FUNC **ip_parse_common_args(char **argv);
+//int FAST_FUNC print_neigh(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+int FAST_FUNC ipaddr_list_or_flush(char **argv, int flush);
+//int FAST_FUNC iproute_monitor(char **argv);
+//void FAST_FUNC ipneigh_reset_filter(void);
 
-extern int do_ipaddr(char **argv);
-extern int do_iproute(char **argv);
-extern int do_iprule(char **argv);
-extern int do_ipneigh(char **argv);
-extern int do_iptunnel(char **argv);
-extern int do_iplink(char **argv);
-extern int do_ipmonitor(char **argv);
-extern int do_multiaddr(char **argv);
-extern int do_multiroute(char **argv);
+int FAST_FUNC do_ipaddr(char **argv);
+int FAST_FUNC do_iproute(char **argv);
+int FAST_FUNC do_iprule(char **argv);
+//int FAST_FUNC do_ipneigh(char **argv);
+int FAST_FUNC do_iptunnel(char **argv);
+int FAST_FUNC do_iplink(char **argv);
+//int FAST_FUNC do_ipmonitor(char **argv);
+//int FAST_FUNC do_multiaddr(char **argv);
+//int FAST_FUNC do_multiroute(char **argv);
 
 POP_SAVED_FUNCTION_VISIBILITY
 
index 5e4012b..59c759b 100644 (file)
@@ -1,18 +1,15 @@
 /* vi: set sw=4 ts=4: */
 /*
- * ip.c                "ip" utility frontend.
- *
- *             This program is free software; you can redistribute it and/or
- *             modify it under the terms of the GNU General Public License
- *             as published by the Free Software Foundation; either version
- *             2 of the License, or (at your option) any later version.
- *
- * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
  *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
  * Changes:
  *
- * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
  */
 
 #include "ip_common.h"  /* #include "libbb.h" is inside */
@@ -22,7 +19,7 @@ family_t preferred_family = AF_UNSPEC;
 smallint oneline;
 char _SL_;
 
-char **ip_parse_common_args(char **argv)
+char** FAST_FUNC ip_parse_common_args(char **argv)
 {
        static const char ip_common_commands[] ALIGN1 =
                "oneline" "\0"
index 3812934..aa4779a 100644 (file)
@@ -1,13 +1,11 @@
 /* vi: set sw=4 ts=4: */
 /*
- * ipaddress.c         "ip address".
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
- *
- * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
  * Changes:
- *     Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated
+ * Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated
  */
 
 #include <fnmatch.h>
@@ -20,7 +18,7 @@
 
 #ifndef IFF_LOWER_UP
 /* from linux/if.h */
-#define IFF_LOWER_UP   0x10000         /* driver signals L1 up*/
+#define IFF_LOWER_UP  0x10000  /* driver signals L1 up */
 #endif
 
 struct filter_t {
@@ -164,6 +162,8 @@ static NOINLINE int print_linkinfo(const struct nlmsghdr *n)
                printf("master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
        }
 #endif
+/* IFLA_OPERSTATE was added to kernel with the same commit as IFF_DORMANT */
+#ifdef IFF_DORMANT
        if (tb[IFLA_OPERSTATE]) {
                static const char operstate_labels[] ALIGN1 =
                        "UNKNOWN\0""NOTPRESENT\0""DOWN\0""LOWERLAYERDOWN\0"
@@ -171,6 +171,7 @@ static NOINLINE int print_linkinfo(const struct nlmsghdr *n)
                printf("state %s ", nth_string(operstate_labels,
                                        *(uint8_t *)RTA_DATA(tb[IFLA_OPERSTATE])));
        }
+#endif
        if (G_filter.showqueue)
                print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME]));
 
@@ -313,14 +314,16 @@ static int FAST_FUNC print_addrinfo(const struct sockaddr_nl *who UNUSED_PARAM,
        if (rta_tb[IFA_BROADCAST]) {
                printf("brd %s ",
                        rt_addr_n2a(ifa->ifa_family,
-                                   RTA_DATA(rta_tb[IFA_BROADCAST]),
-                                   abuf, sizeof(abuf)));
+                                       RTA_DATA(rta_tb[IFA_BROADCAST]),
+                                       abuf, sizeof(abuf))
+               );
        }
        if (rta_tb[IFA_ANYCAST]) {
                printf("any %s ",
                        rt_addr_n2a(ifa->ifa_family,
-                                   RTA_DATA(rta_tb[IFA_ANYCAST]),
-                                   abuf, sizeof(abuf)));
+                                       RTA_DATA(rta_tb[IFA_ANYCAST]),
+                                       abuf, sizeof(abuf))
+               );
        }
        printf("scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1));
        if (ifa->ifa_flags & IFA_F_SECONDARY) {
@@ -365,7 +368,7 @@ static int FAST_FUNC print_addrinfo(const struct sockaddr_nl *who UNUSED_PARAM,
 
 struct nlmsg_list {
        struct nlmsg_list *next;
-       struct nlmsghdr   h;
+       struct nlmsghdr   h;
 };
 
 static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo)
@@ -415,7 +418,7 @@ static void ipaddr_reset_filter(int _oneline)
 }
 
 /* Return value becomes exitcode. It's okay to not return at all */
-int ipaddr_list_or_flush(char **argv, int flush)
+int FAST_FUNC ipaddr_list_or_flush(char **argv, int flush)
 {
        static const char option[] ALIGN1 = "to\0""scope\0""up\0""label\0""dev\0";
 
@@ -624,10 +627,12 @@ static int ipaddr_modify(int cmd, char **argv)
        req.ifa.ifa_family = preferred_family;
 
        while (*argv) {
-               const smalluint arg = index_in_strings(option, *argv);
-               if (arg <= 1) { /* peer, remote */
+               unsigned arg = index_in_strings(option, *argv);
+               /* if search fails, "local" is assumed */
+               if ((int)arg >= 0)
                        NEXT_ARG();
 
+               if (arg <= 1) { /* peer, remote */
                        if (peer_len) {
                                duparg("peer", *argv);
                        }
@@ -640,7 +645,6 @@ static int ipaddr_modify(int cmd, char **argv)
                        req.ifa.ifa_prefixlen = peer.bitlen;
                } else if (arg <= 3) { /* broadcast, brd */
                        inet_prefix addr;
-                       NEXT_ARG();
                        if (brd_len) {
                                duparg("broadcast", *argv);
                        }
@@ -657,7 +661,6 @@ static int ipaddr_modify(int cmd, char **argv)
                        }
                } else if (arg == 4) { /* anycast */
                        inet_prefix addr;
-                       NEXT_ARG();
                        if (any_len) {
                                duparg("anycast", *argv);
                        }
@@ -669,22 +672,18 @@ static int ipaddr_modify(int cmd, char **argv)
                        any_len = addr.bytelen;
                } else if (arg == 5) { /* scope */
                        uint32_t scope = 0;
-                       NEXT_ARG();
                        if (rtnl_rtscope_a2n(&scope, *argv)) {
                                invarg(*argv, "scope");
                        }
                        req.ifa.ifa_scope = scope;
                        scoped = 1;
                } else if (arg == 6) { /* dev */
-                       NEXT_ARG();
                        d = *argv;
                } else if (arg == 7) { /* label */
-                       NEXT_ARG();
                        l = *argv;
                        addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l) + 1);
                } else {
-                       if (arg == 8) /* local */
-                               NEXT_ARG();
+                       /* local (specified or assumed) */
                        if (local_len) {
                                duparg2("local", *argv);
                        }
@@ -721,7 +720,7 @@ static int ipaddr_modify(int cmd, char **argv)
                }
                brd = peer;
                if (brd.bitlen <= 30) {
-                       for (i=31; i>=brd.bitlen; i--) {
+                       for (i = 31; i >= brd.bitlen; i--) {
                                if (brd_len == -1)
                                        brd.data[0] |= htonl(1<<(31-i));
                                else
@@ -747,15 +746,15 @@ static int ipaddr_modify(int cmd, char **argv)
 }
 
 /* Return value becomes exitcode. It's okay to not return at all */
-int do_ipaddr(char **argv)
+int FAST_FUNC do_ipaddr(char **argv)
 {
        static const char commands[] ALIGN1 =
                "add\0""delete\0""list\0""show\0""lst\0""flush\0";
-       smalluint cmd = 2;
+       int cmd = 2;
        if (*argv) {
                cmd = index_in_substrings(commands, *argv);
-               if (cmd > 5)
-                       bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+               if (cmd < 0)
+                       invarg(*argv, applet_name);
                argv++;
                if (cmd <= 1)
                        return ipaddr_modify((cmd == 0) ? RTM_NEWADDR : RTM_DELADDR, argv);
index 8bf8927..286e59e 100644 (file)
@@ -1,22 +1,40 @@
 /* vi: set sw=4 ts=4: */
 /*
- * iplink.c "ip link".
- *
  * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *                     Patrick McHardy <kaber@trash.net>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include <net/if.h>
-#include <net/if_packet.h>
+/*#include <net/if_packet.h> - not needed? */
 #include <netpacket/packet.h>
-#include <net/ethernet.h>
+#include <netinet/if_ether.h>
 
+#include <linux/if_vlan.h>
 #include "ip_common.h"  /* #include "libbb.h" is inside */
 #include "rt_names.h"
 #include "utils.h"
 
+#undef  ETH_P_8021AD
+#define ETH_P_8021AD            0x88A8
+#undef  VLAN_FLAG_REORDER_HDR
+#define VLAN_FLAG_REORDER_HDR   0x1
+#undef  VLAN_FLAG_GVRP
+#define VLAN_FLAG_GVRP          0x2
+#undef  VLAN_FLAG_LOOSE_BINDING
+#define VLAN_FLAG_LOOSE_BINDING 0x4
+#undef  VLAN_FLAG_MVRP
+#define VLAN_FLAG_MVRP          0x8
+#undef  IFLA_VLAN_PROTOCOL
+#define IFLA_VLAN_PROTOCOL      5
+
+#ifndef IFLA_LINKINFO
+# define IFLA_LINKINFO 18
+# define IFLA_INFO_KIND 1
+#endif
+
 /* taken from linux/sockios.h */
-#define SIOCSIFNAME    0x8923          /* set interface name */
+#define SIOCSIFNAME  0x8923  /* set interface name */
 
 /* Exits on error */
 static int get_ctl_fd(void)
@@ -274,12 +292,103 @@ static int ipaddr_list_link(char **argv)
        return ipaddr_list_or_flush(argv, 0);
 }
 
+static void vlan_parse_opt(char **argv, struct nlmsghdr *n, unsigned int size)
+{
+       static const char keywords[] ALIGN1 =
+               "id\0"
+               "protocol\0"
+               "reorder_hdr\0"
+               "gvrp\0"
+               "mvrp\0"
+               "loose_binding\0"
+       ;
+       static const char protocols[] ALIGN1 =
+               "802.1q\0"
+               "802.1ad\0"
+       ;
+       static const char str_on_off[] ALIGN1 =
+               "on\0"
+               "off\0"
+       ;
+       enum {
+               ARG_id = 0,
+               ARG_reorder_hdr,
+               ARG_gvrp,
+               ARG_mvrp,
+               ARG_loose_binding,
+               ARG_protocol,
+       };
+       enum {
+               PROTO_8021Q = 0,
+               PROTO_8021AD,
+       };
+       enum {
+               PARM_on = 0,
+               PARM_off
+       };
+       int arg;
+       uint16_t id, proto;
+       struct ifla_vlan_flags flags = {};
+
+       while (*argv) {
+               arg = index_in_substrings(keywords, *argv);
+               if (arg < 0)
+                       invarg(*argv, "type vlan");
+
+               NEXT_ARG();
+               if (arg == ARG_id) {
+                       id = get_u16(*argv, "id");
+                       addattr_l(n, size, IFLA_VLAN_ID, &id, sizeof(id));
+               } else if (arg == ARG_protocol) {
+                       arg = index_in_substrings(protocols, *argv);
+                       if (arg == PROTO_8021Q)
+                               proto = ETH_P_8021Q;
+                       else if (arg == PROTO_8021AD)
+                               proto = ETH_P_8021AD;
+                       else
+                               bb_error_msg_and_die("unknown VLAN encapsulation protocol '%s'",
+                                                                    *argv);
+                       addattr_l(n, size, IFLA_VLAN_PROTOCOL, &proto, sizeof(proto));
+               } else {
+                       int param = index_in_strings(str_on_off, *argv);
+                       if (param < 0)
+                               die_must_be_on_off(nth_string(keywords, arg));
+
+                       if (arg == ARG_reorder_hdr) {
+                               flags.mask |= VLAN_FLAG_REORDER_HDR;
+                               flags.flags &= ~VLAN_FLAG_REORDER_HDR;
+                               if (param == PARM_on)
+                                       flags.flags |= VLAN_FLAG_REORDER_HDR;
+                       } else if (arg == ARG_gvrp) {
+                               flags.mask |= VLAN_FLAG_GVRP;
+                               flags.flags &= ~VLAN_FLAG_GVRP;
+                               if (param == PARM_on)
+                                       flags.flags |= VLAN_FLAG_GVRP;
+                       } else if (arg == ARG_mvrp) {
+                               flags.mask |= VLAN_FLAG_MVRP;
+                               flags.flags &= ~VLAN_FLAG_MVRP;
+                               if (param == PARM_on)
+                                       flags.flags |= VLAN_FLAG_MVRP;
+                       } else { /*if (arg == ARG_loose_binding) */
+                               flags.mask |= VLAN_FLAG_LOOSE_BINDING;
+                               flags.flags &= ~VLAN_FLAG_LOOSE_BINDING;
+                               if (param == PARM_on)
+                                       flags.flags |= VLAN_FLAG_LOOSE_BINDING;
+                       }
+               }
+               argv++;
+       }
+
+       if (flags.mask)
+               addattr_l(n, size, IFLA_VLAN_FLAGS, &flags, sizeof(flags));
+}
+
 #ifndef NLMSG_TAIL
 #define NLMSG_TAIL(nmsg) \
        ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
 #endif
 /* Return value becomes exitcode. It's okay to not return at all */
-static int do_change(char **argv, const unsigned rtm)
+static int do_add_or_delete(char **argv, const unsigned rtm)
 {
        static const char keywords[] ALIGN1 =
                "link\0""name\0""type\0""dev\0";
@@ -291,9 +400,9 @@ static int do_change(char **argv, const unsigned rtm)
        };
        struct rtnl_handle rth;
        struct {
-               struct nlmsghdr         n;
-               struct ifinfomsg        i;
-               char                    buf[1024];
+               struct nlmsghdr  n;
+               struct ifinfomsg i;
+               char             buf[1024];
        } req;
        smalluint arg;
        char *name_str = NULL, *link_str = NULL, *type_str = NULL, *dev_str = NULL;
@@ -309,15 +418,17 @@ static int do_change(char **argv, const unsigned rtm)
 
        while (*argv) {
                arg = index_in_substrings(keywords, *argv);
+               if (arg == ARG_type) {
+                       NEXT_ARG();
+                       type_str = *argv++;
+                       break;
+               }
                if (arg == ARG_link) {
                        NEXT_ARG();
                        link_str = *argv;
                } else if (arg == ARG_name) {
                        NEXT_ARG();
                        name_str = *argv;
-               } else if (arg == ARG_type) {
-                       NEXT_ARG();
-                       type_str = *argv;
                } else {
                        if (arg == ARG_dev) {
                                if (dev_str)
@@ -336,6 +447,17 @@ static int do_change(char **argv, const unsigned rtm)
                addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
                addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type_str,
                                strlen(type_str));
+
+               if (*argv) {
+                       struct rtattr *data = NLMSG_TAIL(&req.n);
+                       addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0);
+
+                       if (strcmp(type_str, "vlan") == 0)
+                               vlan_parse_opt(argv, &req.n, sizeof(req));
+
+                       data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data;
+               }
+
                linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
        }
        if (rtm != RTM_NEWLINK) {
@@ -362,18 +484,18 @@ static int do_change(char **argv, const unsigned rtm)
 }
 
 /* Return value becomes exitcode. It's okay to not return at all */
-int do_iplink(char **argv)
+int FAST_FUNC do_iplink(char **argv)
 {
        static const char keywords[] ALIGN1 =
                "add\0""delete\0""set\0""show\0""lst\0""list\0";
        if (*argv) {
-               smalluint key = index_in_substrings(keywords, *argv);
-               if (key > 5) /* invalid argument */
-                       bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+               int key = index_in_substrings(keywords, *argv);
+               if (key < 0) /* invalid argument */
+                       invarg(*argv, applet_name);
                argv++;
                if (key <= 1) /* add/delete */
-                       return do_change(argv, key ? RTM_DELLINK : RTM_NEWLINK);
-               else if (key == 2) /* set */
+                       return do_add_or_delete(argv, key ? RTM_DELLINK : RTM_NEWLINK);
+               if (key == 2) /* set */
                        return do_set(argv);
        }
        /* show, lst, list */
index d771a60..f8a67d9 100644 (file)
@@ -1,19 +1,16 @@
 /* vi: set sw=4 ts=4: */
 /*
- * iproute.c           "ip route".
- *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
- *
- * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
  * Changes:
  *
- * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
  * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
  */
 
-#include "ip_common.h" /* #include "libbb.h" is inside */
+#include "ip_common.h"  /* #include "libbb.h" is inside */
 #include "rt_names.h"
 #include "utils.h"
 
@@ -34,8 +31,8 @@ struct filter_t {
        //int type; - read-only
        //int typemask; - unused
        //int tos, tosmask; - unused
-       int iif, iifmask;
-       int oif, oifmask;
+       int iif;
+       int oif;
        //int realm, realmmask; - unused
        //inet_prefix rprefsrc; - read-only
        inet_prefix rvia;
@@ -85,7 +82,7 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
 {
        struct rtmsg *r = NLMSG_DATA(n);
        int len = n->nlmsg_len;
-       struct rtattr * tb[RTA_MAX+1];
+       struct rtattr *tb[RTA_MAX+1];
        char abuf[256];
        inet_prefix dst;
        inet_prefix src;
@@ -162,8 +159,21 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
        }
 
        memset(tb, 0, sizeof(tb));
+       memset(&src, 0, sizeof(src));
+       memset(&dst, 0, sizeof(dst));
        parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
 
+       if (tb[RTA_SRC]) {
+               src.bitlen = r->rtm_src_len;
+               src.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
+               memcpy(src.data, RTA_DATA(tb[RTA_SRC]), src.bytelen);
+       }
+       if (tb[RTA_DST]) {
+               dst.bitlen = r->rtm_dst_len;
+               dst.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
+               memcpy(dst.data, RTA_DATA(tb[RTA_DST]), dst.bytelen);
+       }
+
        if (G_filter.rdst.family
         && inet_addr_match(&dst, &G_filter.rdst, G_filter.rdst.bitlen)
        ) {
@@ -185,23 +195,32 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
        ) {
                return 0;
        }
-       if (G_filter.flushb
-        && r->rtm_family == AF_INET6
-        && r->rtm_dst_len == 0
-        && r->rtm_type == RTN_UNREACHABLE
-        && tb[RTA_PRIORITY]
-        && *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1
-       ) {
-               return 0;
+       if (G_filter.oif != 0) {
+               if (!tb[RTA_OIF])
+                       return 0;
+               if (G_filter.oif != *(int*)RTA_DATA(tb[RTA_OIF]))
+                       return 0;
        }
 
        if (G_filter.flushb) {
                struct nlmsghdr *fn;
+
+               /* We are creating route flush commands */
+
+               if (r->rtm_family == AF_INET6
+                && r->rtm_dst_len == 0
+                && r->rtm_type == RTN_UNREACHABLE
+                && tb[RTA_PRIORITY]
+                && *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1
+               ) {
+                       return 0;
+               }
+
                if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) {
                        if (flush_update())
                                bb_error_msg_and_die("flush");
                }
-               fn = (struct nlmsghdr*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
+               fn = (void*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
                memcpy(fn, n, n->nlmsg_len);
                fn->nlmsg_type = RTM_DELROUTE;
                fn->nlmsg_flags = NLM_F_REQUEST;
@@ -211,6 +230,8 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
                return 0;
        }
 
+       /* We are printing routes */
+
        if (n->nlmsg_type == RTM_DELROUTE) {
                printf("Deleted ");
        }
@@ -260,10 +281,12 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
                                        RTA_DATA(tb[RTA_GATEWAY]),
                                        abuf, sizeof(abuf)));
        }
-       if (tb[RTA_OIF] && G_filter.oifmask != -1) {
+       if (tb[RTA_OIF]) {
                printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
        }
 
+       /* Todo: parse & show "proto kernel", "scope link" here */
+
        if (tb[RTA_PREFSRC] && /*G_filter.rprefsrc.bitlen - always 0*/ 0 != host_len) {
                /* Do not use format_host(). It is our local addr
                   and symbolic name will not be useful.
@@ -295,7 +318,7 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
                                printf(" error %d", ci->rta_error);
                }
        }
-       if (tb[RTA_IIF] && G_filter.iifmask != -1) {
+       if (tb[RTA_IIF] && G_filter.iif == 0) {
                printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
        }
        bb_putchar('\n');
@@ -327,9 +350,9 @@ IF_FEATURE_IP_RULE(ARG_table,)
        };
        struct rtnl_handle rth;
        struct {
-               struct nlmsghdr         n;
-               struct rtmsg            r;
-               char                    buf[1024];
+               struct nlmsghdr n;
+               struct rtmsg    r;
+               char            buf[1024];
        } req;
        char mxbuf[256];
        struct rtattr * mxrta = (void*)mxbuf;
@@ -416,7 +439,8 @@ IF_FEATURE_IP_RULE(ARG_table,)
                                NEXT_ARG();
                        }
                        if ((**argv < '0' || **argv > '9')
-                        && rtnl_rtntype_a2n(&type, *argv) == 0) {
+                        && rtnl_rtntype_a2n(&type, *argv) == 0
+                       ) {
                                NEXT_ARG();
                                req.r.rtm_type = type;
                                ok |= type_ok;
@@ -665,12 +689,10 @@ static int iproute_list_or_flush(char **argv, int flush)
                if (id) {
                        idx = xll_name_to_index(id);
                        G_filter.iif = idx;
-                       G_filter.iifmask = -1;
                }
                if (od) {
                        idx = xll_name_to_index(od);
                        G_filter.oif = idx;
-                       G_filter.oifmask = -1;
                }
        }
 
@@ -791,8 +813,8 @@ static int iproute_get(char **argv)
                                }
                                req.r.rtm_dst_len = addr.bitlen;
                        }
-                       argv++;
                }
+               argv++;
        }
 
        if (req.r.rtm_dst_len == 0) {
@@ -869,7 +891,7 @@ static int iproute_get(char **argv)
 }
 
 /* Return value becomes exitcode. It's okay to not return at all */
-int do_iproute(char **argv)
+int FAST_FUNC do_iproute(char **argv)
 {
        static const char ip_route_commands[] ALIGN1 =
        /*0-3*/ "add\0""append\0""change\0""chg\0"
index 835529e..8dbe6bd 100644 (file)
@@ -1,18 +1,15 @@
 /* vi: set sw=4 ts=4: */
 /*
- * iprule.c            "ip rule".
- *
- *             This program is free software; you can redistribute it and/or
- *             modify it under the terms of the GNU General Public License
- *             as published by the Free Software Foundation; either version
- *             2 of the License, or (at your option) any later version.
- *
- * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
  *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
  * Changes:
  *
- * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
  * initially integrated into busybox by Bernhard Reutner-Fischer
  */
 
@@ -76,15 +73,17 @@ static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
        if (tb[RTA_SRC]) {
                if (r->rtm_src_len != host_len) {
                        printf("%s/%u", rt_addr_n2a(r->rtm_family,
-                                                        RTA_DATA(tb[RTA_SRC]),
-                                                        abuf, sizeof(abuf)),
+                                                       RTA_DATA(tb[RTA_SRC]),
+                                                       abuf, sizeof(abuf)),
                                r->rtm_src_len
-                               );
+                       );
                } else {
                        fputs(format_host(r->rtm_family,
-                                                      RTA_PAYLOAD(tb[RTA_SRC]),
-                                                      RTA_DATA(tb[RTA_SRC]),
-                                                      abuf, sizeof(abuf)), stdout);
+                                               RTA_PAYLOAD(tb[RTA_SRC]),
+                                               RTA_DATA(tb[RTA_SRC]),
+                                               abuf, sizeof(abuf)),
+                               stdout
+                       );
                }
        } else if (r->rtm_src_len) {
                printf("0/%d", r->rtm_src_len);
@@ -191,9 +190,9 @@ static int iprule_modify(int cmd, char **argv)
        bool table_ok = 0;
        struct rtnl_handle rth;
        struct {
-               struct nlmsghdr n;
-               struct rtmsg    r;
-               char            buf[1024];
+               struct nlmsghdr n;
+               struct rtmsg    r;
+               char            buf[1024];
        } req;
        smalluint key;
 
@@ -216,7 +215,7 @@ static int iprule_modify(int cmd, char **argv)
        while (*argv) {
                key = index_in_substrings(keywords, *argv) + 1;
                if (key == 0) /* no match found in keywords array, bail out. */
-                       bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+                       invarg(*argv, applet_name);
                if (key == ARG_from) {
                        inet_prefix dst;
                        NEXT_ARG();
@@ -304,14 +303,14 @@ static int iprule_modify(int cmd, char **argv)
 }
 
 /* Return value becomes exitcode. It's okay to not return at all */
-int do_iprule(char **argv)
+int FAST_FUNC do_iprule(char **argv)
 {
        static const char ip_rule_commands[] ALIGN1 =
                "add\0""delete\0""list\0""show\0";
        if (*argv) {
-               smalluint cmd = index_in_substrings(ip_rule_commands, *argv);
-               if (cmd > 3)
-                       bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+               int cmd = index_in_substrings(ip_rule_commands, *argv);
+               if (cmd < 0)
+                       invarg(*argv, applet_name);
                argv++;
                if (cmd < 2)
                        return iprule_modify((cmd == 0) ? RTM_NEWRULE : RTM_DELRULE, argv);
index 2573438..b54c3c5 100644 (file)
@@ -1,16 +1,14 @@
 /* vi: set sw=4 ts=4: */
 /*
- * iptunnel.c         "ip tunnel"
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
- *
- * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
  * Changes:
  *
- * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
- * Rani Assaf <rani@magic.metawire.com> 980930:        do not allow key for ipip/sit
- * Phil Karn <karn@ka9q.ampr.org>      990408: "pmtudisc" flag
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit
+ * Phil Karn <karn@ka9q.ampr.org>       990408: "pmtudisc" flag
  */
 
 #include <netinet/ip.h>
@@ -440,7 +438,7 @@ static void print_tunnel(struct ip_tunnel_parm *p)
                        printf(" inherit");
                if (p->iph.tos & ~1)
                        printf("%c%s ", p->iph.tos & 1 ? '/' : ' ',
-                              rtnl_dsfield_n2a(p->iph.tos & ~1, b1));
+                               rtnl_dsfield_n2a(p->iph.tos & ~1, b1));
        }
        if (!(p->iph.frag_off & htons(IP_DF)))
                printf(" nopmtudisc");
@@ -556,16 +554,16 @@ static int do_show(char **argv)
 }
 
 /* Return value becomes exitcode. It's okay to not return at all */
-int do_iptunnel(char **argv)
+int FAST_FUNC do_iptunnel(char **argv)
 {
        static const char keywords[] ALIGN1 =
                "add\0""change\0""delete\0""show\0""list\0""lst\0";
        enum { ARG_add = 0, ARG_change, ARG_del, ARG_show, ARG_list, ARG_lst };
 
        if (*argv) {
-               smalluint key = index_in_substrings(keywords, *argv);
-               if (key > 5)
-                       bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+               int key = index_in_substrings(keywords, *argv);
+               if (key < 0)
+                       invarg(*argv, applet_name);
                argv++;
                if (key == ARG_add)
                        return do_add(SIOCADDTUNNEL, argv);
index ba24832..c7533a4 100644 (file)
@@ -1,14 +1,11 @@
 /* vi: set sw=4 ts=4: */
 /*
- * libnetlink.c        RTnetlink service routines.
- *
- *             This program is free software; you can redistribute it and/or
- *             modify it under the terms of the GNU General Public License
- *             as published by the Free Software Foundation; either version
- *             2 of the License, or (at your option) any later version.
- *
- * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
  *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  */
 
 #include <sys/socket.h>
 #include "libbb.h"
 #include "libnetlink.h"
 
-void FAST_FUNC rtnl_close(struct rtnl_handle *rth)
-{
-       close(rth->fd);
-}
-
-int FAST_FUNC xrtnl_open(struct rtnl_handle *rth/*, unsigned subscriptions*/)
+void FAST_FUNC xrtnl_open(struct rtnl_handle *rth/*, unsigned subscriptions*/)
 {
        socklen_t addr_len;
 
@@ -44,7 +36,6 @@ int FAST_FUNC xrtnl_open(struct rtnl_handle *rth/*, unsigned subscriptions*/)
                bb_error_msg_and_die("wrong address family %d", rth->local.nl_family);
 */
        rth->seq = time(NULL);
-       return 0;
 }
 
 int FAST_FUNC xrtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
@@ -53,10 +44,6 @@ int FAST_FUNC xrtnl_wilddump_request(struct rtnl_handle *rth, int family, int ty
                struct nlmsghdr nlh;
                struct rtgenmsg g;
        } req;
-       struct sockaddr_nl nladdr;
-
-       memset(&nladdr, 0, sizeof(nladdr));
-       nladdr.nl_family = AF_NETLINK;
 
        req.nlh.nlmsg_len = sizeof(req);
        req.nlh.nlmsg_type = type;
@@ -65,10 +52,10 @@ int FAST_FUNC xrtnl_wilddump_request(struct rtnl_handle *rth, int family, int ty
        req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
        req.g.rtgen_family = family;
 
-       return xsendto(rth->fd, (void*)&req, sizeof(req),
-                                (struct sockaddr*)&nladdr, sizeof(nladdr));
+       return rtnl_send(rth, (void*)&req, sizeof(req));
 }
 
+//TODO: pass rth->fd instead of full rth?
 int FAST_FUNC rtnl_send(struct rtnl_handle *rth, char *buf, int len)
 {
        struct sockaddr_nl nladdr;
@@ -86,8 +73,8 @@ int FAST_FUNC rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, in
        struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } };
        struct msghdr msg = {
                (void*)&nladdr, sizeof(nladdr),
-               iov,    2,
-               NULL,   0,
+               iov,  2,
+               NULL, 0,
                0
        };
 
@@ -120,8 +107,8 @@ static int rtnl_dump_filter(struct rtnl_handle *rth,
 
                struct msghdr msg = {
                        (void*)&nladdr, sizeof(nladdr),
-                       &iov,   1,
-                       NULL,   0,
+                       &iov, 1,
+                       NULL, 0,
                        0
                };
 
@@ -226,8 +213,8 @@ int FAST_FUNC rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
        char   *buf = xmalloc(8*1024); /* avoid big stack buffer */
        struct msghdr msg = {
                (void*)&nladdr, sizeof(nladdr),
-               &iov,   1,
-               NULL,   0,
+               &iov, 1,
+               NULL, 0,
                0
        };
 
@@ -339,8 +326,10 @@ int FAST_FUNC addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data)
 {
        int len = RTA_LENGTH(4);
        struct rtattr *rta;
-       if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen)
+
+       if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) {
                return -1;
+       }
        rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
        rta->rta_type = type;
        rta->rta_len = len;
@@ -354,8 +343,9 @@ int FAST_FUNC addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, in
        int len = RTA_LENGTH(alen);
        struct rtattr *rta;
 
-       if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen)
+       if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) {
                return -1;
+       }
        rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
        rta->rta_type = type;
        rta->rta_len = len;
@@ -397,16 +387,15 @@ int FAST_FUNC rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data
 }
 
 
-int FAST_FUNC parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+void FAST_FUNC parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
 {
        while (RTA_OK(rta, len)) {
                if (rta->rta_type <= max) {
                        tb[rta->rta_type] = rta;
                }
-               rta = RTA_NEXT(rta,len);
+               rta = RTA_NEXT(rta, len);
        }
        if (len) {
                bb_error_msg("deficit %d, rta_len=%d!", len, rta->rta_len);
        }
-       return 0;
 }
index 41ecfa6..51bee2d 100644 (file)
 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 
 struct rtnl_handle {
-       int                     fd;
-       struct sockaddr_nl      local;
-       struct sockaddr_nl      peer;
-       uint32_t                seq;
-       uint32_t                dump;
+       int                fd;
+       struct sockaddr_nl local;
+       struct sockaddr_nl peer;
+       uint32_t           seq;
+       uint32_t           dump;
 };
 
-extern int xrtnl_open(struct rtnl_handle *rth) FAST_FUNC;
-extern void rtnl_close(struct rtnl_handle *rth) FAST_FUNC;
+extern void xrtnl_open(struct rtnl_handle *rth) FAST_FUNC;
+#define rtnl_close(rth) (close((rth)->fd))
 extern int xrtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type) FAST_FUNC;
 extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) FAST_FUNC;
 extern int xrtnl_dump_filter(struct rtnl_handle *rth,
@@ -42,7 +42,7 @@ extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int a
 extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t data) FAST_FUNC;
 extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen) FAST_FUNC;
 
-extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) FAST_FUNC;
+extern void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) FAST_FUNC;
 
 POP_SAVED_FUNCTION_VISIBILITY
 
index c2c0130..33a54ea 100644 (file)
@@ -1,13 +1,11 @@
 /* vi: set sw=4 ts=4: */
 /*
- * ll_addr.c
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
  *
- *             This program is free software; you can redistribute it and/or
- *             modify it under the terms of the GNU General Public License
- *             as published by the Free Software Foundation; either version
- *             2 of the License, or (at your option) any later version.
- *
- * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  */
 
 #include <net/if_arp.h>
index 246b9e3..27cd90f 100644 (file)
@@ -1,17 +1,14 @@
 /* vi: set sw=4 ts=4: */
 /*
- * ll_map.c
- *
- *             This program is free software; you can redistribute it and/or
- *             modify it under the terms of the GNU General Public License
- *             as published by the Free Software Foundation; either version
- *             2 of the License, or (at your option) any later version.
- *
- * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
  *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  */
 
-#include <net/if.h>    /* struct ifreq and co. */
+#include <net/if.h>  /* struct ifreq and co. */
 
 #include "libbb.h"
 #include "libnetlink.h"
index 145902b..da2b53c 100644 (file)
@@ -1,38 +1,91 @@
 /* vi: set sw=4 ts=4: */
 /*
- * ll_proto.c
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
  *
- *             This program is free software; you can redistribute it and/or
- *             modify it under the terms of the GNU General Public License
- *             as published by the Free Software Foundation; either version
- *             2 of the License, or (at your option) any later version.
- *
- * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  */
 
 #include "libbb.h"
 #include "rt_names.h"
 #include "utils.h"
 
-#if defined(__GLIBC__) && __GLIBC__ >=2 && __GLIBC_MINOR__ >= 1
-#include <net/ethernet.h>
-#else
-#include <linux/if_ether.h>
+#include <netinet/if_ether.h>
+
+/* Please conditionalize exotic protocols on CONFIG_something */
+
+static const uint16_t llproto_ids[] = {
+#define __PF(f,n) ETH_P_##f,
+__PF(LOOP,loop)
+__PF(PUP,pup)
+#ifdef ETH_P_PUPAT
+__PF(PUPAT,pupat)
+#endif
+__PF(IP,ip)
+__PF(X25,x25)
+__PF(ARP,arp)
+__PF(BPQ,bpq)
+#ifdef ETH_P_IEEEPUP
+__PF(IEEEPUP,ieeepup)
+#endif
+#ifdef ETH_P_IEEEPUPAT
+__PF(IEEEPUPAT,ieeepupat)
+#endif
+__PF(DEC,dec)
+__PF(DNA_DL,dna_dl)
+__PF(DNA_RC,dna_rc)
+__PF(DNA_RT,dna_rt)
+__PF(LAT,lat)
+__PF(DIAG,diag)
+__PF(CUST,cust)
+__PF(SCA,sca)
+__PF(RARP,rarp)
+__PF(ATALK,atalk)
+__PF(AARP,aarp)
+__PF(IPX,ipx)
+__PF(IPV6,ipv6)
+#ifdef ETH_P_PPP_DISC
+__PF(PPP_DISC,ppp_disc)
+#endif
+#ifdef ETH_P_PPP_SES
+__PF(PPP_SES,ppp_ses)
+#endif
+#ifdef ETH_P_ATMMPOA
+__PF(ATMMPOA,atmmpoa)
+#endif
+#ifdef ETH_P_ATMFATE
+__PF(ATMFATE,atmfate)
 #endif
 
-#if !ENABLE_WERROR
-#warning de-bloat
+__PF(802_3,802_3)
+__PF(AX25,ax25)
+__PF(ALL,all)
+__PF(802_2,802_2)
+__PF(SNAP,snap)
+__PF(DDCMP,ddcmp)
+__PF(WAN_PPP,wan_ppp)
+__PF(PPP_MP,ppp_mp)
+__PF(LOCALTALK,localtalk)
+__PF(PPPTALK,ppptalk)
+__PF(TR_802_2,tr_802_2)
+__PF(MOBITEX,mobitex)
+__PF(CONTROL,control)
+__PF(IRDA,irda)
+#ifdef ETH_P_ECONET
+__PF(ECONET,econet)
 #endif
-/* Before re-enabling this, please (1) conditionalize exotic protocols
- * on CONFIG_something, and (2) decouple strings and numbers
- * (use llproto_ids[] = n,n,n..; and llproto_names[] = "loop\0" "pup\0" ...;)
- */
 
-#define __PF(f,n) { ETH_P_##f, #n },
-static struct {
-       int id;
-       const char *name;
-} llproto_names[] = {
+0x8100,
+ETH_P_IP
+};
+#undef __PF
+
+/* Keep declarations above and below in sync! */
+
+static const char llproto_names[] =
+#define __PF(f,n) #n "\0"
 __PF(LOOP,loop)
 __PF(PUP,pup)
 #ifdef ETH_P_PUPAT
@@ -92,9 +145,9 @@ __PF(IRDA,irda)
 __PF(ECONET,econet)
 #endif
 
-{ 0x8100, "802.1Q" },
-{ ETH_P_IP, "ipv4" },
-};
+"802.1Q" "\0"
+"ipv4" "\0"
+;
 #undef __PF
 
 
@@ -102,23 +155,26 @@ const char* FAST_FUNC ll_proto_n2a(unsigned short id, char *buf, int len)
 {
        unsigned i;
        id = ntohs(id);
-       for (i = 0; i < ARRAY_SIZE(llproto_names); i++) {
-                if (llproto_names[i].id == id)
-                       return llproto_names[i].name;
+       for (i = 0; i < ARRAY_SIZE(llproto_ids); i++) {
+               if (llproto_ids[i] == id)
+                       return nth_string(llproto_names, i);
        }
-       snprintf(buf, len, "[%d]", id);
+       snprintf(buf, len, "[%u]", id);
        return buf;
 }
 
 int FAST_FUNC ll_proto_a2n(unsigned short *id, char *buf)
 {
        unsigned i;
-       for (i = 0; i < ARRAY_SIZE(llproto_names); i++) {
-                if (strcasecmp(llproto_names[i].name, buf) == 0) {
-                        i = llproto_names[i].id;
-                        goto good;
-                }
+       const char *name = llproto_names;
+       for (i = 0; i < ARRAY_SIZE(llproto_ids); i++) {
+               if (strcasecmp(name, buf) == 0) {
+                       i = llproto_ids[i];
+                       goto good;
+               }
+               name += strlen(name) + 1;
        }
+       errno = 0;
        i = bb_strtou(buf, NULL, 0);
        if (errno || i > 0xffff)
                return -1;
@@ -126,4 +182,3 @@ int FAST_FUNC ll_proto_a2n(unsigned short *id, char *buf)
        *id = htons(i);
        return 0;
 }
-
index 3861c28..bb42e26 100644 (file)
@@ -1,14 +1,13 @@
 /* vi: set sw=4 ts=4: */
 /*
- * ll_types.c
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
  *
- *             This program is free software; you can redistribute it and/or
- *             modify it under the terms of the GNU General Public License
- *             as published by the Free Software Foundation; either version
- *             2 of the License, or (at your option) any later version.
- *
- * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  */
+#include <sys/socket.h> /* linux/if_arp.h needs it on some systems */
 #include <arpa/inet.h>
 #include <linux/if_arp.h>
 
index 8dd16e3..c474ab9 100644 (file)
@@ -1,13 +1,11 @@
 /* vi: set sw=4 ts=4: */
 /*
- * rt_names.c          rtnetlink names DB.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
  *
- *             This program is free software; you can redistribute it and/or
- *             modify it under the terms of the GNU General Public License
- *             as published by the Free Software Foundation; either version
- *             2 of the License, or (at your option) any later version.
- *
- * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  */
 #include "libbb.h"
 #include "rt_names.h"
index 5e358e1..3bab53b 100644 (file)
@@ -1,14 +1,11 @@
 /* vi: set sw=4 ts=4: */
 /*
- * rtm_map.c
- *
- *             This program is free software; you can redistribute it and/or
- *             modify it under the terms of the GNU General Public License
- *             as published by the Free Software Foundation; either version
- *             2 of the License, or (at your option) any later version.
- *
- * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
  *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  */
 
 #include "libbb.h"
index 85034c0..d0fe306 100644 (file)
@@ -1,14 +1,12 @@
 /* vi: set sw=4 ts=4: */
 /*
- * utils.c
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
- *
- * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
  * Changes:
  *
- * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
  */
 
 #include "libbb.h"
@@ -64,7 +62,7 @@ int get_addr_1(inet_prefix *addr, char *name, int family)
 {
        memset(addr, 0, sizeof(*addr));
 
-       if (strcmp(name, bb_str_default) == 0
+       if (strcmp(name, "default") == 0
         || strcmp(name, "all") == 0
         || strcmp(name, "any") == 0
        ) {
@@ -85,73 +83,98 @@ int get_addr_1(inet_prefix *addr, char *name, int family)
                return 0;
        }
 
-       addr->family = AF_INET;
        if (family != AF_UNSPEC && family != AF_INET)
                return -1;
+
+       /* Try to parse it as IPv4 */
+       addr->family = AF_INET;
+#if 0 /* Doesn't handle e.g. "10.10", for example, "ip r l root 10.10/16" */
        if (inet_pton(AF_INET, name, addr->data) <= 0)
                return -1;
+#else
+       {
+               unsigned i = 0;
+               unsigned n = 0;
+               const char *cp = name - 1;
+               while (*++cp) {
+                       if ((unsigned char)(*cp - '0') <= 9) {
+                               n = 10 * n + (unsigned char)(*cp - '0');
+                               if (n >= 256)
+                                       return -1;
+                               ((uint8_t*)addr->data)[i] = n;
+                               continue;
+                       }
+                       if (*cp == '.' && ++i <= 3) {
+                               n = 0;
+                               continue;
+                       }
+                       return -1;
+               }
+       }
+#endif
        addr->bytelen = 4;
        addr->bitlen = -1;
+
        return 0;
 }
 
-static int get_prefix_1(inet_prefix *dst, char *arg, int family)
+static void get_prefix_1(inet_prefix *dst, char *arg, int family)
 {
-       int err;
-       unsigned plen;
        char *slash;
 
        memset(dst, 0, sizeof(*dst));
 
-       if (strcmp(arg, bb_str_default) == 0
+       if (strcmp(arg, "default") == 0
         || strcmp(arg, "all") == 0
         || strcmp(arg, "any") == 0
        ) {
                dst->family = family;
                /*dst->bytelen = 0; - done by memset */
                /*dst->bitlen = 0;*/
-               return 0;
+               return;
        }
 
        slash = strchr(arg, '/');
        if (slash)
                *slash = '\0';
-       err = get_addr_1(dst, arg, family);
-       if (err == 0) {
+
+       if (get_addr_1(dst, arg, family) == 0) {
                dst->bitlen = (dst->family == AF_INET6) ? 128 : 32;
                if (slash) {
+                       unsigned plen;
                        inet_prefix netmask_pfx;
 
                        netmask_pfx.family = AF_UNSPEC;
                        plen = bb_strtou(slash + 1, NULL, 0);
                        if ((errno || plen > dst->bitlen)
-                        && (get_addr_1(&netmask_pfx, slash + 1, family)))
-                               err = -1;
-                       else if (netmask_pfx.family == AF_INET) {
+                        && get_addr_1(&netmask_pfx, slash + 1, family) != 0
+                       ) {
+                               goto bad;
+                       }
+                       if (netmask_pfx.family == AF_INET) {
                                /* fill in prefix length of dotted quad */
                                uint32_t mask = ntohl(netmask_pfx.data[0]);
                                uint32_t host = ~mask;
 
                                /* a valid netmask must be 2^n - 1 */
-                               if (!(host & (host + 1))) {
-                                       for (plen = 0; mask; mask <<= 1)
-                                               ++plen;
-                                       if (plen <= dst->bitlen) {
-                                               dst->bitlen = plen;
-                                               /* dst->flags |= PREFIXLEN_SPECIFIED; */
-                                       } else
-                                               err = -1;
-                               } else
-                                       err = -1;
-                       } else {
-                               /* plain prefix */
-                               dst->bitlen = plen;
+                               if (host & (host + 1))
+                                       goto bad;
+
+                               for (plen = 0; mask; mask <<= 1)
+                                       ++plen;
+                               if (plen > dst->bitlen)
+                                       goto bad;
+                               /* dst->flags |= PREFIXLEN_SPECIFIED; */
                        }
+                       dst->bitlen = plen;
                }
        }
+
        if (slash)
                *slash = '/';
-       return err;
+       return;
+ bad:
+       bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "inet", "prefix", arg);
 }
 
 int get_addr(inet_prefix *dst, char *arg, int family)
@@ -165,15 +188,12 @@ int get_addr(inet_prefix *dst, char *arg, int family)
        return 0;
 }
 
-int get_prefix(inet_prefix *dst, char *arg, int family)
+void get_prefix(inet_prefix *dst, char *arg, int family)
 {
        if (family == AF_PACKET) {
                bb_error_msg_and_die("\"%s\" may be inet %s, but it is not allowed in this context", arg, "prefix");
        }
-       if (get_prefix_1(dst, arg, family)) {
-               bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "inet", "prefix", arg);
-       }
-       return 0;
+       get_prefix_1(dst, arg, family);
 }
 
 uint32_t get_addr32(char *name)
@@ -206,10 +226,10 @@ void duparg2(const char *key, const char *arg)
        bb_error_msg_and_die("either \"%s\" is duplicate, or \"%s\" is garbage", key, arg);
 }
 
-int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits)
+int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits)
 {
-       uint32_t *a1 = a->data;
-       uint32_t *a2 = b->data;
+       const uint32_t *a1 = a->data;
+       const uint32_t *a2 = b->data;
        int words = bits >> 5;
 
        bits &= 0x1f;
index ed03e78..5fb4a86 100644 (file)
@@ -17,14 +17,14 @@ extern smallint oneline;
 extern char _SL_;
 
 #ifndef IPPROTO_ESP
-#define IPPROTO_ESP    50
+#define IPPROTO_ESP  50
 #endif
 #ifndef IPPROTO_AH
-#define IPPROTO_AH     51
+#define IPPROTO_AH  51
 #endif
 
 #define SPRINT_BSIZE 64
-#define SPRINT_BUF(x)  char x[SPRINT_BSIZE]
+#define SPRINT_BUF(x)  char x[SPRINT_BSIZE]
 
 extern void incomplete_command(void) NORETURN;
 
@@ -58,9 +58,9 @@ struct ipx_addr {
 
 extern uint32_t get_addr32(char *name);
 extern int get_addr_1(inet_prefix *dst, char *arg, int family);
-/*extern int get_prefix_1(inet_prefix *dst, char *arg, int family);*/
+/*extern void get_prefix_1(inet_prefix *dst, char *arg, int family);*/
 extern int get_addr(inet_prefix *dst, char *arg, int family);
-extern int get_prefix(inet_prefix *dst, char *arg, int family);
+extern void get_prefix(inet_prefix *dst, char *arg, int family);
 
 extern unsigned get_unsigned(char *arg, const char *errmsg);
 extern uint32_t get_u32(char *arg, const char *errmsg);
@@ -77,7 +77,7 @@ extern const char *format_host(int af, int len, void *addr, char *buf, int bufle
 void invarg(const char *, const char *) NORETURN;
 void duparg(const char *, const char *) NORETURN;
 void duparg2(const char *, const char *) NORETURN;
-int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits);
+int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits);
 
 const char *dnet_ntop(int af, const void *addr, char *str, size_t len);
 int dnet_pton(int af, const char *src, void *addr);
index 046e308..9a8846d 100644 (file)
@@ -7,9 +7,68 @@
  *                     Glenn McGrath
  * Extended matching support 2008 by Nico Erfurth <masta@perlgolf.de>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//config:config NAMEIF
+//config:      bool "nameif"
+//config:      default y
+//config:      select PLATFORM_LINUX
+//config:      select FEATURE_SYSLOG
+//config:      help
+//config:        nameif is used to rename network interface by its MAC address.
+//config:        Renamed interfaces MUST be in the down state.
+//config:        It is possible to use a file (default: /etc/mactab)
+//config:        with list of new interface names and MACs.
+//config:        Maximum interface name length: IFNAMSIZ = 16
+//config:        File fields are separated by space or tab.
+//config:        File format:
+//config:        # Comment
+//config:        new_interface_name    XX:XX:XX:XX:XX:XX
+//config:
+//config:config FEATURE_NAMEIF_EXTENDED
+//config:      bool "Extended nameif"
+//config:      default y
+//config:      depends on NAMEIF
+//config:      help
+//config:        This extends the nameif syntax to support the bus_info, driver,
+//config:        phyaddr selectors. The syntax is compatible to the normal nameif.
+//config:        File format:
+//config:          new_interface_name  driver=asix bus=usb-0000:00:08.2-3
+//config:          new_interface_name  bus=usb-0000:00:08.2-3 00:80:C8:38:91:B5
+//config:          new_interface_name  phy_address=2 00:80:C8:38:91:B5
+//config:          new_interface_name  mac=00:80:C8:38:91:B5
+//config:          new_interface_name  00:80:C8:38:91:B5
+
+//usage:#define nameif_trivial_usage
+//usage:       IF_NOT_FEATURE_NAMEIF_EXTENDED(
+//usage:               "[-s] [-c FILE] [IFNAME HWADDR]..."
+//usage:       )
+//usage:       IF_FEATURE_NAMEIF_EXTENDED(
+//usage:               "[-s] [-c FILE] [IFNAME SELECTOR]..."
+//usage:       )
+//usage:#define nameif_full_usage "\n\n"
+//usage:       "Rename network interface while it in the down state."
+//usage:       IF_NOT_FEATURE_NAMEIF_EXTENDED(
+//usage:     "\nThe device with address HWADDR is renamed to IFACE."
+//usage:       )
+//usage:       IF_FEATURE_NAMEIF_EXTENDED(
+//usage:     "\nThe device matched by SELECTOR is renamed to IFACE."
+//usage:     "\nSELECTOR can be a combination of:"
+//usage:     "\n       driver=STRING"
+//usage:     "\n       bus=STRING"
+//usage:     "\n       phy_address=NUM"
+//usage:     "\n       [mac=]XX:XX:XX:XX:XX:XX"
+//usage:       )
+//usage:     "\n"
+//usage:     "\n       -c FILE Configuration file (default: /etc/mactab)"
+//usage:     "\n       -s      Log to syslog"
+//usage:
+//usage:#define nameif_example_usage
+//usage:       "$ nameif -s dmz0 00:A0:C9:8C:F6:3F\n"
+//usage:       " or\n"
+//usage:       "$ nameif -c /etc/my_mactab_file\n"
+
 #include "libbb.h"
 #include <syslog.h>
 #include <net/if.h>
 #endif
 
 /* Taken from linux/sockios.h */
-#define SIOCSIFNAME    0x8923  /* set interface name */
+#define SIOCSIFNAME  0x8923  /* set interface name */
 
 /* Octets in one Ethernet addr, from <linux/if_ether.h> */
-#define ETH_ALEN       6
+#define ETH_ALEN     6
 
 #ifndef ifr_newname
 #define ifr_newname ifr_ifru.ifru_slave
@@ -38,6 +97,7 @@ typedef struct ethtable_s {
 #if ENABLE_FEATURE_NAMEIF_EXTENDED
        char *bus_info;
        char *driver;
+       int32_t phy_address;
 #endif
 } ethtable_t;
 
@@ -59,6 +119,25 @@ struct ethtool_drvinfo {
        uint32_t eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */
        uint32_t regdump_len;  /* Size of data from ETHTOOL_GREGS (bytes) */
 };
+
+struct ethtool_cmd {
+       uint32_t   cmd;
+       uint32_t   supported;      /* Features this interface supports */
+       uint32_t   advertising;    /* Features this interface advertises */
+       uint16_t   speed;          /* The forced speed, 10Mb, 100Mb, gigabit */
+       uint8_t    duplex;         /* Duplex, half or full */
+       uint8_t    port;           /* Which connector port */
+       uint8_t    phy_address;
+       uint8_t    transceiver;    /* Which transceiver to use */
+       uint8_t    autoneg;        /* Enable or disable autonegotiation */
+       uint32_t   maxtxpkt;       /* Tx pkts before generating tx int */
+       uint32_t   maxrxpkt;       /* Rx pkts before generating rx int */
+       uint16_t   speed_hi;
+       uint16_t   reserved2;
+       uint32_t   reserved[3];
+};
+
+#define ETHTOOL_GSET      0x00000001 /* Get settings. */
 #define ETHTOOL_GDRVINFO  0x00000003 /* Get driver info. */
 #endif
 
@@ -74,6 +153,7 @@ static void nameif_parse_selector(ethtable_t *ch, char *selector)
 #endif
                selector = skip_whitespace(selector);
 #if ENABLE_FEATURE_NAMEIF_EXTENDED
+               ch->phy_address = -1;
                if (*selector == '\0')
                        break;
                /* Search for the end .... */
@@ -87,6 +167,9 @@ static void nameif_parse_selector(ethtable_t *ch, char *selector)
                } else if (strncmp(selector, "driver=", 7) == 0) {
                        ch->driver = xstrdup(selector + 7);
                        found_selector++;
+               } else if (strncmp(selector, "phyaddr=", 8) == 0) {
+                       ch->phy_address = xatoi_positive(selector + 8);
+                       found_selector++;
                } else {
 #endif
                        lmac = xmalloc(ETH_ALEN);
@@ -133,7 +216,7 @@ void delete_eth_table(ethtable_t *ch);
 #endif
 
 int nameif_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int nameif_main(int argc, char **argv)
+int nameif_main(int argc UNUSED_PARAM, char **argv)
 {
        ethtable_t *clist = NULL;
        const char *fname = "/etc/mactab";
@@ -148,17 +231,15 @@ int nameif_main(int argc, char **argv)
                 * can't hurt. 2>/dev/null if you don't like it: */
                logmode |= LOGMODE_SYSLOG;
        }
-       argc -= optind;
        argv += optind;
 
-       if (argc & 1)
-               bb_show_usage();
-
-       if (argc) {
-               while (*argv) {
-                       char *ifname = xstrdup(*argv++);
-                       prepend_new_eth_table(&clist, ifname, *argv++);
-               }
+       if (argv[0]) {
+               do {
+                       if (!argv[1])
+                               bb_show_usage();
+                       prepend_new_eth_table(&clist, argv[0], argv[1]);
+                       argv += 2;
+               } while (*argv);
        } else {
                parser = config_open(fname);
                while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL))
@@ -173,8 +254,9 @@ int nameif_main(int argc, char **argv)
                struct ifreq ifr;
 #if  ENABLE_FEATURE_NAMEIF_EXTENDED
                struct ethtool_drvinfo drvinfo;
+               struct ethtool_cmd eth_settings;
 #endif
-               if (parser->lineno < 2)
+               if (parser->lineno <= 2)
                        continue; /* Skip the first two lines */
 
                /* Find the current interface name and copy it to ifr.ifr_name */
@@ -182,8 +264,14 @@ int nameif_main(int argc, char **argv)
                strncpy_IFNAMSIZ(ifr.ifr_name, token[0]);
 
 #if ENABLE_FEATURE_NAMEIF_EXTENDED
+               /* Check for phy address */
+               memset(&eth_settings, 0, sizeof(eth_settings));
+               eth_settings.cmd = ETHTOOL_GSET;
+               ifr.ifr_data = (caddr_t) &eth_settings;
+               ioctl(ctl_sk, SIOCETHTOOL, &ifr);
+
                /* Check for driver etc. */
-               memset(&drvinfo, 0, sizeof(struct ethtool_drvinfo));
+               memset(&drvinfo, 0, sizeof(drvinfo));
                drvinfo.cmd = ETHTOOL_GDRVINFO;
                ifr.ifr_data = (caddr_t) &drvinfo;
                /* Get driver and businfo first, so we have it in drvinfo */
@@ -198,16 +286,17 @@ int nameif_main(int argc, char **argv)
                                continue;
                        if (ch->driver && strcmp(ch->driver, drvinfo.driver) != 0)
                                continue;
+                       if (ch->phy_address != -1 && ch->phy_address != eth_settings.phy_address)
+                               continue;
 #endif
                        if (ch->mac && memcmp(ch->mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN) != 0)
                                continue;
                        /* if we came here, all selectors have matched */
-                       break;
+                       goto found;
                }
                /* Nothing found for current interface */
-               if (!ch)
-                       continue;
-
+               continue;
+ found:
                if (strcmp(ifr.ifr_name, ch->ifname) != 0) {
                        strcpy(ifr.ifr_newname, ch->ifname);
                        ioctl_or_perror_and_die(ctl_sk, SIOCSIFNAME, &ifr,
@@ -223,10 +312,14 @@ int nameif_main(int argc, char **argv)
                        ch->next->prev = ch->prev;
                if (ENABLE_FEATURE_CLEAN_UP)
                        delete_eth_table(ch);
-       }
+       } /* while */
+
        if (ENABLE_FEATURE_CLEAN_UP) {
-               for (ch = clist; ch; ch = ch->next)
+               ethtable_t *next;
+               for (ch = clist; ch; ch = next) {
+                       next = ch->next;
                        delete_eth_table(ch);
+               }
                config_close(parser);
        };
 
diff --git a/networking/nbd-client.c b/networking/nbd-client.c
new file mode 100644 (file)
index 0000000..cadda52
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2010 Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+#include <netinet/tcp.h>
+#include <linux/fs.h>
+
+//applet:IF_NBDCLIENT(APPLET_ODDNAME(nbd-client, nbdclient, BB_DIR_USR_SBIN, BB_SUID_DROP, nbdclient))
+
+//kbuild:lib-$(CONFIG_NBDCLIENT) += nbd-client.o
+
+//config:config NBDCLIENT
+//config:      bool "nbd-client"
+//config:      default y
+//config:      help
+//config:        Network block device client
+
+#define NBD_SET_SOCK          _IO(0xab, 0)
+#define NBD_SET_BLKSIZE       _IO(0xab, 1)
+#define NBD_SET_SIZE          _IO(0xab, 2)
+#define NBD_DO_IT             _IO(0xab, 3)
+#define NBD_CLEAR_SOCK        _IO(0xab, 4)
+#define NBD_CLEAR_QUEUE       _IO(0xab, 5)
+#define NBD_PRINT_DEBUG       _IO(0xab, 6)
+#define NBD_SET_SIZE_BLOCKS   _IO(0xab, 7)
+#define NBD_DISCONNECT        _IO(0xab, 8)
+#define NBD_SET_TIMEOUT       _IO(0xab, 9)
+
+//usage:#define nbdclient_trivial_usage
+//usage:       "HOST PORT BLOCKDEV"
+//usage:#define nbdclient_full_usage "\n\n"
+//usage:       "Connect to HOST and provide a network block device on BLOCKDEV"
+
+//TODO: more compat with nbd-client version 2.9.13 -
+//Usage: nbd-client [bs=blocksize] [timeout=sec] host port nbd_device [-swap] [-persist] [-nofork]
+//Or   : nbd-client -d nbd_device
+//Or   : nbd-client -c nbd_device
+//Default value for blocksize is 1024 (recommended for ethernet)
+//Allowed values for blocksize are 512,1024,2048,4096
+//Note, that kernel 2.4.2 and older ones do not work correctly with
+//blocksizes other than 1024 without patches
+
+int nbdclient_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int nbdclient_main(int argc, char **argv)
+{
+       unsigned long timeout = 0;
+#if BB_MMU
+       int nofork = 0;
+#endif
+       char *host, *port, *device;
+       struct nbd_header_t {
+               uint64_t magic1; // "NBDMAGIC"
+               uint64_t magic2; // 0x420281861253 big endian
+               uint64_t devsize;
+               uint32_t flags;
+               char data[124];
+       } nbd_header;
+       struct bug_check {
+               char c[offsetof(struct nbd_header_t, data) == 8+8+8+4 ? 1 : -1];
+       };
+
+       // Parse command line stuff (just a stub now)
+       if (argc != 4)
+               bb_show_usage();
+
+#if !BB_MMU
+       bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
+#endif
+
+       host = argv[1];
+       port = argv[2];
+       device = argv[3];
+
+       // Repeat until spanked (-persist behavior)
+       for (;;) {
+               int sock, nbd;
+               int ro;
+
+               // Make sure the /dev/nbd exists
+               nbd = xopen(device, O_RDWR);
+
+               // Find and connect to server
+               sock = create_and_connect_stream_or_die(host, xatou16(port));
+               setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1));
+
+               // Log on to the server
+               xread(sock, &nbd_header, 8+8+8+4 + 124);
+               if (memcmp(&nbd_header.magic1, "NBDMAGIC""\x00\x00\x42\x02\x81\x86\x12\x53", 16) != 0)
+                       bb_error_msg_and_die("login failed");
+
+               // Set 4k block size.  Everything uses that these days
+               ioctl(nbd, NBD_SET_BLKSIZE, 4096);
+               ioctl(nbd, NBD_SET_SIZE_BLOCKS, SWAP_BE64(nbd_header.devsize) / 4096);
+               ioctl(nbd, NBD_CLEAR_SOCK);
+
+               // If the sucker was exported read only, respect that locally
+               ro = (nbd_header.flags & SWAP_BE32(2)) / SWAP_BE32(2);
+               if (ioctl(nbd, BLKROSET, &ro) < 0)
+                       bb_perror_msg_and_die("BLKROSET");
+
+               if (timeout)
+                       if (ioctl(nbd, NBD_SET_TIMEOUT, timeout))
+                               bb_perror_msg_and_die("NBD_SET_TIMEOUT");
+               if (ioctl(nbd, NBD_SET_SOCK, sock))
+                       bb_perror_msg_and_die("NBD_SET_SOCK");
+
+               // if (swap) mlockall(MCL_CURRENT|MCL_FUTURE);
+
+#if BB_MMU
+               // Open the device to force reread of the partition table.
+               // Need to do it in a separate process, since open(device)
+               // needs some other process to sit in ioctl(nbd, NBD_DO_IT).
+               if (fork() == 0) {
+                       char *s = strrchr(device, '/');
+                       sprintf(nbd_header.data, "/sys/block/%.32s/pid", s ? s + 1 : device);
+                       // Is it up yet?
+                       for (;;) {
+                               int fd = open(nbd_header.data, O_RDONLY);
+                               if (fd >= 0) {
+                                       //close(fd);
+                                       break;
+                               }
+                               sleep(1);
+                       }
+                       open(device, O_RDONLY);
+                       return 0;
+               }
+
+               // Daemonize here
+               if (!nofork) {
+                       daemon(0, 0);
+                       nofork = 1;
+               }
+#endif
+
+               // This turns us (the process that calls this ioctl)
+               // into a dedicated NBD request handler.
+               // We block here for a long time.
+               // When exactly ioctl returns? On a signal,
+               // or if someone does ioctl(NBD_DISCONNECT) [nbd-client -d].
+               if (ioctl(nbd, NBD_DO_IT) >= 0 || errno == EBADR) {
+                       // Flush queue and exit
+                       ioctl(nbd, NBD_CLEAR_QUEUE);
+                       ioctl(nbd, NBD_CLEAR_SOCK);
+                       break;
+               }
+
+               close(sock);
+               close(nbd);
+       }
+
+       return 0;
+}
index c771374..2f9e174 100644 (file)
@@ -1,10 +1,10 @@
 /* vi: set sw=4 ts=4: */
-/*  nc: mini-netcat - built from the ground up for LRP
+/* nc: mini-netcat - built from the ground up for LRP
  *
- *  Copyright (C) 1998, 1999  Charles P. Wright
- *  Copyright (C) 1998  Dave Cinege
+ * Copyright (C) 1998, 1999  Charles P. Wright
+ * Copyright (C) 1998  Dave Cinege
  *
- *  Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
@@ -24,7 +24,7 @@
 //config:        Allow netcat to act as a server.
 //config:
 //config:config NC_EXTRA
-//config:      bool "Netcat extensions (-eiw and filename)"
+//config:      bool "Netcat extensions (-eiw and -f FILE)"
 //config:      default y
 //config:      depends on NC
 //config:      help
@@ -40,7 +40,7 @@
 //config:        This option makes nc closely follow original nc-1.10.
 //config:        The code is about 2.5k bigger. It enables
 //config:        -s ADDR, -n, -u, -v, -o FILE, -z options, but loses
-//config:        busybox-specific extensions: -f FILE and -ll.
+//config:        busybox-specific extensions: -f FILE.
 
 #if ENABLE_NC_110_COMPAT
 # include "nc_bloaty.c"
@@ -49,7 +49,7 @@
 //usage:#if !ENABLE_NC_110_COMPAT
 //usage:
 //usage:#if ENABLE_NC_SERVER || ENABLE_NC_EXTRA
-//usage:#define NC_OPTIONS_STR "\n\nOptions:"
+//usage:#define NC_OPTIONS_STR "\n"
 //usage:#else
 //usage:#define NC_OPTIONS_STR
 //usage:#endif
 //usage:#define nc_full_usage "\n\n"
 //usage:       "Open a pipe to IP:PORT" IF_NC_EXTRA(" or FILE")
 //usage:       NC_OPTIONS_STR
-//usage:       IF_NC_EXTRA(
-//usage:     "\n       -e PROG Run PROG after connect"
 //usage:       IF_NC_SERVER(
 //usage:     "\n       -l      Listen mode, for inbound connects"
 //usage:       IF_NC_EXTRA(
-//usage:     "\n               (use -l twice with -e for persistent server)")
+//usage:     "\n               (use -ll with -e for persistent server)"
+//usage:       )
 //usage:     "\n       -p PORT Local port"
 //usage:       )
-//usage:     "\n       -w SEC  Timeout for connect"
+//usage:       IF_NC_EXTRA(
+//usage:     "\n       -w SEC  Connect timeout"
 //usage:     "\n       -i SEC  Delay interval for lines sent"
 //usage:     "\n       -f FILE Use file (ala /dev/ttyS0) instead of network"
+//usage:     "\n       -e PROG Run PROG after connect"
 //usage:       )
 //usage:
 //usage:#define nc_notes_usage ""
@@ -120,7 +121,7 @@ int nc_main(int argc, char **argv)
                /* getopt32 is _almost_ usable:
                ** it cannot handle "... -e PROG -prog-opt" */
                while ((opt = getopt(argc, argv,
-                       "" IF_NC_SERVER("lp:") IF_NC_EXTRA("w:i:f:e:") )) > 0
+                       "" IF_NC_SERVER("lp:") IF_NC_EXTRA("w:i:f:e:") )) > 0
                ) {
                        if (ENABLE_NC_SERVER && opt == 'l')
                                IF_NC_SERVER(do_listen++);
@@ -147,7 +148,7 @@ int nc_main(int argc, char **argv)
                                                *p++ = argv[optind++];
                                        }
                                )
-                               /* optind points to argv[arvc] (NULL) now.
+                               /* optind points to argv[argc] (NULL) now.
                                ** FIXME: we assume that getopt will not count options
                                ** possibly present on "-e PROG ARGS" and will not
                                ** include them into final value of optind
@@ -226,10 +227,9 @@ int nc_main(int argc, char **argv)
                /* child, or main thread if only one -l */
                xmove_fd(cfd, 0);
                xdup2(0, 1);
-               xdup2(0, 2);
+               /*xdup2(0, 2); - original nc 1.10 does this, we don't */
                IF_NC_EXTRA(BB_EXECVP(execparam[0], execparam);)
-               /* Don't print stuff or it will go over the wire... */
-               _exit(127);
+               IF_NC_EXTRA(bb_perror_msg_and_die("can't execute '%s'", execparam[0]);)
        }
 
        /* Select loop copying stdin to cfd, and cfd to stdout */
@@ -261,7 +261,7 @@ int nc_main(int argc, char **argv)
                                        if (nread < 1) {
                                                /* Close outgoing half-connection so they get EOF,
                                                 * but leave incoming alone so we can see response */
-                                               shutdown(cfd, 1);
+                                               shutdown(cfd, SHUT_WR);
                                                FD_CLR(STDIN_FILENO, &readfds);
                                        }
                                        ofd = cfd;
index aebb9cb..b9eff3d 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2007 Denys Vlasenko.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /* Author's comments from nc 1.10:
  * - TCP connects from wrong ip/ports (if peer ip:port is specified
  *   on the command line, but accept() says that it came from different addr)
  *   are closed, but we don't exit - we continue to listen/accept.
+ * Since bbox 1.22:
+ * - nc exits when _both_ stdin and network are closed.
+ *   This makes these two commands:
+ *    echo "Yes" | nc 127.0.0.1 1234
+ *    echo "no" | nc -lp 1234
+ *   exchange their data _and exit_ instead of being stuck.
  */
 
 /* done in nc.c: #include "libbb.h" */
 //usage:#define nc_trivial_usage
 //usage:       "[OPTIONS] HOST PORT  - connect"
 //usage:       IF_NC_SERVER("\n"
-//usage:       "nc [OPTIONS] -l -p PORT [HOST] [PORT]  - listen")
+//usage:       "nc [OPTIONS] -l -p PORT [HOST] [PORT]  - listen"
+//usage:       )
 //usage:#define nc_full_usage "\n\n"
-//usage:       "Options:"
-//usage:     "\n       -e PROG Run PROG after connect (must be last)"
+//usage:       "       -e PROG Run PROG after connect (must be last)"
 //usage:       IF_NC_SERVER(
 //usage:     "\n       -l      Listen mode, for inbound connects"
+//usage:     "\n       -lk     With -e, provides persistent server"
+/* -ll does the same as -lk, but its our extension, while -k is BSD'd,
+ * presumably more widely known. Therefore we advertise it, not -ll.
+ * I would like to drop -ll support, but our "small" nc supports it,
+ * and Rob uses it.
+ */
 //usage:       )
 //usage:     "\n       -p PORT Local port"
 //usage:     "\n       -s ADDR Local address"
@@ -115,6 +127,7 @@ struct globals {
        unsigned wrote_out;          /* total stdout bytes */
        unsigned wrote_net;          /* total net bytes */
 #endif
+       char *proggie0saved;
        /* ouraddr is never NULL and goes through three states as we progress:
         1 - local address before bind (IP/port possibly zero)
         2 - local address after bind (port is nonzero)
@@ -127,9 +140,6 @@ struct globals {
 
        jmp_buf jbuf;                /* timer crud */
 
-       /* will malloc up the following globals: */
-       fd_set ding1;                /* for select loop */
-       fd_set ding2;
        char bigbuf_in[BIGSIZ];      /* data buffers */
        char bigbuf_net[BIGSIZ];
 };
@@ -141,8 +151,6 @@ struct globals {
 #define themaddr   (G.themaddr  )
 #define remend     (G.remend    )
 #define jbuf       (G.jbuf      )
-#define ding1      (G.ding1     )
-#define ding2      (G.ding2     )
 #define bigbuf_in  (G.bigbuf_in )
 #define bigbuf_net (G.bigbuf_net)
 #define o_verbose  (G.o_verbose )
@@ -159,26 +167,21 @@ struct globals {
 
 /* Must match getopt32 call! */
 enum {
-       OPT_h = (1 << 0),
-       OPT_n = (1 << 1),
-       OPT_p = (1 << 2),
-       OPT_s = (1 << 3),
-       OPT_u = (1 << 4),
-       OPT_v = (1 << 5),
-       OPT_w = (1 << 6),
-       OPT_l = (1 << 7) * ENABLE_NC_SERVER,
-       OPT_i = (1 << (7+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
-       OPT_o = (1 << (8+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
-       OPT_z = (1 << (9+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
+       OPT_n = (1 << 0),
+       OPT_p = (1 << 1),
+       OPT_s = (1 << 2),
+       OPT_u = (1 << 3),
+       OPT_v = (1 << 4),
+       OPT_w = (1 << 5),
+       OPT_l = (1 << 6) * ENABLE_NC_SERVER,
+       OPT_k = (1 << 7) * ENABLE_NC_SERVER,
+       OPT_i = (1 << (7+2*ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
+       OPT_o = (1 << (8+2*ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
+       OPT_z = (1 << (9+2*ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
 };
 
 #define o_nflag   (option_mask32 & OPT_n)
 #define o_udpmode (option_mask32 & OPT_u)
-#if ENABLE_NC_SERVER
-#define o_listen  (option_mask32 & OPT_l)
-#else
-#define o_listen  0
-#endif
 #if ENABLE_NC_EXTRA
 #define o_ofile   (option_mask32 & OPT_o)
 #define o_zero    (option_mask32 & OPT_z)
@@ -263,12 +266,13 @@ Debug("findline returning whole thing: %d", siz);
 static int doexec(char **proggie) NORETURN;
 static int doexec(char **proggie)
 {
+       if (G.proggie0saved)
+               proggie[0] = G.proggie0saved;
        xmove_fd(netfd, 0);
        dup2(0, 1);
        /* dup2(0, 2); - do we *really* want this? NO!
         * exec'ed prog can do it yourself, if needed */
-       execvp(proggie[0], proggie);
-       bb_perror_msg_and_die("can't execute '%s'", proggie[0]);
+       BB_EXECVP_or_die(proggie);
 }
 
 /* connect_w_timeout:
@@ -298,7 +302,7 @@ static int connect_w_timeout(int fd)
  incoming and returns an open connection *from* someplace.  If we were
  given host/port args, any connections from elsewhere are rejected.  This
  in conjunction with local-address binding should limit things nicely... */
-static void dolisten(void)
+static void dolisten(int is_persistent, char **proggie)
 {
        int rr;
 
@@ -371,6 +375,7 @@ create new one, and bind() it. TODO */
                        xconnect(netfd, &remend.u.sa, ouraddr->len);
        } else {
                /* TCP */
+ another:
                arm(o_wait); /* wrap this in a timer, too; 0 = forever */
                if (setjmp(jbuf) == 0) {
  again:
@@ -386,10 +391,10 @@ create new one, and bind() it. TODO */
                                if (port == 0) {
                                        /* "nc -nl -p LPORT RHOST" (w/o RPORT!):
                                         * we should accept any remote port */
-                                       set_nport(&remend, 0); /* blot out remote port# */
+                                       set_nport(&remend.u.sa, 0); /* blot out remote port# */
                                }
                                r = memcmp(&remend.u.sa, &themaddr->u.sa, remend.len);
-                               set_nport(&remend, sv_port); /* restore */
+                               set_nport(&remend.u.sa, sv_port); /* restore */
                                if (r != 0) {
                                        /* nc 1.10 bails out instead, and its error message
                                         * is not suppressed by o_verbose */
@@ -405,6 +410,19 @@ create new one, and bind() it. TODO */
                        unarm();
                } else
                        bb_error_msg_and_die("timeout");
+
+               if (is_persistent && proggie) {
+                       /* -l -k -e PROG */
+                       signal(SIGCHLD, SIG_IGN); /* no zombies please */
+                       if (xvfork() != 0) {
+                               /* parent: go back and accept more connections */
+                               close(rr);
+                               goto another;
+                       }
+                       /* child */
+                       signal(SIGCHLD, SIG_DFL);
+               }
+
                xmove_fd(rr, netfd); /* dump the old socket, here's our new one */
                /* find out what address the connection was *to* on our end, in case we're
                 doing a listen-on-any on a multihomed machine.  This allows one to
@@ -429,8 +447,7 @@ create new one, and bind() it. TODO */
 
                rr = getsockopt(netfd, IPPROTO_IP, IP_OPTIONS, optbuf, &x);
                if (rr >= 0 && x) {    /* we've got options, lessee em... */
-                       bin2hex(bigbuf_net, optbuf, x);
-                       bigbuf_net[2*x] = '\0';
+                       *bin2hex(bigbuf_net, optbuf, x) = '\0';
                        fprintf(stderr, "IP options: %s\n", bigbuf_net);
                }
 #endif
@@ -455,6 +472,9 @@ create new one, and bind() it. TODO */
                if (!o_nflag)
                        free(remhostname);
        }
+
+       if (proggie)
+               doexec(proggie);
 }
 
 /* udptest:
@@ -486,7 +506,7 @@ static int udptest(void)
         us to hang forever, and hit it */
                o_wait = 5;                     /* enough that we'll notice?? */
                rr = xsocket(ouraddr->u.sa.sa_family, SOCK_STREAM, 0);
-               set_nport(themaddr, htons(SLEAZE_PORT));
+               set_nport(&themaddr->u.sa, htons(SLEAZE_PORT));
                connect_w_timeout(rr);
                /* don't need to restore themaddr's port, it's not used anymore */
                close(rr);
@@ -572,26 +592,27 @@ static int readwrite(void)
        unsigned rzleft;
        unsigned rnleft;
        unsigned netretry;              /* net-read retry counter */
-       unsigned wretry;                /* net-write sanity counter */
-       unsigned wfirst;                /* one-shot flag to skip first net read */
+       unsigned fds_open;
 
        /* if you don't have all this FD_* macro hair in sys/types.h, you'll have to
         either find it or do your own bit-bashing: *ding1 |= (1 << fd), etc... */
-       FD_SET(netfd, &ding1);                /* global: the net is open */
+       fd_set ding1;                   /* for select loop */
+       fd_set ding2;
+       FD_ZERO(&ding1);
+       FD_SET(netfd, &ding1);
+       FD_SET(STDIN_FILENO, &ding1);
+       fds_open = 2;
+
        netretry = 2;
-       wfirst = 0;
        rzleft = rnleft = 0;
        if (o_interval)
                sleep(o_interval);                /* pause *before* sending stuff, too */
 
-       errno = 0;                        /* clear from sleep, close, whatever */
        /* and now the big ol' select shoveling loop ... */
-       while (FD_ISSET(netfd, &ding1)) {        /* i.e. till the *net* closes! */
-               wretry = 8200;                        /* more than we'll ever hafta write */
-               if (wfirst) {                        /* any saved stdin buffer? */
-                       wfirst = 0;                        /* clear flag for the duration */
-                       goto shovel;                        /* and go handle it first */
-               }
+       /* nc 1.10 has "while (FD_ISSET(netfd)" here */
+       while (fds_open) {
+               unsigned wretry = 8200;               /* net-write sanity counter */
+
                ding2 = ding1;                        /* FD_COPY ain't portable... */
        /* some systems, notably linux, crap into their select timers on return, so
         we create a expendable copy and give *that* to select.  */
@@ -611,13 +632,14 @@ static int readwrite(void)
        /* if we have a timeout AND stdin is closed AND we haven't heard anything
         from the net during that time, assume it's dead and close it too. */
                if (rr == 0) {
-                       if (!FD_ISSET(STDIN_FILENO, &ding1))
+                       if (!FD_ISSET(STDIN_FILENO, &ding1)) {
                                netretry--;                        /* we actually try a coupla times. */
-                       if (!netretry) {
-                               if (o_verbose > 1)                /* normally we don't care */
-                                       fprintf(stderr, "net timeout\n");
-                               close(netfd);
-                               return 0;                        /* not an error! */
+                               if (!netretry) {
+                                       if (o_verbose > 1)         /* normally we don't care */
+                                               fprintf(stderr, "net timeout\n");
+                                       /*close(netfd); - redundant, exit will do it */
+                                       return 0;                  /* not an error! */
+                               }
                        }
                } /* select timeout */
        /* xxx: should we check the exception fds too?  The read fds seem to give
@@ -631,7 +653,8 @@ static int readwrite(void)
                                        /* nc 1.10 doesn't do this */
                                        bb_perror_msg("net read");
                                }
-                               FD_CLR(netfd, &ding1);                /* net closed, we'll finish up... */
+                               FD_CLR(netfd, &ding1);                /* net closed */
+                               fds_open--;
                                rzleft = 0;                        /* can't write anymore: broken pipe */
                        } else {
                                rnleft = rr;
@@ -651,11 +674,12 @@ Debug("got %d from the net, errno %d", rr, errno);
        /* Considered making reads here smaller for UDP mode, but 8192-byte
         mobygrams are kinda fun and exercise the reassembler. */
                        if (rr <= 0) {                        /* at end, or fukt, or ... */
-                               FD_CLR(STDIN_FILENO, &ding1);                /* disable and close stdin */
-                               close(STDIN_FILENO);
-// Does it make sense to shutdown(net_fd, SHUT_WR)
-// to let other side know that we won't write anything anymore?
-// (and what about keeping compat if we do that?)
+                               FD_CLR(STDIN_FILENO, &ding1); /* disable stdin */
+                               /*close(STDIN_FILENO); - not really necessary */
+                               /* Let peer know we have no more data */
+                               /* nc 1.10 doesn't do this: */
+                               shutdown(netfd, SHUT_WR);
+                               fds_open--;
                        } else {
                                rzleft = rr;
                                zp = bigbuf_in;
@@ -666,24 +690,14 @@ Debug("got %d from the net, errno %d", rr, errno);
         Geez, why does this look an awful lot like the big loop in "rsh"? ...
         not sure if the order of this matters, but write net -> stdout first. */
 
-       /* sanity check.  Works because they're both unsigned... */
-               if ((rzleft > 8200) || (rnleft > 8200)) {
-                       holler_error("bogus buffers: %u, %u", rzleft, rnleft);
-                       rzleft = rnleft = 0;
-               }
-       /* net write retries sometimes happen on UDP connections */
-               if (!wretry) {                        /* is something hung? */
-                       holler_error("too many output retries");
-                       return 1;
-               }
                if (rnleft) {
                        rr = write(STDOUT_FILENO, np, rnleft);
                        if (rr > 0) {
                                if (o_ofile) /* log the stdout */
                                        oprint('<', (unsigned char *)np, rr);
-                               np += rr;                        /* fix up ptrs and whatnot */
-                               rnleft -= rr;                        /* will get sanity-checked above */
-                               wrote_out += rr;                /* global count */
+                               np += rr;
+                               rnleft -= rr;
+                               wrote_out += rr; /* global count */
                        }
 Debug("wrote %d to stdout, errno %d", rr, errno);
                } /* rnleft */
@@ -698,20 +712,24 @@ Debug("wrote %d to stdout, errno %d", rr, errno);
                                        oprint('>', (unsigned char *)zp, rr);
                                zp += rr;
                                rzleft -= rr;
-                               wrote_net += rr;                /* global count */
+                               wrote_net += rr; /* global count */
                        }
 Debug("wrote %d to net, errno %d", rr, errno);
                } /* rzleft */
                if (o_interval) {                        /* cycle between slow lines, or ... */
                        sleep(o_interval);
-                       errno = 0;                        /* clear from sleep */
                        continue;                        /* ...with hairy select loop... */
                }
-               if ((rzleft) || (rnleft)) {                /* shovel that shit till they ain't */
+               if (rzleft || rnleft) {                  /* shovel that shit till they ain't */
                        wretry--;                        /* none left, and get another load */
+       /* net write retries sometimes happen on UDP connections */
+                       if (!wretry) {                   /* is something hung? */
+                               holler_error("too many output retries");
+                               return 1;
+                       }
                        goto shovel;
                }
-       } /* while ding1:netfd is open */
+       } /* while (fds_open) */
 
        /* XXX: maybe want a more graceful shutdown() here, or screw around with
         linger times??  I suspect that I don't need to since I'm always doing
@@ -728,9 +746,10 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
 {
        char *str_p, *str_s;
        IF_NC_EXTRA(char *str_i, *str_o;)
-       char *themdotted = themdotted; /* gcc */
+       char *themdotted = themdotted; /* for compiler */
        char **proggie;
        int x;
+       unsigned cnt_l = 0;
        unsigned o_lport = 0;
 
        INIT_G();
@@ -756,22 +775,40 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
                        proggie++;
                        goto e_found;
                }
+               /* -<other_opts>e PROG [ARGS] ? */
+               /* (aboriginal linux uses this form) */
+               if (proggie[0][0] == '-') {
+                       char *optpos = *proggie + 1;
+                       /* Skip all valid opts w/o params */
+                       optpos = optpos + strspn(optpos, "nuv"IF_NC_SERVER("lk")IF_NC_EXTRA("z"));
+                       if (*optpos == 'e' && !optpos[1]) {
+                               *optpos = '\0';
+                               proggie++;
+                               G.proggie0saved = *proggie;
+                               *proggie = NULL; /* terminate argv for getopt32 */
+                               goto e_found;
+                       }
+               }
        }
        proggie = NULL;
  e_found:
 
        // -g -G -t -r deleted, unimplemented -a deleted too
-       opt_complementary = "?2:vv:w+"; /* max 2 params; -v is a counter; -w N */
-       getopt32(argv, "hnp:s:uvw:" IF_NC_SERVER("l")
+       opt_complementary = "?2:vv:ll:w+"; /* max 2 params; -v and -l are counters; -w N */
+       getopt32(argv, "np:s:uvw:" IF_NC_SERVER("lk")
                        IF_NC_EXTRA("i:o:z"),
                        &str_p, &str_s, &o_wait
-                       IF_NC_EXTRA(, &str_i, &str_o, &o_verbose));
+                       IF_NC_EXTRA(, &str_i, &str_o), &o_verbose IF_NC_SERVER(, &cnt_l));
        argv += optind;
 #if ENABLE_NC_EXTRA
        if (option_mask32 & OPT_i) /* line-interval time */
                o_interval = xatou_range(str_i, 1, 0xffff);
 #endif
+#if ENABLE_NC_SERVER
        //if (option_mask32 & OPT_l) /* listen mode */
+       if (option_mask32 & OPT_k) /* persistent server mode */
+               cnt_l = 2;
+#endif
        //if (option_mask32 & OPT_n) /* numeric-only, no DNS lookups */
        //if (option_mask32 & OPT_o) /* hexdump log */
        if (option_mask32 & OPT_p) { /* local source port */
@@ -813,14 +850,14 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
                                (themaddr ? themaddr->u.sa.sa_family : AF_UNSPEC),
                                x);
                if (o_lport)
-                       set_nport(ouraddr, htons(o_lport));
+                       set_nport(&ouraddr->u.sa, htons(o_lport));
        }
        xmove_fd(x, netfd);
        setsockopt_reuseaddr(netfd);
        if (o_udpmode)
                socket_want_pktinfo(netfd);
        if (!ENABLE_FEATURE_UNIX_LOCAL
-        || o_listen
+        || cnt_l != 0 /* listen */
         || ouraddr->u.sa.sa_family != AF_UNIX
        ) {
                xbind(netfd, &ouraddr->u.sa, ouraddr->len);
@@ -839,9 +876,8 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
        }
 #endif
 
-       FD_SET(STDIN_FILENO, &ding1);                        /* stdin *is* initially open */
        if (proggie) {
-               close(0); /* won't need stdin */
+               close(STDIN_FILENO); /* won't need stdin */
                option_mask32 &= ~OPT_o; /* -o with -e is meaningless! */
        }
 #if ENABLE_NC_EXTRA
@@ -849,16 +885,14 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
                xmove_fd(xopen(str_o, O_WRONLY|O_CREAT|O_TRUNC), ofd);
 #endif
 
-       if (o_listen) {
-               dolisten();
+       if (cnt_l != 0) {
+               dolisten((cnt_l - 1), proggie);
                /* dolisten does its own connect reporting */
-               if (proggie) /* -e given? */
-                       doexec(proggie);
                x = readwrite(); /* it even works with UDP! */
        } else {
                /* Outbound connects.  Now we're more picky about args... */
                if (!themaddr)
-                       bb_error_msg_and_die("no destination");
+                       bb_show_usage();
 
                remend = *themaddr;
                if (o_verbose)
index 8b7a574..f80b845 100644 (file)
  * 2008-07-10
  * optional '-p' flag support ported from net-tools by G. Somlo <somlo@cmu.edu>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
 #include "inet_common.h"
 
+//usage:#define netstat_trivial_usage
+//usage:       "[-"IF_ROUTE("r")"al] [-tuwx] [-en"IF_FEATURE_NETSTAT_WIDE("W")IF_FEATURE_NETSTAT_PRG("p")"]"
+//usage:#define netstat_full_usage "\n\n"
+//usage:       "Display networking information\n"
+//usage:       IF_ROUTE(
+//usage:     "\n       -r      Routing table"
+//usage:       )
+//usage:     "\n       -a      All sockets"
+//usage:     "\n       -l      Listening sockets"
+//usage:     "\n               Else: connected sockets"
+//usage:     "\n       -t      TCP sockets"
+//usage:     "\n       -u      UDP sockets"
+//usage:     "\n       -w      Raw sockets"
+//usage:     "\n       -x      Unix sockets"
+//usage:     "\n               Else: all socket types"
+//usage:     "\n       -e      Other/more information"
+//usage:     "\n       -n      Don't resolve names"
+//usage:       IF_FEATURE_NETSTAT_WIDE(
+//usage:     "\n       -W      Wide display"
+//usage:       )
+//usage:       IF_FEATURE_NETSTAT_PRG(
+//usage:     "\n       -p      Show PID/program name for sockets"
+//usage:       )
+
 #define NETSTAT_OPTS "laentuwx" \
        IF_ROUTE(               "r") \
        IF_FEATURE_NETSTAT_WIDE("W") \
        IF_FEATURE_NETSTAT_PRG( "p")
 
 enum {
-       OPTBIT_KEEP_OLD = 7,
-       IF_ROUTE(               OPTBIT_ROUTE,)
-       IF_FEATURE_NETSTAT_WIDE(OPTBIT_WIDE ,)
-       IF_FEATURE_NETSTAT_PRG( OPTBIT_PRG  ,)
        OPT_sock_listen = 1 << 0, // l
        OPT_sock_all    = 1 << 1, // a
        OPT_extended    = 1 << 2, // e
@@ -35,6 +55,10 @@ enum {
        OPT_sock_udp    = 1 << 5, // u
        OPT_sock_raw    = 1 << 6, // w
        OPT_sock_unix   = 1 << 7, // x
+       OPTBIT_x        = 7,
+       IF_ROUTE(               OPTBIT_ROUTE,)
+       IF_FEATURE_NETSTAT_WIDE(OPTBIT_WIDE ,)
+       IF_FEATURE_NETSTAT_PRG( OPTBIT_PRG  ,)
        OPT_route       = IF_ROUTE(               (1 << OPTBIT_ROUTE)) + 0, // r
        OPT_wide        = IF_FEATURE_NETSTAT_WIDE((1 << OPTBIT_WIDE )) + 0, // W
        OPT_prg         = IF_FEATURE_NETSTAT_PRG( (1 << OPTBIT_PRG  )) + 0, // p
@@ -88,24 +112,25 @@ typedef enum {
        SS_DISCONNECTING /* in process of disconnecting  */
 } socket_state;
 
-#define SO_ACCEPTCON (1<<16)   /* performed a listen           */
-#define SO_WAITDATA  (1<<17)   /* wait data to read            */
-#define SO_NOSPACE   (1<<18)   /* no space to write            */
-
-/* Standard printout size */
-#define PRINT_IP_MAX_SIZE           23
-#define PRINT_NET_CONN              "%s   %6ld %6ld %-23s %-23s %-12s"
-#define PRINT_NET_CONN_HEADER       "\nProto Recv-Q Send-Q %-23s %-23s State       "
+#define SO_ACCEPTCON (1<<16)  /* performed a listen           */
+#define SO_WAITDATA  (1<<17)  /* wait data to read            */
+#define SO_NOSPACE   (1<<18)  /* no space to write            */
 
+#define ADDR_NORMAL_WIDTH        23
 /* When there are IPv6 connections the IPv6 addresses will be
  * truncated to none-recognition. The '-W' option makes the
  * address columns wide enough to accomodate for longest possible
  * IPv6 addresses, i.e. addresses of the form
  * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:ddd.ddd.ddd.ddd
  */
-#define PRINT_IP_MAX_SIZE_WIDE      51  /* INET6_ADDRSTRLEN + 5 for the port number */
-#define PRINT_NET_CONN_WIDE         "%s   %6ld %6ld %-51s %-51s %-12s"
-#define PRINT_NET_CONN_HEADER_WIDE  "\nProto Recv-Q Send-Q %-51s %-51s State       "
+#define ADDR_WIDE                51  /* INET6_ADDRSTRLEN + 5 for the port number */
+#if ENABLE_FEATURE_NETSTAT_WIDE
+# define FMT_NET_CONN_DATA       "%s   %6lu %6lu %-*s %-*s %-12s"
+# define FMT_NET_CONN_HEADER     "\nProto Recv-Q Send-Q %-*s %-*s State       %s\n"
+#else
+# define FMT_NET_CONN_DATA       "%s   %6lu %6lu %-23s %-23s %-12s"
+# define FMT_NET_CONN_HEADER     "\nProto Recv-Q Send-Q %-23s %-23s State       %s\n"
+#endif
 
 #define PROGNAME_WIDTH     20
 #define PROGNAME_WIDTH_STR "20"
@@ -121,22 +146,30 @@ struct prg_node {
 #define PRG_HASH_SIZE 211
 
 struct globals {
-       const char *net_conn_line;
        smallint flags;
 #if ENABLE_FEATURE_NETSTAT_PRG
        smallint prg_cache_loaded;
        struct prg_node *prg_hash[PRG_HASH_SIZE];
 #endif
+#if ENABLE_FEATURE_NETSTAT_PRG
+       const char *progname_banner;
+#endif
+#if ENABLE_FEATURE_NETSTAT_WIDE
+       unsigned addr_width;
+#endif
 };
 #define G (*ptr_to_globals)
 #define flags            (G.flags           )
-#define net_conn_line    (G.net_conn_line   )
-#define prg_hash         (G.prg_hash        )
 #define prg_cache_loaded (G.prg_cache_loaded)
+#define prg_hash         (G.prg_hash        )
+#if ENABLE_FEATURE_NETSTAT_PRG
+# define progname_banner (G.progname_banner )
+#else
+# define progname_banner ""
+#endif
 #define INIT_G() do { \
        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
        flags = NETSTAT_CONNECTED | NETSTAT_ALLPROTO; \
-       net_conn_line = PRINT_NET_CONN; \
 } while (0)
 
 
@@ -145,10 +178,6 @@ struct globals {
 /* Deliberately truncating long to unsigned *int* */
 #define PRG_HASHIT(x) ((unsigned)(x) % PRG_HASH_SIZE)
 
-#define print_progname_banner() do { \
-       if (option_mask32 & OPT_prg) printf(PROGNAME_BANNER); \
-} while (0)
-
 static void prg_cache_add(long inode, char *name)
 {
        unsigned hi = PRG_HASHIT(inode);
@@ -158,7 +187,7 @@ static void prg_cache_add(long inode, char *name)
        for (pnp = prg_hash + hi; (pn = *pnp) != NULL; pnp = &pn->next) {
                if (pn->inode == inode) {
                        /* Some warning should be appropriate here
-                          as we got multiple processes for one i-node */
+                        * as we got multiple processes for one i-node */
                        return;
                }
        }
@@ -201,12 +230,12 @@ static long extract_socket_inode(const char *lname)
 
        if (strncmp(lname, "socket:[", sizeof("socket:[")-1) == 0) {
                /* "socket:[12345]", extract the "12345" as inode */
-               inode = bb_strtol(lname + sizeof("socket:[")-1, (char**)&lname, 0);
+               inode = bb_strtoul(lname + sizeof("socket:[")-1, (char**)&lname, 0);
                if (*lname != ']')
                        inode = -1;
        } else if (strncmp(lname, "[0000]:", sizeof("[0000]:")-1) == 0) {
                /* "[0000]:12345", extract the "12345" as inode */
-               inode = bb_strtol(lname + sizeof("[0000]:")-1, NULL, 0);
+               inode = bb_strtoul(lname + sizeof("[0000]:")-1, NULL, 0);
                if (errno) /* not NUL terminated? */
                        inode = -1;
        }
@@ -218,9 +247,9 @@ static long extract_socket_inode(const char *lname)
        return inode;
 }
 
-static int FAST_FUNC file_act(const char *fileName,
+static int FAST_FUNC add_to_prg_cache_if_socket(const char *fileName,
                struct stat *statbuf UNUSED_PARAM,
-               void *userData,
+               void *pid_slash_progname,
                int depth UNUSED_PARAM)
 {
        char *linkname;
@@ -231,7 +260,7 @@ static int FAST_FUNC file_act(const char *fileName,
                inode = extract_socket_inode(linkname);
                free(linkname);
                if (inode >= 0)
-                       prg_cache_add(inode, (char *)userData);
+                       prg_cache_add(inode, (char *)pid_slash_progname);
        }
        return TRUE;
 }
@@ -241,38 +270,40 @@ static int FAST_FUNC dir_act(const char *fileName,
                void *userData UNUSED_PARAM,
                int depth)
 {
-       const char *shortName;
-       char *p, *q;
+       const char *pid;
+       char *pid_slash_progname;
+       char proc_pid_fname[sizeof("/proc/%u/cmdline") + sizeof(long)*3];
        char cmdline_buf[512];
-       int i;
+       int n, len;
 
        if (depth == 0) /* "/proc" itself */
                return TRUE; /* continue looking one level below /proc */
 
-       shortName = fileName + sizeof("/proc/")-1; /* point after "/proc/" */
-       if (!isdigit(shortName[0])) /* skip /proc entries whic aren't processes */
+       pid = fileName + sizeof("/proc/")-1; /* point after "/proc/" */
+       if (!isdigit(pid[0])) /* skip /proc entries which aren't processes */
                return SKIP;
 
-       p = concat_path_file(fileName, "cmdline"); /* "/proc/PID/cmdline" */
-       i = open_read_close(p, cmdline_buf, sizeof(cmdline_buf) - 1);
-       free(p);
-       if (i < 0)
+       len = snprintf(proc_pid_fname, sizeof(proc_pid_fname), "%s/cmdline", fileName);
+       n = open_read_close(proc_pid_fname, cmdline_buf, sizeof(cmdline_buf) - 1);
+       if (n < 0)
                return FALSE;
-       cmdline_buf[i] = '\0';
-       q = concat_path_file(shortName, bb_basename(cmdline_buf)); /* "PID/argv0" */
-
-       /* go through all files in /proc/PID/fd */
-       p = concat_path_file(fileName, "fd");
-       i = recursive_action(p, ACTION_RECURSE | ACTION_QUIET,
-                               file_act, NULL, (void *)q, 0);
-
-       free(p);
-       free(q);
-
-       if (!i)
-               return FALSE;   /* signal permissions error to caller */
-
-       return SKIP;            /* caller should not recurse further into this dir. */
+       cmdline_buf[n] = '\0';
+
+       /* go through all files in /proc/PID/fd and check whether they are sockets */
+       strcpy(proc_pid_fname + len - (sizeof("cmdline")-1), "fd");
+       pid_slash_progname = concat_path_file(pid, bb_basename(cmdline_buf)); /* "PID/argv0" */
+       n = recursive_action(proc_pid_fname,
+                       ACTION_RECURSE | ACTION_QUIET,
+                       add_to_prg_cache_if_socket,
+                       NULL,
+                       (void *)pid_slash_progname,
+                       0);
+       free(pid_slash_progname);
+
+       if (!n)
+               return FALSE; /* signal permissions error to caller */
+
+       return SKIP; /* caller should not recurse further into this dir */
 }
 
 static void prg_cache_load(void)
@@ -294,7 +325,6 @@ static void prg_cache_load(void)
 #else
 
 #define prg_cache_clear()       ((void)0)
-#define print_progname_banner() ((void)0)
 
 #endif //ENABLE_FEATURE_NETSTAT_PRG
 
@@ -364,13 +394,16 @@ struct inet_params {
 static int scan_inet_proc_line(struct inet_params *param, char *line)
 {
        int num;
-       char local_addr[64], rem_addr[64];
+       /* IPv6 /proc files use 32-char hex representation
+        * of IPv6 address, followed by :PORT_IN_HEX
+        */
+       char local_addr[33], rem_addr[33]; /* 32 + 1 for NUL */
 
        num = sscanf(line,
-                       "%*d: %64[0-9A-Fa-f]:%X "
-                       "%64[0-9A-Fa-f]:%X %X "
+                       "%*d: %32[0-9A-Fa-f]:%X "
+                       "%32[0-9A-Fa-f]:%X %X "
                        "%lX:%lX %*X:%*X "
-                       "%*X %d %*d %ld ",
+                       "%*X %d %*d %lu ",
                        local_addr, &param->local_port,
                        rem_addr, &param->rem_port, &param->state,
                        &param->txq, &param->rxq,
@@ -403,8 +436,11 @@ static void print_inet_line(struct inet_params *param,
                char *r = ip_port_str(
                                &param->remaddr.sa, param->rem_port,
                                proto, flags & NETSTAT_NUMERIC);
-               printf(net_conn_line,
-                       proto, param->rxq, param->txq, l, r, state_str);
+               printf(FMT_NET_CONN_DATA,
+                       proto, param->rxq, param->txq,
+                       IF_FEATURE_NETSTAT_WIDE(G.addr_width,) l,
+                       IF_FEATURE_NETSTAT_WIDE(G.addr_width,) r,
+                       state_str);
 #if ENABLE_FEATURE_NETSTAT_PRG
                if (option_mask32 & OPT_prg)
                        printf("%."PROGNAME_WIDTH_STR"s", prg_cache_get(param->inode));
@@ -419,6 +455,7 @@ static int FAST_FUNC tcp_do_one(char *line)
 {
        struct inet_params param;
 
+       memset(&param, 0, sizeof(param));
        if (scan_inet_proc_line(&param, line))
                return 1;
 
@@ -446,6 +483,7 @@ static int FAST_FUNC udp_do_one(char *line)
        const char *state_str;
        struct inet_params param;
 
+       memset(&param, 0, sizeof(param)); /* otherwise we display garbage IPv6 scope_ids */
        if (scan_inet_proc_line(&param, line))
                return 1;
 
@@ -573,7 +611,7 @@ static int FAST_FUNC unix_do_one(char *line)
                strcat(ss_flags, "N ");
        strcat(ss_flags, "]");
 
-       printf("%-5s %-6ld %-11s %-10s %-13s %6lu ",
+       printf("%-5s %-6lu %-11s %-10s %-13s %6lu ",
                ss_proto, refcnt, ss_flags, ss_type, ss_state, inode
                );
 
@@ -617,38 +655,39 @@ static void do_info(const char *file, int FAST_FUNC (*proc)(char *))
 int netstat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int netstat_main(int argc UNUSED_PARAM, char **argv)
 {
-       const char *net_conn_line_header = PRINT_NET_CONN_HEADER;
        unsigned opt;
 
        INIT_G();
 
        /* Option string must match NETSTAT_xxx constants */
        opt = getopt32(argv, NETSTAT_OPTS);
-       if (opt & 0x1) { // -l
+       if (opt & OPT_sock_listen) { // -l
                flags &= ~NETSTAT_CONNECTED;
                flags |= NETSTAT_LISTENING;
        }
-       if (opt & 0x2) flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED; // -a
-       //if (opt & 0x4) // -e
-       if (opt & 0x8) flags |= NETSTAT_NUMERIC; // -n
-       //if (opt & 0x10) // -t: NETSTAT_TCP
-       //if (opt & 0x20) // -u: NETSTAT_UDP
-       //if (opt & 0x40) // -w: NETSTAT_RAW
-       //if (opt & 0x80) // -x: NETSTAT_UNIX
-       if (opt & OPT_route) { // -r
+       if (opt & OPT_sock_all) flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED; // -a
+       //if (opt & OPT_extended) // -e
+       if (opt & OPT_noresolve) flags |= NETSTAT_NUMERIC; // -n
+       //if (opt & OPT_sock_tcp) // -t: NETSTAT_TCP
+       //if (opt & OPT_sock_udp) // -u: NETSTAT_UDP
+       //if (opt & OPT_sock_raw) // -w: NETSTAT_RAW
+       //if (opt & OPT_sock_unix) // -x: NETSTAT_UNIX
 #if ENABLE_ROUTE
+       if (opt & OPT_route) { // -r
                bb_displayroutes(flags & NETSTAT_NUMERIC, !(opt & OPT_extended));
                return 0;
-#else
-               bb_show_usage();
-#endif
        }
+#endif
+#if ENABLE_FEATURE_NETSTAT_WIDE
+       G.addr_width = ADDR_NORMAL_WIDTH;
        if (opt & OPT_wide) { // -W
-               net_conn_line = PRINT_NET_CONN_WIDE;
-               net_conn_line_header = PRINT_NET_CONN_HEADER_WIDE;
+               G.addr_width = ADDR_WIDE;
        }
+#endif
 #if ENABLE_FEATURE_NETSTAT_PRG
+       progname_banner = "";
        if (opt & OPT_prg) { // -p
+               progname_banner = PROGNAME_BANNER;
                prg_cache_load();
        }
 #endif
@@ -659,7 +698,7 @@ int netstat_main(int argc UNUSED_PARAM, char **argv)
                flags |= opt;
        }
        if (flags & (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
-               printf("Active Internet connections "); /* xxx */
+               printf("Active Internet connections "); /* xxx */
 
                if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED))
                        printf("(servers and established)");
@@ -667,9 +706,11 @@ int netstat_main(int argc UNUSED_PARAM, char **argv)
                        printf("(only servers)");
                else
                        printf("(w/o servers)");
-               printf(net_conn_line_header, "Local Address", "Foreign Address");
-               print_progname_banner();
-               bb_putchar('\n');
+               printf(FMT_NET_CONN_HEADER,
+                               IF_FEATURE_NETSTAT_WIDE(G.addr_width,) "Local Address",
+                               IF_FEATURE_NETSTAT_WIDE(G.addr_width,) "Foreign Address",
+                               progname_banner
+               );
        }
        if (flags & NETSTAT_TCP) {
                do_info("/proc/net/tcp", tcp_do_one);
@@ -697,9 +738,7 @@ int netstat_main(int argc UNUSED_PARAM, char **argv)
                        printf("(only servers)");
                else
                        printf("(w/o servers)");
-               printf("\nProto RefCnt Flags       Type       State         I-Node ");
-               print_progname_banner();
-               printf("Path\n");
+               printf("\nProto RefCnt Flags       Type       State         I-Node %sPath\n", progname_banner);
                do_info("/proc/net/unix", unix_do_one);
        }
        prg_cache_clear();
index 2628711..dd4b1ff 100644 (file)
@@ -8,9 +8,23 @@
  * Correct default name server display and explicit name server option
  * added by Ben Zeckel <bzeckel@hmc.edu> June 2001
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define nslookup_trivial_usage
+//usage:       "[HOST] [SERVER]"
+//usage:#define nslookup_full_usage "\n\n"
+//usage:       "Query the nameserver for the IP address of the given HOST\n"
+//usage:       "optionally using a specified DNS server"
+//usage:
+//usage:#define nslookup_example_usage
+//usage:       "$ nslookup localhost\n"
+//usage:       "Server:     default\n"
+//usage:       "Address:    default\n"
+//usage:       "\n"
+//usage:       "Name:       debian\n"
+//usage:       "Address:    127.0.0.1\n"
+
 #include <resolv.h>
 #include "libbb.h"
 
@@ -66,7 +80,7 @@ static int print_host(const char *hostname, const char *header)
        // hint.ai_flags = AI_CANONNAME;
        rc = getaddrinfo(hostname, NULL /*service*/, &hint, &result);
 
-       if (!rc) {
+       if (rc == 0) {
                struct addrinfo *cur = result;
                unsigned cnt = 0;
 
@@ -94,7 +108,7 @@ static int print_host(const char *hostname, const char *header)
                bb_error_msg("can't resolve '%s'", hostname);
 #endif
        }
-       if (ENABLE_FEATURE_CLEAN_UP)
+       if (ENABLE_FEATURE_CLEAN_UP && result)
                freeaddrinfo(result);
        return (rc != 0);
 }
@@ -124,6 +138,9 @@ static void set_default_dns(const char *server)
 {
        len_and_sockaddr *lsa;
 
+       if (!server)
+               return;
+
        /* NB: this works even with, say, "[::1]:5353"! :) */
        lsa = xhost2sockaddr(server, 53);
 
@@ -167,9 +184,17 @@ int nslookup_main(int argc, char **argv)
        /* (but it also says "may be enabled in /etc/resolv.conf") */
        /*_res.options |= RES_USE_INET6;*/
 
-       if (argv[2])
-               set_default_dns(argv[2]);
+       set_default_dns(argv[2]);
 
        server_print();
+
+       /* getaddrinfo and friends are free to request a resolver
+        * reinitialization. Just in case, set_default_dns() again
+        * after getaddrinfo (in server_print). This reportedly helps
+        * with bug 675 "nslookup does not properly use second argument"
+        * at least on Debian Wheezy and Openwrt AA (eglibc based).
+        */
+       set_default_dns(argv[2]);
+
        return print_host(argv[1], "Name:");
 }
index 14c3a5f..c4b0187 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Author: Adam Tkac <vonsch@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  *
  * Parts of OpenNTPD clock syncronization code is replaced by
  * code which is based on ntp-4.2.6, whuch carries the following
  *                                                                     *
  ***********************************************************************
  */
+
+//usage:#define ntpd_trivial_usage
+//usage:       "[-dnqNw"IF_FEATURE_NTPD_SERVER("l")"] [-S PROG] [-p PEER]..."
+//usage:#define ntpd_full_usage "\n\n"
+//usage:       "NTP client/server\n"
+//usage:     "\n       -d      Verbose"
+//usage:     "\n       -n      Do not daemonize"
+//usage:     "\n       -q      Quit after clock is set"
+//usage:     "\n       -N      Run at high priority"
+//usage:     "\n       -w      Do not set time (only query peers), implies -n"
+//usage:       IF_FEATURE_NTPD_SERVER(
+//usage:     "\n       -l      Run as server on port 123"
+//usage:       )
+//usage:     "\n       -S PROG Run PROG after stepping time, stratum change, and every 11 mins"
+//usage:     "\n       -p PEER Obtain time from PEER (may be repeated)"
+
 #include "libbb.h"
 #include <math.h>
 #include <netinet/ip.h> /* For IPTOS_LOWDELAY definition */
+#include <sys/resource.h> /* setpriority */
 #include <sys/timex.h>
 #ifndef IPTOS_LOWDELAY
 # define IPTOS_LOWDELAY 0x10
 
 
 /* Verbosity control (max level of -dddd options accepted).
- * max 5 is very talkative (and bloated). 2 is non-bloated,
+ * max 6 is very talkative (and bloated). 3 is non-bloated,
  * production level setting.
  */
-#define MAX_VERBOSE     2
+#define MAX_VERBOSE     3
 
 
 /* High-level description of the algorithm:
  *
  * We start running with very small poll_exp, BURSTPOLL,
- * in order to quickly accumulate INITIAL_SAMLPES datapoints
+ * in order to quickly accumulate INITIAL_SAMPLES datapoints
  * for each peer. Then, time is stepped if the offset is larger
  * than STEP_THRESHOLD, otherwise it isn't; anyway, we enlarge
  * poll_exp to MINPOLL and enter frequency measurement step:
  * was hibernated, someone set totally wrong date, etc),
  * then the time is stepped, all datapoints are discarded,
  * and we go back to steady state.
+ *
+ * Made some changes to speed up re-syncing after our clock goes bad
+ * (tested with suspending my laptop):
+ * - if largish offset (>= STEP_THRESHOLD * 8 == 1 sec) is seen
+ *   from a peer, schedule next query for this peer soon
+ *   without drastically lowering poll interval for everybody.
+ *   This makes us collect enough data for step much faster:
+ *   e.g. at poll = 10 (1024 secs), step was done within 5 minutes
+ *   after first reply which indicated that our clock is 14 seconds off.
+ * - on step, do not discard d_dispersion data of the existing datapoints,
+ *   do not clear reachable_bits. This prevents discarding first ~8
+ *   datapoints after the step.
  */
 
-#define RETRY_INTERVAL  5       /* on error, retry in N secs */
+#define RETRY_INTERVAL     5    /* on error, retry in N secs */
 #define RESPONSE_INTERVAL 15    /* wait for reply up to N secs */
-#define INITIAL_SAMLPES 4       /* how many samples do we want for init */
+#define INITIAL_SAMPLES    4    /* how many samples do we want for init */
+#define BAD_DELAY_GROWTH   4    /* drop packet if its delay grew by more than this */
 
 /* Clock discipline parameters and constants */
 
 //UNUSED: #define PANIC_THRESHOLD 1000    /* panic threshold (sec) */
 
 #define FREQ_TOLERANCE  0.000015 /* frequency tolerance (15 PPM) */
-#define BURSTPOLL       0      /* initial poll */
+#define BURSTPOLL       0       /* initial poll */
 #define MINPOLL         5       /* minimum poll interval. std ntpd uses 6 (6: 64 sec) */
-#define BIGPOLL         10      /* drop to lower poll at any trouble (10: 17 min) */
+/* If we got largish offset from a peer, cap next query interval
+ * for this peer by this many seconds:
+ */
+#define BIGOFF_INTERVAL (1 << 6)
+/* If offset > discipline_jitter * POLLADJ_GATE, and poll interval is >= 2^BIGPOLL,
+ * then it is decreased _at once_. (If < 2^BIGPOLL, it will be decreased _eventually_).
+ */
+#define BIGPOLL         10      /* 2^10 sec ~= 17 min */
 #define MAXPOLL         12      /* maximum poll interval (12: 1.1h, 17: 36.4h). std ntpd uses 17 */
 /* Actively lower poll when we see such big offsets.
  * With STEP_THRESHOLD = 0.125, it means we try to sync more aggressively
- * if offset increases over 0.03 sec */
-#define POLLDOWN_OFFSET (STEP_THRESHOLD / 4)
+ * if offset increases over ~0.04 sec */
+#define POLLDOWN_OFFSET (STEP_THRESHOLD / 3)
 #define MINDISP         0.01    /* minimum dispersion (sec) */
 #define MAXDISP         16      /* maximum dispersion (sec) */
 #define MAXSTRAT        16      /* maximum stratum (infinity metric) */
 
 /* Poll-adjust threshold.
  * When we see that offset is small enough compared to discipline jitter,
- * we grow a counter: += MINPOLL. When it goes over POLLADJ_LIMIT,
+ * we grow a counter: += MINPOLL. When counter goes over POLLADJ_LIMIT,
  * we poll_exp++. If offset isn't small, counter -= poll_exp*2,
- * and when it goes below -POLLADJ_LIMIT, we poll_exp--
- * (bumped from 30 to 36 since otherwise I often see poll_exp going *2* steps down)
+ * and when it goes below -POLLADJ_LIMIT, we poll_exp--.
+ * (Bumped from 30 to 40 since otherwise I often see poll_exp going *2* steps down)
  */
-#define POLLADJ_LIMIT   36
-/* If offset < POLLADJ_GATE * discipline_jitter, then we can increase
+#define POLLADJ_LIMIT   40
+/* If offset < discipline_jitter * POLLADJ_GATE, then we decide to increase
  * poll interval (we think we can't improve timekeeping
  * by staying at smaller poll).
  */
 #define POLLADJ_GATE    4
+#define TIMECONST_HACK_GATE 2
 /* Compromise Allan intercept (sec). doc uses 1500, std ntpd uses 512 */
 #define ALLAN           512
 /* PLL loop gain */
@@ -192,22 +230,22 @@ typedef struct {
 } msg_t;
 
 typedef struct {
-       double d_recv_time;
        double d_offset;
+       double d_recv_time;
        double d_dispersion;
 } datapoint_t;
 
 typedef struct {
        len_and_sockaddr *p_lsa;
        char             *p_dotted;
-       /* when to send new query (if p_fd == -1)
-        * or when receive times out (if p_fd >= 0): */
        int              p_fd;
        int              datapoint_idx;
        uint32_t         lastpkt_refid;
        uint8_t          lastpkt_status;
        uint8_t          lastpkt_stratum;
        uint8_t          reachable_bits;
+       /* when to send new query (if p_fd == -1)
+        * or when receive times out (if p_fd >= 0): */
        double           next_action_time;
        double           p_xmttime;
        double           lastpkt_recv_time;
@@ -238,6 +276,8 @@ enum {
        OPT_p = (1 << 5),
        OPT_S = (1 << 6),
        OPT_l = (1 << 7) * ENABLE_FEATURE_NTPD_SERVER,
+       /* We hijack some bits for other purposes */
+       OPT_qq = (1 << 31),
 };
 
 struct globals {
@@ -254,15 +294,19 @@ struct globals {
        llist_t  *ntp_peers;
 #if ENABLE_FEATURE_NTPD_SERVER
        int      listen_fd;
+# define G_listen_fd (G.listen_fd)
+#else
+# define G_listen_fd (-1)
 #endif
        unsigned verbose;
        unsigned peer_cnt;
        /* refid: 32-bit code identifying the particular server or reference clock
-        *  in stratum 0 packets this is a four-character ASCII string,
-        *  called the kiss code, used for debugging and monitoring
-        *  in stratum 1 packets this is a four-character ASCII string
-        *  assigned to the reference clock by IANA. Example: "GPS "
-        *  in stratum 2+ packets, it's IPv4 address or 4 first bytes of MD5 hash of IPv6
+        * in stratum 0 packets this is a four-character ASCII string,
+        * called the kiss code, used for debugging and monitoring
+        * in stratum 1 packets this is a four-character ASCII string
+        * assigned to the reference clock by IANA. Example: "GPS "
+        * in stratum 2+ packets, it's IPv4 address or 4 first bytes
+        * of MD5 hash of IPv6
         */
        uint32_t refid;
        uint8_t  ntp_status;
@@ -271,34 +315,42 @@ struct globals {
         * mains-frequency clock incrementing at 60 Hz is 16 ms, even when the
         * system clock hardware representation is to the nanosecond.
         *
-        * Delays, jitters of various kinds are clamper down to precision.
+        * Delays, jitters of various kinds are clamped down to precision.
         *
         * If precision_sec is too large, discipline_jitter gets clamped to it
-        * and if offset is much smaller than discipline_jitter, poll interval
-        * grows even though we really can benefit from staying at smaller one,
-        * collecting non-lagged datapoits and correcting the offset.
+        * and if offset is smaller than discipline_jitter * POLLADJ_GATE, poll
+        * interval grows even though we really can benefit from staying at
+        * smaller one, collecting non-lagged datapoits and correcting offset.
         * (Lagged datapoits exist when poll_exp is large but we still have
         * systematic offset error - the time distance between datapoints
-        * is significat and older datapoints have smaller offsets.
+        * is significant and older datapoints have smaller offsets.
         * This makes our offset estimation a bit smaller than reality)
         * Due to this effect, setting G_precision_sec close to
         * STEP_THRESHOLD isn't such a good idea - offsets may grow
         * too big and we will step. I observed it with -6.
         *
-        * OTOH, setting precision too small would result in futile attempts
-        * to syncronize to the unachievable precision.
+        * OTOH, setting precision_sec far too small would result in futile
+        * attempts to syncronize to an unachievable precision.
         *
         * -6 is 1/64 sec, -7 is 1/128 sec and so on.
+        * -8 is 1/256 ~= 0.003906 (worked well for me --vda)
+        * -9 is 1/512 ~= 0.001953 (let's try this for some time)
+        */
+#define G_precision_exp  -9
+       /*
+        * G_precision_exp is used only for construction outgoing packets.
+        * It's ok to set G_precision_sec to a slightly different value
+        * (One which is "nicer looking" in logs).
+        * Exact value would be (1.0 / (1 << (- G_precision_exp))):
         */
-#define G_precision_exp  -8
-#define G_precision_sec  (1.0 / (1 << (- G_precision_exp)))
+#define G_precision_sec  0.002
        uint8_t  stratum;
        /* Bool. After set to 1, never goes back to 0: */
        smallint initial_poll_complete;
 
 #define STATE_NSET      0       /* initial state, "nothing is set" */
 //#define STATE_FSET    1       /* frequency set from file */
-#define STATE_SPIK      2       /* spike detected */
+//#define STATE_SPIK    2       /* spike detected */
 //#define STATE_FREQ    3       /* initial frequency */
 #define STATE_SYNC      4       /* clock synchronized (normal operation) */
        uint8_t  discipline_state;      // doc calls it c.state
@@ -309,6 +361,10 @@ struct globals {
        double   last_update_offset;    // c.last
        double   last_update_recv_time; // s.t
        double   discipline_jitter;     // c.jitter
+       /* Since we only compare it with ints, can simplify code
+        * by not making this variable floating point:
+        */
+       unsigned offset_to_jitter_ratio;
        //double   cluster_offset;        // s.offset
        //double   cluster_jitter;        // s.jitter
 #if !USING_KERNEL_PLL_LOOP
@@ -327,6 +383,7 @@ static const int const_IPTOS_LOWDELAY = IPTOS_LOWDELAY;
 #define VERB3 if (MAX_VERBOSE >= 3 && G.verbose >= 3)
 #define VERB4 if (MAX_VERBOSE >= 4 && G.verbose >= 4)
 #define VERB5 if (MAX_VERBOSE >= 5 && G.verbose >= 5)
+#define VERB6 if (MAX_VERBOSE >= 6 && G.verbose >= 6)
 
 
 static double LOG2D(int a)
@@ -482,23 +539,34 @@ static void
 filter_datapoints(peer_t *p)
 {
        int i, idx;
+       double sum, wavg;
+       datapoint_t *fdp;
+
+#if 0
+/* Simulations have shown that use of *averaged* offset for p->filter_offset
+ * is in fact worse than simply using last received one: with large poll intervals
+ * (>= 2048) averaging code uses offset values which are outdated by hours,
+ * and time/frequency correction goes totally wrong when fed essentially bogus offsets.
+ */
        int got_newest;
-       double minoff, maxoff, wavg, sum, w;
+       double minoff, maxoff, w;
        double x = x; /* for compiler */
        double oldest_off = oldest_off;
        double oldest_age = oldest_age;
        double newest_off = newest_off;
        double newest_age = newest_age;
 
-       minoff = maxoff = p->filter_datapoint[0].d_offset;
+       fdp = p->filter_datapoint;
+
+       minoff = maxoff = fdp[0].d_offset;
        for (i = 1; i < NUM_DATAPOINTS; i++) {
-               if (minoff > p->filter_datapoint[i].d_offset)
-                       minoff = p->filter_datapoint[i].d_offset;
-               if (maxoff < p->filter_datapoint[i].d_offset)
-                       maxoff = p->filter_datapoint[i].d_offset;
+               if (minoff > fdp[i].d_offset)
+                       minoff = fdp[i].d_offset;
+               if (maxoff < fdp[i].d_offset)
+                       maxoff = fdp[i].d_offset;
        }
 
-       idx = p->datapoint_idx; /* most recent datapoint */
+       idx = p->datapoint_idx; /* most recent datapoint's index */
        /* Average offset:
         * Drop two outliers and take weighted average of the rest:
         * most_recent/2 + older1/4 + older2/8 ... + older5/32 + older6/32
@@ -517,27 +585,27 @@ filter_datapoints(peer_t *p)
        got_newest = 0;
        sum = 0;
        for (i = 0; i < NUM_DATAPOINTS; i++) {
-               VERB4 {
+               VERB5 {
                        bb_error_msg("datapoint[%d]: off:%f disp:%f(%f) age:%f%s",
                                i,
-                               p->filter_datapoint[idx].d_offset,
-                               p->filter_datapoint[idx].d_dispersion, dispersion(&p->filter_datapoint[idx]),
-                               G.cur_time - p->filter_datapoint[idx].d_recv_time,
-                               (minoff == p->filter_datapoint[idx].d_offset || maxoff == p->filter_datapoint[idx].d_offset)
+                               fdp[idx].d_offset,
+                               fdp[idx].d_dispersion, dispersion(&fdp[idx]),
+                               G.cur_time - fdp[idx].d_recv_time,
+                               (minoff == fdp[idx].d_offset || maxoff == fdp[idx].d_offset)
                                        ? " (outlier by offset)" : ""
                        );
                }
 
-               sum += dispersion(&p->filter_datapoint[idx]) / (2 << i);
+               sum += dispersion(&fdp[idx]) / (2 << i);
 
-               if (minoff == p->filter_datapoint[idx].d_offset) {
+               if (minoff == fdp[idx].d_offset) {
                        minoff -= 1; /* so that we don't match it ever again */
                } else
-               if (maxoff == p->filter_datapoint[idx].d_offset) {
+               if (maxoff == fdp[idx].d_offset) {
                        maxoff += 1;
                } else {
-                       oldest_off = p->filter_datapoint[idx].d_offset;
-                       oldest_age = G.cur_time - p->filter_datapoint[idx].d_recv_time;
+                       oldest_off = fdp[idx].d_offset;
+                       oldest_age = G.cur_time - fdp[idx].d_recv_time;
                        if (!got_newest) {
                                got_newest = 1;
                                newest_off = oldest_off;
@@ -570,6 +638,32 @@ filter_datapoints(peer_t *p)
        }
        p->filter_offset = wavg;
 
+#else
+
+       fdp = p->filter_datapoint;
+       idx = p->datapoint_idx; /* most recent datapoint's index */
+
+       /* filter_offset: simply use the most recent value */
+       p->filter_offset = fdp[idx].d_offset;
+
+       /*                     n-1
+        *                     ---    dispersion(i)
+        * filter_dispersion =  \     -------------
+        *                      /       (i+1)
+        *                     ---     2
+        *                     i=0
+        */
+       wavg = 0;
+       sum = 0;
+       for (i = 0; i < NUM_DATAPOINTS; i++) {
+               sum += dispersion(&fdp[idx]) / (2 << i);
+               wavg += fdp[idx].d_offset;
+               idx = (idx - 1) & (NUM_DATAPOINTS - 1);
+       }
+       wavg /= NUM_DATAPOINTS;
+       p->filter_dispersion = sum;
+#endif
+
        /*                  +-----                 -----+ ^ 1/2
         *                  |       n-1                 |
         *                  |       ---                 |
@@ -583,16 +677,15 @@ filter_datapoints(peer_t *p)
         */
        sum = 0;
        for (i = 0; i < NUM_DATAPOINTS; i++) {
-               sum += SQUARE(wavg - p->filter_datapoint[i].d_offset);
+               sum += SQUARE(wavg - fdp[i].d_offset);
        }
        sum = SQRT(sum / NUM_DATAPOINTS);
        p->filter_jitter = sum > G_precision_sec ? sum : G_precision_sec;
 
-       VERB3 bb_error_msg("filter offset:%f(corr:%e) disp:%f jitter:%f",
-                       p->filter_offset, x,
+       VERB4 bb_error_msg("filter offset:%+f disp:%f jitter:%f",
+                       p->filter_offset,
                        p->filter_dispersion,
                        p->filter_jitter);
-
 }
 
 static void
@@ -601,26 +694,42 @@ reset_peer_stats(peer_t *p, double offset)
        int i;
        bool small_ofs = fabs(offset) < 16 * STEP_THRESHOLD;
 
+       /* Used to set p->filter_datapoint[i].d_dispersion = MAXDISP
+        * and clear reachable bits, but this proved to be too agressive:
+        * after step (tested with suspinding laptop for ~30 secs),
+        * this caused all previous data to be considered invalid,
+        * making us needing to collect full ~8 datapoins per peer
+        * after step in order to start trusting them.
+        * In turn, this was making poll interval decrease even after
+        * step was done. (Poll interval decreases already before step
+        * in this scenario, because we see large offsets and end up with
+        * no good peer to select).
+        */
+
        for (i = 0; i < NUM_DATAPOINTS; i++) {
                if (small_ofs) {
                        p->filter_datapoint[i].d_recv_time += offset;
                        if (p->filter_datapoint[i].d_offset != 0) {
-                               p->filter_datapoint[i].d_offset += offset;
+                               p->filter_datapoint[i].d_offset -= offset;
+                               //bb_error_msg("p->filter_datapoint[%d].d_offset %f -> %f",
+                               //      i,
+                               //      p->filter_datapoint[i].d_offset + offset,
+                               //      p->filter_datapoint[i].d_offset);
                        }
                } else {
                        p->filter_datapoint[i].d_recv_time  = G.cur_time;
                        p->filter_datapoint[i].d_offset     = 0;
-                       p->filter_datapoint[i].d_dispersion = MAXDISP;
+                       /*p->filter_datapoint[i].d_dispersion = MAXDISP;*/
                }
        }
        if (small_ofs) {
                p->lastpkt_recv_time += offset;
        } else {
-               p->reachable_bits = 0;
+               /*p->reachable_bits = 0;*/
                p->lastpkt_recv_time = G.cur_time;
        }
        filter_datapoints(p); /* recalc p->filter_xxx */
-       VERB5 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time);
+       VERB6 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time);
 }
 
 static void
@@ -702,6 +811,12 @@ send_query_to_peer(peer_t *p)
                free(local_lsa);
        }
 
+       /* Emit message _before_ attempted send. Think of a very short
+        * roundtrip networks: we need to go back to recv loop ASAP,
+        * to reduce delay. Printing messages after send works against that.
+        */
+       VERB1 bb_error_msg("sending query to %s", p->p_dotted);
+
        /*
         * Send out a random 64-bit number as our transmit time.  The NTP
         * server will copy said number into the originate field on the
@@ -719,23 +834,34 @@ send_query_to_peer(peer_t *p)
        p->p_xmt_msg.m_xmttime.fractionl = random();
        p->p_xmttime = gettime1900d();
 
+       /* Were doing it only if sendto worked, but
+        * loss of sync detection needs reachable_bits updated
+        * even if sending fails *locally*:
+        * "network is unreachable" because cable was pulled?
+        * We still need to declare "unsync" if this condition persists.
+        */
+       p->reachable_bits <<= 1;
+
        if (do_sendto(p->p_fd, /*from:*/ NULL, /*to:*/ &p->p_lsa->u.sa, /*addrlen:*/ p->p_lsa->len,
                        &p->p_xmt_msg, NTP_MSGSIZE_NOAUTH) == -1
        ) {
                close(p->p_fd);
                p->p_fd = -1;
+               /*
+                * We know that we sent nothing.
+                * We can retry *soon* without fearing
+                * that we are flooding the peer.
+                */
                set_next(p, RETRY_INTERVAL);
                return;
        }
 
-       p->reachable_bits <<= 1;
-       VERB1 bb_error_msg("sent query to %s", p->p_dotted);
        set_next(p, RESPONSE_INTERVAL);
 }
 
 
 /* Note that there is no provision to prevent several run_scripts
- * to be done in quick succession. In fact, it happens rather often
+ * to be started in quick succession. In fact, it happens rather often
  * if initial syncronization results in a step.
  * You will see "step" and then "stratum" script runs, sometimes
  * as close as only 0.002 seconds apart.
@@ -746,6 +872,8 @@ static void run_script(const char *action, double offset)
        char *argv[3];
        char *env1, *env2, *env3, *env4;
 
+       G.last_script_run = G.cur_time;
+
        if (!G.script_name)
                return;
 
@@ -782,8 +910,6 @@ static void run_script(const char *action, double offset)
        free(env2);
        free(env3);
        free(env4);
-
-       G.last_script_run = G.cur_time;
 }
 
 static NOINLINE void
@@ -791,37 +917,49 @@ step_time(double offset)
 {
        llist_t *item;
        double dtime;
-       struct timeval tv;
-       char buf[80];
+       struct timeval tvc, tvn;
+       char buf[sizeof("yyyy-mm-dd hh:mm:ss") + /*paranoia:*/ 4];
        time_t tval;
 
-       gettimeofday(&tv, NULL); /* never fails */
-       dtime = offset + tv.tv_sec;
-       dtime += 1.0e-6 * tv.tv_usec;
-       d_to_tv(dtime, &tv);
-
-       if (settimeofday(&tv, NULL) == -1)
+       gettimeofday(&tvc, NULL); /* never fails */
+       dtime = tvc.tv_sec + (1.0e-6 * tvc.tv_usec) + offset;
+       d_to_tv(dtime, &tvn);
+       if (settimeofday(&tvn, NULL) == -1)
                bb_perror_msg_and_die("settimeofday");
 
-       tval = tv.tv_sec;
-       strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Z %Y", localtime(&tval));
-
-       bb_error_msg("setting clock to %s (offset %fs)", buf, offset);
+       VERB2 {
+               tval = tvc.tv_sec;
+               strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &tval);
+               bb_error_msg("current time is %s.%06u", buf, (unsigned)tvc.tv_usec);
+       }
+       tval = tvn.tv_sec;
+       strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &tval);
+       bb_error_msg("setting time to %s.%06u (offset %+fs)", buf, (unsigned)tvn.tv_usec, offset);
 
        /* Correct various fields which contain time-relative values: */
 
+       /* Globals: */
+       G.cur_time += offset;
+       G.last_update_recv_time += offset;
+       G.last_script_run += offset;
+
        /* p->lastpkt_recv_time, p->next_action_time and such: */
        for (item = G.ntp_peers; item != NULL; item = item->link) {
                peer_t *pp = (peer_t *) item->data;
                reset_peer_stats(pp, offset);
-               //bb_error_msg("offset:%f pp->next_action_time:%f -> %f",
+               //bb_error_msg("offset:%+f pp->next_action_time:%f -> %f",
                //      offset, pp->next_action_time, pp->next_action_time + offset);
                pp->next_action_time += offset;
+               if (pp->p_fd >= 0) {
+                       /* We wait for reply from this peer too.
+                        * But due to step we are doing, reply's data is no longer
+                        * useful (in fact, it'll be bogus). Stop waiting for it.
+                        */
+                       close(pp->p_fd);
+                       pp->p_fd = -1;
+                       set_next(pp, RETRY_INTERVAL);
+               }
        }
-       /* Globals: */
-       G.cur_time += offset;
-       G.last_update_recv_time += offset;
-       G.last_script_run += offset;
 }
 
 
@@ -863,27 +1001,27 @@ fit(peer_t *p, double rd)
 {
        if ((p->reachable_bits & (p->reachable_bits-1)) == 0) {
                /* One or zero bits in reachable_bits */
-               VERB3 bb_error_msg("peer %s unfit for selection: unreachable", p->p_dotted);
+               VERB4 bb_error_msg("peer %s unfit for selection: unreachable", p->p_dotted);
                return 0;
        }
-#if 0  /* we filter out such packets earlier */
+#if 0 /* we filter out such packets earlier */
        if ((p->lastpkt_status & LI_ALARM) == LI_ALARM
         || p->lastpkt_stratum >= MAXSTRAT
        ) {
-               VERB3 bb_error_msg("peer %s unfit for selection: bad status/stratum", p->p_dotted);
+               VERB4 bb_error_msg("peer %s unfit for selection: bad status/stratum", p->p_dotted);
                return 0;
        }
 #endif
        /* rd is root_distance(p) */
        if (rd > MAXDIST + FREQ_TOLERANCE * (1 << G.poll_exp)) {
-               VERB3 bb_error_msg("peer %s unfit for selection: root distance too high", p->p_dotted);
+               VERB4 bb_error_msg("peer %s unfit for selection: root distance too high", p->p_dotted);
                return 0;
        }
 //TODO
 //     /* Do we have a loop? */
 //     if (p->refid == p->dstaddr || p->refid == s.refid)
 //             return 0;
-        return 1;
+       return 1;
 }
 static peer_t*
 select_and_cluster(void)
@@ -916,7 +1054,7 @@ select_and_cluster(void)
                        continue;
                }
 
-               VERB4 bb_error_msg("interval: [%f %f %f] %s",
+               VERB5 bb_error_msg("interval: [%f %f %f] %s",
                                offset - rd,
                                offset,
                                offset + rd,
@@ -941,7 +1079,7 @@ select_and_cluster(void)
        }
        num_candidates = num_points / 3;
        if (num_candidates == 0) {
-               VERB3 bb_error_msg("no valid datapoints, no peer selected");
+               VERB3 bb_error_msg("no valid datapoints%s", ", no peer selected");
                return NULL;
        }
 //TODO: sorting does not seem to be done in reference code
@@ -999,12 +1137,13 @@ select_and_cluster(void)
                        break;
                num_falsetickers++;
                if (num_falsetickers * 2 >= num_candidates) {
-                       VERB3 bb_error_msg("too many falsetickers:%d (candidates:%d), no peer selected",
-                                       num_falsetickers, num_candidates);
+                       VERB3 bb_error_msg("falsetickers:%d, candidates:%d%s",
+                                       num_falsetickers, num_candidates,
+                                       ", no peer selected");
                        return NULL;
                }
        }
-       VERB3 bb_error_msg("selected interval: [%f, %f]; candidates:%d falsetickers:%d",
+       VERB4 bb_error_msg("selected interval: [%f, %f]; candidates:%d falsetickers:%d",
                        low, high, num_candidates, num_falsetickers);
 
        /* Clustering */
@@ -1022,7 +1161,7 @@ select_and_cluster(void)
                survivor[num_survivors].p = p;
                /* x.opt_rd == root_distance(p); */
                survivor[num_survivors].metric = MAXDIST * p->lastpkt_stratum + point[i].opt_rd;
-               VERB4 bb_error_msg("survivor[%d] metric:%f peer:%s",
+               VERB5 bb_error_msg("survivor[%d] metric:%f peer:%s",
                        num_survivors, survivor[num_survivors].metric, p->p_dotted);
                num_survivors++;
        }
@@ -1032,8 +1171,9 @@ select_and_cluster(void)
         * is acceptable.
         */
        if (num_survivors < MIN_SELECTED) {
-               VERB3 bb_error_msg("num_survivors %d < %d, no peer selected",
-                               num_survivors, MIN_SELECTED);
+               VERB3 bb_error_msg("survivors:%d%s",
+                               num_survivors,
+                               ", no peer selected");
                return NULL;
        }
 
@@ -1053,7 +1193,7 @@ select_and_cluster(void)
                double min_jitter = min_jitter;
 
                if (num_survivors <= MIN_CLUSTERED) {
-                       VERB3 bb_error_msg("num_survivors %d <= %d, not discarding more",
+                       VERB4 bb_error_msg("num_survivors %d <= %d, not discarding more",
                                        num_survivors, MIN_CLUSTERED);
                        break;
                }
@@ -1079,11 +1219,11 @@ select_and_cluster(void)
                                max_selection_jitter = selection_jitter_sq;
                                max_idx = i;
                        }
-                       VERB5 bb_error_msg("survivor %d selection_jitter^2:%f",
+                       VERB6 bb_error_msg("survivor %d selection_jitter^2:%f",
                                        i, selection_jitter_sq);
                }
                max_selection_jitter = SQRT(max_selection_jitter / num_survivors);
-               VERB4 bb_error_msg("max_selection_jitter (at %d):%f min_jitter:%f",
+               VERB5 bb_error_msg("max_selection_jitter (at %d):%f min_jitter:%f",
                                max_idx, max_selection_jitter, min_jitter);
 
                /* If the maximum selection jitter is less than the
@@ -1092,7 +1232,7 @@ select_and_cluster(void)
                 * as well stop.
                 */
                if (max_selection_jitter < min_jitter) {
-                       VERB3 bb_error_msg("max_selection_jitter:%f < min_jitter:%f, num_survivors:%d, not discarding more",
+                       VERB4 bb_error_msg("max_selection_jitter:%f < min_jitter:%f, num_survivors:%d, not discarding more",
                                        max_selection_jitter, min_jitter, num_survivors);
                        break;
                }
@@ -1100,7 +1240,7 @@ select_and_cluster(void)
                /* Delete survivor[max_idx] from the list
                 * and go around again.
                 */
-               VERB5 bb_error_msg("dropping survivor %d", max_idx);
+               VERB6 bb_error_msg("dropping survivor %d", max_idx);
                num_survivors--;
                while (max_idx < num_survivors) {
                        survivor[max_idx] = survivor[max_idx + 1];
@@ -1142,7 +1282,7 @@ select_and_cluster(void)
                /* Starting from 1 is ok here */
                for (i = 1; i < num_survivors; i++) {
                        if (G.last_update_peer == survivor[i].p) {
-                               VERB4 bb_error_msg("keeping old synced peer");
+                               VERB5 bb_error_msg("keeping old synced peer");
                                p = G.last_update_peer;
                                goto keep_old;
                        }
@@ -1150,7 +1290,7 @@ select_and_cluster(void)
        }
        G.last_update_peer = p;
  keep_old:
-       VERB3 bb_error_msg("selected peer %s filter_offset:%f age:%f",
+       VERB4 bb_error_msg("selected peer %s filter_offset:%+f age:%f",
                        p->p_dotted,
                        p->filter_offset,
                        G.cur_time - p->lastpkt_recv_time
@@ -1169,7 +1309,7 @@ set_new_values(int disc_state, double offset, double recv_time)
         * of the last clock filter sample, which must be earlier than
         * the current time.
         */
-       VERB3 bb_error_msg("disc_state=%d last update offset=%f recv_time=%f",
+       VERB4 bb_error_msg("disc_state=%d last update offset=%f recv_time=%f",
                        disc_state, offset, recv_time);
        G.discipline_state = disc_state;
        G.last_update_offset = offset;
@@ -1207,8 +1347,8 @@ update_local_clock(peer_t *p)
         * an old sample or the same sample twice.
         */
        if (recv_time <= G.last_update_recv_time) {
-               VERB3 bb_error_msg("same or older datapoint: %f >= %f, not using it",
-                               G.last_update_recv_time, recv_time);
+               VERB3 bb_error_msg("update from %s: same or older datapoint, not using it",
+                       p->p_dotted);
                return 0; /* "leave poll interval as is" */
        }
 
@@ -1224,7 +1364,7 @@ update_local_clock(peer_t *p)
        if (G.discipline_state == STATE_FREQ) {
                /* Ignore updates until the stepout threshold */
                if (since_last_update < WATCH_THRESHOLD) {
-                       VERB3 bb_error_msg("measuring drift, datapoint ignored, %f sec remains",
+                       VERB4 bb_error_msg("measuring drift, datapoint ignored, %f sec remains",
                                        WATCH_THRESHOLD - since_last_update);
                        return 0; /* "leave poll interval as is" */
                }
@@ -1238,10 +1378,21 @@ update_local_clock(peer_t *p)
         * offset exceeds the step threshold and when it does not.
         */
        if (abs_offset > STEP_THRESHOLD) {
+#if 0
+               double remains;
+
+// This "spike state" seems to be useless, peer selection already drops
+// occassional "bad" datapoints. If we are here, there were _many_
+// large offsets. When a few first large offsets are seen,
+// we end up in "no valid datapoints, no peer selected" state.
+// Only when enough of them are seen (which means it's not a fluke),
+// we end up here. Looks like _our_ clock is off.
                switch (G.discipline_state) {
                case STATE_SYNC:
                        /* The first outlyer: ignore it, switch to SPIK state */
-                       VERB3 bb_error_msg("offset:%f - spike detected", offset);
+                       VERB3 bb_error_msg("update from %s: offset:%+f, spike%s",
+                               p->p_dotted, offset,
+                               "");
                        G.discipline_state = STATE_SPIK;
                        return -1; /* "decrease poll interval" */
 
@@ -1249,13 +1400,16 @@ update_local_clock(peer_t *p)
                        /* Ignore succeeding outlyers until either an inlyer
                         * is found or the stepout threshold is exceeded.
                         */
-                       if (since_last_update < WATCH_THRESHOLD) {
-                               VERB3 bb_error_msg("spike detected, datapoint ignored, %f sec remains",
-                                               WATCH_THRESHOLD - since_last_update);
+                       remains = WATCH_THRESHOLD - since_last_update;
+                       if (remains > 0) {
+                               VERB3 bb_error_msg("update from %s: offset:%+f, spike%s",
+                                       p->p_dotted, offset,
+                                       ", datapoint ignored");
                                return -1; /* "decrease poll interval" */
                        }
                        /* fall through: we need to step */
                } /* switch */
+#endif
 
                /* Step the time and clamp down the poll interval.
                 *
@@ -1278,7 +1432,7 @@ update_local_clock(peer_t *p)
                 * is always suppressed, even at the longer poll
                 * intervals.
                 */
-               VERB3 bb_error_msg("stepping time by %f; poll_exp=MINPOLL", offset);
+               VERB4 bb_error_msg("stepping time by %+f; poll_exp=MINPOLL", offset);
                step_time(offset);
                if (option_mask32 & OPT_q) {
                        /* We were only asked to set time once. Done. */
@@ -1291,18 +1445,21 @@ update_local_clock(peer_t *p)
 
                run_script("step", offset);
 
+               recv_time += offset;
+
 #if USING_INITIAL_FREQ_ESTIMATION
                if (G.discipline_state == STATE_NSET) {
                        set_new_values(STATE_FREQ, /*offset:*/ 0, recv_time);
                        return 1; /* "ok to increase poll interval" */
                }
 #endif
-               set_new_values(STATE_SYNC, /*offset:*/ 0, recv_time);
+               abs_offset = offset = 0;
+               set_new_values(STATE_SYNC, offset, recv_time);
 
        } else { /* abs_offset <= STEP_THRESHOLD */
 
                if (G.poll_exp < MINPOLL && G.initial_poll_complete) {
-                       VERB3 bb_error_msg("small offset:%f, disabling burst mode", offset);
+                       VERB4 bb_error_msg("small offset:%+f, disabling burst mode", offset);
                        G.polladj_count = 0;
                        G.poll_exp = MINPOLL;
                }
@@ -1311,9 +1468,8 @@ update_local_clock(peer_t *p)
                 * weighted offset differences. Used by the poll adjust code.
                 */
                etemp = SQUARE(G.discipline_jitter);
-               dtemp = SQUARE(MAXD(fabs(offset - G.last_update_offset), G_precision_sec));
+               dtemp = SQUARE(offset - G.last_update_offset);
                G.discipline_jitter = SQRT(etemp + (dtemp - etemp) / AVG);
-               VERB3 bb_error_msg("discipline jitter=%f", G.discipline_jitter);
 
                switch (G.discipline_state) {
                case STATE_NSET:
@@ -1332,7 +1488,7 @@ update_local_clock(peer_t *p)
 #else
                        set_new_values(STATE_SYNC, offset, recv_time);
 #endif
-                       VERB3 bb_error_msg("transitioning to FREQ, datapoint ignored");
+                       VERB4 bb_error_msg("transitioning to FREQ, datapoint ignored");
                        return 0; /* "leave poll interval as is" */
 
 #if 0 /* this is dead code for now */
@@ -1390,6 +1546,10 @@ update_local_clock(peer_t *p)
                }
        }
 
+       if (G.discipline_jitter < G_precision_sec)
+               G.discipline_jitter = G_precision_sec;
+       G.offset_to_jitter_ratio = abs_offset / G.discipline_jitter;
+
        G.reftime = G.cur_time;
        G.ntp_status = p->lastpkt_status;
        G.refid = p->lastpkt_refid;
@@ -1397,11 +1557,11 @@ update_local_clock(peer_t *p)
        dtemp = p->filter_jitter; // SQRT(SQUARE(p->filter_jitter) + SQUARE(G.cluster_jitter));
        dtemp += MAXD(p->filter_dispersion + FREQ_TOLERANCE * (G.cur_time - p->lastpkt_recv_time) + abs_offset, MINDISP);
        G.rootdisp = p->lastpkt_rootdisp + dtemp;
-       VERB3 bb_error_msg("updating leap/refid/reftime/rootdisp from peer %s", p->p_dotted);
+       VERB4 bb_error_msg("updating leap/refid/reftime/rootdisp from peer %s", p->p_dotted);
 
        /* We are in STATE_SYNC now, but did not do adjtimex yet.
         * (Any other state does not reach this, they all return earlier)
-        * By this time, freq_drift and G.last_update_offset are set
+        * By this time, freq_drift and offset are set
         * to values suitable for adjtimex.
         */
 #if !USING_KERNEL_PLL_LOOP
@@ -1417,18 +1577,18 @@ update_local_clock(peer_t *p)
        dtemp = SQUARE(dtemp);
        G.discipline_wander = SQRT(etemp + (dtemp - etemp) / AVG);
 
-       VERB3 bb_error_msg("discipline freq_drift=%.9f(int:%ld corr:%e) wander=%f",
+       VERB4 bb_error_msg("discipline freq_drift=%.9f(int:%ld corr:%e) wander=%f",
                        G.discipline_freq_drift,
                        (long)(G.discipline_freq_drift * 65536e6),
                        freq_drift,
                        G.discipline_wander);
 #endif
-       VERB3 {
+       VERB4 {
                memset(&tmx, 0, sizeof(tmx));
                if (adjtimex(&tmx) < 0)
                        bb_perror_msg_and_die("adjtimex");
-               VERB3 bb_error_msg("p adjtimex freq:%ld offset:%ld constant:%ld status:0x%x",
-                               tmx.freq, tmx.offset, tmx.constant, tmx.status);
+               bb_error_msg("p adjtimex freq:%ld offset:%+ld status:0x%x tc:%ld",
+                               tmx.freq, tmx.offset, tmx.status, tmx.constant);
        }
 
        memset(&tmx, 0, sizeof(tmx));
@@ -1440,40 +1600,42 @@ update_local_clock(peer_t *p)
        tmx.modes = ADJ_FREQUENCY | ADJ_OFFSET;
        /* 65536 is one ppm */
        tmx.freq = G.discipline_freq_drift * 65536e6;
-       tmx.offset = G.last_update_offset * 1000000; /* usec */
 #endif
        tmx.modes = ADJ_OFFSET | ADJ_STATUS | ADJ_TIMECONST;// | ADJ_MAXERROR | ADJ_ESTERROR;
-       tmx.offset = (G.last_update_offset * 1000000); /* usec */
-                       /* + (G.last_update_offset < 0 ? -0.5 : 0.5) - too small to bother */
+       tmx.offset = (offset * 1000000); /* usec */
        tmx.status = STA_PLL;
        if (G.ntp_status & LI_PLUSSEC)
                tmx.status |= STA_INS;
        if (G.ntp_status & LI_MINUSSEC)
                tmx.status |= STA_DEL;
+
        tmx.constant = G.poll_exp - 4;
-       //tmx.esterror = (u_int32)(clock_jitter * 1e6);
-       //tmx.maxerror = (u_int32)((sys_rootdelay / 2 + sys_rootdisp) * 1e6);
+       /* EXPERIMENTAL.
+        * The below if statement should be unnecessary, but...
+        * It looks like Linux kernel's PLL is far too gentle in changing
+        * tmx.freq in response to clock offset. Offset keeps growing
+        * and eventually we fall back to smaller poll intervals.
+        * We can make correction more agressive (about x2) by supplying
+        * PLL time constant which is one less than the real one.
+        * To be on a safe side, let's do it only if offset is significantly
+        * larger than jitter.
+        */
+       if (tmx.constant > 0 && G.offset_to_jitter_ratio >= TIMECONST_HACK_GATE)
+               tmx.constant--;
+
+       //tmx.esterror = (uint32_t)(clock_jitter * 1e6);
+       //tmx.maxerror = (uint32_t)((sys_rootdelay / 2 + sys_rootdisp) * 1e6);
        rc = adjtimex(&tmx);
        if (rc < 0)
                bb_perror_msg_and_die("adjtimex");
        /* NB: here kernel returns constant == G.poll_exp, not == G.poll_exp - 4.
         * Not sure why. Perhaps it is normal.
         */
-       VERB3 bb_error_msg("adjtimex:%d freq:%ld offset:%ld constant:%ld status:0x%x",
-                               rc, tmx.freq, tmx.offset, tmx.constant, tmx.status);
-#if 0
-       VERB3 {
-               /* always gives the same output as above msg */
-               memset(&tmx, 0, sizeof(tmx));
-               if (adjtimex(&tmx) < 0)
-                       bb_perror_msg_and_die("adjtimex");
-               VERB3 bb_error_msg("c adjtimex freq:%ld offset:%ld constant:%ld status:0x%x",
-                               tmx.freq, tmx.offset, tmx.constant, tmx.status);
-       }
-#endif
+       VERB4 bb_error_msg("adjtimex:%d freq:%ld offset:%+ld status:0x%x",
+                               rc, tmx.freq, tmx.offset, tmx.status);
        G.kernel_freq_drift = tmx.freq / 65536;
-       VERB2 bb_error_msg("update peer:%s, offset:%f, clock drift:%ld ppm",
-                       p->p_dotted, G.last_update_offset, G.kernel_freq_drift);
+       VERB2 bb_error_msg("update from:%s offset:%+f jitter:%f clock drift:%+.3fppm tc:%d",
+                       p->p_dotted, offset, G.discipline_jitter, (double)tmx.freq / 65536, (int)tmx.constant);
 
        return 1; /* "ok to increase poll interval" */
 }
@@ -1491,7 +1653,7 @@ retry_interval(void)
        interval = RETRY_INTERVAL;
        r = random();
        interval += r % (unsigned)(RETRY_INTERVAL / 4);
-       VERB3 bb_error_msg("chose retry interval:%u", interval);
+       VERB4 bb_error_msg("chose retry interval:%u", interval);
        return interval;
 }
 static unsigned
@@ -1504,7 +1666,7 @@ poll_interval(int exponent)
        interval = 1 << exponent;
        r = random();
        interval += ((r & (interval-1)) >> 4) + ((r >> 8) & 1); /* + 1/16 of interval, max */
-       VERB3 bb_error_msg("chose poll interval:%u (poll_exp:%d exp:%d)", interval, G.poll_exp, exponent);
+       VERB4 bb_error_msg("chose poll interval:%u (poll_exp:%d exp:%d)", interval, G.poll_exp, exponent);
        return interval;
 }
 static NOINLINE void
@@ -1514,10 +1676,13 @@ recv_and_process_peer_pkt(peer_t *p)
        ssize_t     size;
        msg_t       msg;
        double      T1, T2, T3, T4;
+       double      dv, offset;
        unsigned    interval;
        datapoint_t *datapoint;
        peer_t      *q;
 
+       offset = 0;
+
        /* We can recvfrom here and check from.IP, but some multihomed
         * ntp servers reply from their *other IP*.
         * TODO: maybe we should check at least what we can: from.port == 123?
@@ -1532,22 +1697,30 @@ recv_and_process_peer_pkt(peer_t *p)
                ) {
 //TODO: always do this?
                        interval = retry_interval();
-                       goto set_next_and_close_sock;
+                       goto set_next_and_ret;
                }
                xfunc_die();
        }
 
        if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
                bb_error_msg("malformed packet received from %s", p->p_dotted);
-               goto bail;
+               return;
        }
 
        if (msg.m_orgtime.int_partl != p->p_xmt_msg.m_xmttime.int_partl
         || msg.m_orgtime.fractionl != p->p_xmt_msg.m_xmttime.fractionl
        ) {
-               goto bail;
+               /* Somebody else's packet */
+               return;
        }
 
+       /* We do not expect any more packets from this peer for now.
+        * Closing the socket informs kernel about it.
+        * We open a new socket when we send a new query.
+        */
+       close(p->p_fd);
+       p->p_fd = -1;
+
        if ((msg.m_status & LI_ALARM) == LI_ALARM
         || msg.m_stratum == 0
         || msg.m_stratum > NTP_MAXSTRATUM
@@ -1555,9 +1728,8 @@ recv_and_process_peer_pkt(peer_t *p)
 // TODO: stratum 0 responses may have commands in 32-bit m_refid field:
 // "DENY", "RSTR" - peer does not like us at all
 // "RATE" - peer is overloaded, reduce polling freq
-               interval = poll_interval(0);
-               bb_error_msg("reply from %s: not synced, next query in %us", p->p_dotted, interval);
-               goto set_next_and_close_sock;
+               bb_error_msg("reply from %s: peer is unsynced", p->p_dotted);
+               goto pick_normal_interval;
        }
 
 //     /* Verify valid root distance */
@@ -1590,41 +1762,51 @@ recv_and_process_peer_pkt(peer_t *p)
        T4 = G.cur_time;
 
        p->lastpkt_recv_time = T4;
+       VERB6 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time);
 
-       VERB5 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time);
-       p->datapoint_idx = p->reachable_bits ? (p->datapoint_idx + 1) % NUM_DATAPOINTS : 0;
-       datapoint = &p->filter_datapoint[p->datapoint_idx];
-       datapoint->d_recv_time = T4;
-       datapoint->d_offset    = ((T2 - T1) + (T3 - T4)) / 2;
        /* The delay calculation is a special case. In cases where the
         * server and client clocks are running at different rates and
         * with very fast networks, the delay can appear negative. In
         * order to avoid violating the Principle of Least Astonishment,
         * the delay is clamped not less than the system precision.
         */
+       dv = p->lastpkt_delay;
        p->lastpkt_delay = (T4 - T1) - (T3 - T2);
        if (p->lastpkt_delay < G_precision_sec)
                p->lastpkt_delay = G_precision_sec;
+       /*
+        * If this packet's delay is much bigger than the last one,
+        * it's better to just ignore it than use its much less precise value.
+        */
+       if (p->reachable_bits && p->lastpkt_delay > dv * BAD_DELAY_GROWTH) {
+               bb_error_msg("reply from %s: delay %f is too high, ignoring", p->p_dotted, p->lastpkt_delay);
+               goto pick_normal_interval;
+       }
+
+       p->datapoint_idx = p->reachable_bits ? (p->datapoint_idx + 1) % NUM_DATAPOINTS : 0;
+       datapoint = &p->filter_datapoint[p->datapoint_idx];
+       datapoint->d_recv_time = T4;
+       datapoint->d_offset    = offset = ((T2 - T1) + (T3 - T4)) / 2;
        datapoint->d_dispersion = LOG2D(msg.m_precision_exp) + G_precision_sec;
        if (!p->reachable_bits) {
                /* 1st datapoint ever - replicate offset in every element */
                int i;
-               for (i = 1; i < NUM_DATAPOINTS; i++) {
-                       p->filter_datapoint[i].d_offset = datapoint->d_offset;
+               for (i = 0; i < NUM_DATAPOINTS; i++) {
+                       p->filter_datapoint[i].d_offset = offset;
                }
        }
 
        p->reachable_bits |= 1;
        if ((MAX_VERBOSE && G.verbose) || (option_mask32 & OPT_w)) {
-               bb_error_msg("reply from %s: reach 0x%02x offset %f delay %f status 0x%02x strat %d refid 0x%08x rootdelay %f",
+               bb_error_msg("reply from %s: offset:%+f delay:%f status:0x%02x strat:%d refid:0x%08x rootdelay:%f reach:0x%02x",
                        p->p_dotted,
-                       p->reachable_bits,
-                       datapoint->d_offset,
+                       offset,
                        p->lastpkt_delay,
                        p->lastpkt_status,
                        p->lastpkt_stratum,
                        p->lastpkt_refid,
-                       p->lastpkt_rootdelay
+                       p->lastpkt_rootdelay,
+                       p->reachable_bits
                        /* not shown: m_ppoll, m_precision_exp, m_rootdisp,
                         * m_reftime, m_orgtime, m_rectime, m_xmttime
                         */
@@ -1643,7 +1825,7 @@ recv_and_process_peer_pkt(peer_t *p)
                         * drop poll interval one step down.
                         */
                        if (fabs(q->filter_offset) >= POLLDOWN_OFFSET) {
-                               VERB3 bb_error_msg("offset:%f > POLLDOWN_OFFSET", q->filter_offset);
+                               VERB4 bb_error_msg("offset:%+f > POLLDOWN_OFFSET", q->filter_offset);
                                goto poll_down;
                        }
                }
@@ -1657,14 +1839,7 @@ recv_and_process_peer_pkt(peer_t *p)
                 * is increased, otherwise it is decreased. A bit of hysteresis
                 * helps calm the dance. Works best using burst mode.
                 */
-               VERB4 if (rc > 0) {
-                       bb_error_msg("offset:%f POLLADJ_GATE*discipline_jitter:%f poll:%s",
-                               q->filter_offset, POLLADJ_GATE * G.discipline_jitter,
-                               fabs(q->filter_offset) < POLLADJ_GATE * G.discipline_jitter
-                                       ? "grows" : "falls"
-                       );
-               }
-               if (rc > 0 && fabs(q->filter_offset) < POLLADJ_GATE * G.discipline_jitter) {
+               if (rc > 0 && G.offset_to_jitter_ratio <= POLLADJ_GATE) {
                        /* was += G.poll_exp but it is a bit
                         * too optimistic for my taste at high poll_exp's */
                        G.polladj_count += MINPOLL;
@@ -1672,11 +1847,11 @@ recv_and_process_peer_pkt(peer_t *p)
                                G.polladj_count = 0;
                                if (G.poll_exp < MAXPOLL) {
                                        G.poll_exp++;
-                                       VERB3 bb_error_msg("polladj: discipline_jitter:%f ++poll_exp=%d",
+                                       VERB4 bb_error_msg("polladj: discipline_jitter:%f ++poll_exp=%d",
                                                        G.discipline_jitter, G.poll_exp);
                                }
                        } else {
-                               VERB3 bb_error_msg("polladj: incr:%d", G.polladj_count);
+                               VERB4 bb_error_msg("polladj: incr:%d", G.polladj_count);
                        }
                } else {
                        G.polladj_count -= G.poll_exp * 2;
@@ -1698,28 +1873,35 @@ recv_and_process_peer_pkt(peer_t *p)
                                                if (pp->p_fd < 0)
                                                        pp->next_action_time -= (1 << G.poll_exp);
                                        }
-                                       VERB3 bb_error_msg("polladj: discipline_jitter:%f --poll_exp=%d",
+                                       VERB4 bb_error_msg("polladj: discipline_jitter:%f --poll_exp=%d",
                                                        G.discipline_jitter, G.poll_exp);
                                }
                        } else {
-                               VERB3 bb_error_msg("polladj: decr:%d", G.polladj_count);
+                               VERB4 bb_error_msg("polladj: decr:%d", G.polladj_count);
                        }
                }
        }
 
        /* Decide when to send new query for this peer */
+ pick_normal_interval:
        interval = poll_interval(0);
+       if (fabs(offset) >= STEP_THRESHOLD * 8 && interval > BIGOFF_INTERVAL) {
+               /* If we are synced, offsets are less than STEP_THRESHOLD,
+                * or at the very least not much larger than it.
+                * Now we see a largish one.
+                * Either this peer is feeling bad, or packet got corrupted,
+                * or _our_ clock is wrong now and _all_ peers will show similar
+                * largish offsets too.
+                * I observed this with laptop suspend stopping clock.
+                * In any case, it makes sense to make next request soonish:
+                * cases 1 and 2: get a better datapoint,
+                * case 3: allows to resync faster.
+                */
+               interval = BIGOFF_INTERVAL;
+       }
 
- set_next_and_close_sock:
+ set_next_and_ret:
        set_next(p, interval);
-       /* We do not expect any more packets from this peer for now.
-        * Closing the socket informs kernel about it.
-        * We open a new socket when we send a new query.
-        */
-       close(p->p_fd);
-       p->p_fd = -1;
- bail:
-       return;
 }
 
 #if ENABLE_FEATURE_NTPD_SERVER
@@ -1727,17 +1909,17 @@ static NOINLINE void
 recv_and_process_client_pkt(void /*int fd*/)
 {
        ssize_t          size;
-       uint8_t          version;
+       //uint8_t          version;
        len_and_sockaddr *to;
        struct sockaddr  *from;
        msg_t            msg;
        uint8_t          query_status;
        l_fixedpt_t      query_xmttime;
 
-       to = get_sock_lsa(G.listen_fd);
+       to = get_sock_lsa(G_listen_fd);
        from = xzalloc(to->len);
 
-       size = recv_from_to(G.listen_fd, &msg, sizeof(msg), MSG_DONTWAIT, from, &to->u.sa, to->len);
+       size = recv_from_to(G_listen_fd, &msg, sizeof(msg), MSG_DONTWAIT, from, &to->u.sa, to->len);
        if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
                char *addr;
                if (size < 0) {
@@ -1756,27 +1938,31 @@ recv_and_process_client_pkt(void /*int fd*/)
 
        /* Build a reply packet */
        memset(&msg, 0, sizeof(msg));
-       msg.m_status = G.stratum < MAXSTRAT ? G.ntp_status : LI_ALARM;
+       msg.m_status = G.stratum < MAXSTRAT ? (G.ntp_status & LI_MASK) : LI_ALARM;
        msg.m_status |= (query_status & VERSION_MASK);
        msg.m_status |= ((query_status & MODE_MASK) == MODE_CLIENT) ?
-                        MODE_SERVER : MODE_SYM_PAS;
+                       MODE_SERVER : MODE_SYM_PAS;
        msg.m_stratum = G.stratum;
        msg.m_ppoll = G.poll_exp;
        msg.m_precision_exp = G_precision_exp;
        /* this time was obtained between poll() and recv() */
        msg.m_rectime = d_to_lfp(G.cur_time);
        msg.m_xmttime = d_to_lfp(gettime1900d()); /* this instant */
+       if (G.peer_cnt == 0) {
+               /* we have no peers: "stratum 1 server" mode. reftime = our own time */
+               G.reftime = G.cur_time;
+       }
        msg.m_reftime = d_to_lfp(G.reftime);
        msg.m_orgtime = query_xmttime;
        msg.m_rootdelay = d_to_sfp(G.rootdelay);
 //simple code does not do this, fix simple code!
        msg.m_rootdisp = d_to_sfp(G.rootdisp);
-       version = (query_status & VERSION_MASK); /* ... >> VERSION_SHIFT - done below instead */
+       //version = (query_status & VERSION_MASK); /* ... >> VERSION_SHIFT - done below instead */
        msg.m_refid = G.refid; // (version > (3 << VERSION_SHIFT)) ? G.refid : G.refid3;
 
        /* We reply from the local address packet was sent to,
         * this makes to/from look swapped here: */
-       do_sendto(G.listen_fd,
+       do_sendto(G_listen_fd,
                /*from:*/ &to->u.sa, /*to:*/ from, /*addrlen:*/ to->len,
                &msg, size);
 
@@ -1903,27 +2089,54 @@ static NOINLINE void ntp_init(char **argv)
                bb_show_usage();
 //     if (opts & OPT_x) /* disable stepping, only slew is allowed */
 //             G.time_was_stepped = 1;
-       while (peers)
-               add_peers(llist_pop(&peers));
+       if (peers) {
+               while (peers)
+                       add_peers(llist_pop(&peers));
+       } else {
+               /* -l but no peers: "stratum 1 server" mode */
+               G.stratum = 1;
+       }
        if (!(opts & OPT_n)) {
                bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO, argv);
                logmode = LOGMODE_NONE;
        }
 #if ENABLE_FEATURE_NTPD_SERVER
-       G.listen_fd = -1;
+       G_listen_fd = -1;
        if (opts & OPT_l) {
-               G.listen_fd = create_and_bind_dgram_or_die(NULL, 123);
-               socket_want_pktinfo(G.listen_fd);
-               setsockopt(G.listen_fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY));
+               G_listen_fd = create_and_bind_dgram_or_die(NULL, 123);
+               socket_want_pktinfo(G_listen_fd);
+               setsockopt(G_listen_fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY));
        }
 #endif
        /* I hesitate to set -20 prio. -15 should be high enough for timekeeping */
        if (opts & OPT_N)
                setpriority(PRIO_PROCESS, 0, -15);
 
-       bb_signals((1 << SIGTERM) | (1 << SIGINT), record_signo);
-       /* Removed SIGHUP here: */
-       bb_signals((1 << SIGPIPE) | (1 << SIGCHLD), SIG_IGN);
+       /* If network is up, syncronization occurs in ~10 seconds.
+        * We give "ntpd -q" 10 seconds to get first reply,
+        * then another 50 seconds to finish syncing.
+        *
+        * I tested ntpd 4.2.6p1 and apparently it never exits
+        * (will try forever), but it does not feel right.
+        * The goal of -q is to act like ntpdate: set time
+        * after a reasonably small period of polling, or fail.
+        */
+       if (opts & OPT_q) {
+               option_mask32 |= OPT_qq;
+               alarm(10);
+       }
+
+       bb_signals(0
+               | (1 << SIGTERM)
+               | (1 << SIGINT)
+               | (1 << SIGALRM)
+               , record_signo
+       );
+       bb_signals(0
+               | (1 << SIGPIPE)
+               | (1 << SIGCHLD)
+               , SIG_IGN
+       );
 }
 
 int ntpd_main(int argc UNUSED_PARAM, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -1945,14 +2158,16 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv)
        idx2peer = xzalloc(sizeof(idx2peer[0]) * cnt);
        pfd = xzalloc(sizeof(pfd[0]) * cnt);
 
-       /* Countdown: we never sync before we sent INITIAL_SAMLPES+1
+       /* Countdown: we never sync before we sent INITIAL_SAMPLES+1
         * packets to each peer.
         * NB: if some peer is not responding, we may end up sending
         * fewer packets to it and more to other peers.
-        * NB2: sync usually happens using INITIAL_SAMLPES packets,
+        * NB2: sync usually happens using INITIAL_SAMPLES packets,
         * since last reply does not come back instantaneously.
         */
-       cnt = G.peer_cnt * (INITIAL_SAMLPES + 1);
+       cnt = G.peer_cnt * (INITIAL_SAMPLES + 1);
+
+       write_pidfile(CONFIG_PID_FILE_PATH "/ntpd.pid");
 
        while (!bb_got_signal) {
                llist_t *item;
@@ -1966,8 +2181,8 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv)
 
                i = 0;
 #if ENABLE_FEATURE_NTPD_SERVER
-               if (G.listen_fd != -1) {
-                       pfd[0].fd = G.listen_fd;
+               if (G_listen_fd != -1) {
+                       pfd[0].fd = G_listen_fd;
                        pfd[0].events = POLLIN;
                        i++;
                }
@@ -2012,16 +2227,33 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv)
                timeout++; /* (nextaction - G.cur_time) rounds down, compensating */
 
                /* Here we may block */
-               VERB2 bb_error_msg("poll %us, sockets:%u, poll interval:%us", timeout, i, 1 << G.poll_exp);
+               VERB2 {
+                       if (i > (ENABLE_FEATURE_NTPD_SERVER && G_listen_fd != -1)) {
+                               /* We wait for at least one reply.
+                                * Poll for it, without wasting time for message.
+                                * Since replies often come under 1 second, this also
+                                * reduces clutter in logs.
+                                */
+                               nfds = poll(pfd, i, 1000);
+                               if (nfds != 0)
+                                       goto did_poll;
+                               if (--timeout <= 0)
+                                       goto did_poll;
+                       }
+                       bb_error_msg("poll:%us sockets:%u interval:%us", timeout, i, 1 << G.poll_exp);
+               }
                nfds = poll(pfd, i, timeout * 1000);
+ did_poll:
                gettime1900d(); /* sets G.cur_time */
                if (nfds <= 0) {
-                       if (G.script_name && G.cur_time - G.last_script_run > 11*60) {
+                       if (!bb_got_signal /* poll wasn't interrupted by a signal */
+                        && G.cur_time - G.last_script_run > 11*60
+                       ) {
                                /* Useful for updating battery-backed RTC and such */
                                run_script("periodic", G.last_update_offset);
                                gettime1900d(); /* sets G.cur_time */
                        }
-                       continue;
+                       goto check_unsync;
                }
 
                /* Process any received packets */
@@ -2038,13 +2270,38 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv)
 #endif
                for (; nfds != 0 && j < i; j++) {
                        if (pfd[j].revents /* & (POLLIN|POLLERR)*/) {
+                               /*
+                                * At init, alarm was set to 10 sec.
+                                * Now we did get a reply.
+                                * Increase timeout to 50 seconds to finish syncing.
+                                */
+                               if (option_mask32 & OPT_qq) {
+                                       option_mask32 &= ~OPT_qq;
+                                       alarm(50);
+                               }
                                nfds--;
                                recv_and_process_peer_pkt(idx2peer[j]);
                                gettime1900d(); /* sets G.cur_time */
                        }
                }
+
+ check_unsync:
+               if (G.ntp_peers && G.stratum != MAXSTRAT) {
+                       for (item = G.ntp_peers; item != NULL; item = item->link) {
+                               peer_t *p = (peer_t *) item->data;
+                               if (p->reachable_bits)
+                                       goto have_reachable_peer;
+                       }
+                       /* No peer responded for last 8 packets, panic */
+                       G.polladj_count = 0;
+                       G.poll_exp = MINPOLL;
+                       G.stratum = MAXSTRAT;
+                       run_script("unsync", 0.0);
+ have_reachable_peer: ;
+               }
        } /* while (!bb_got_signal) */
 
+       remove_pidfile(CONFIG_PID_FILE_PATH "/ntpd.pid");
        kill_myself_with_sig(bb_got_signal);
 }
 
@@ -2061,7 +2318,6 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv)
 static double
 direct_freq(double fp_offset)
 {
-
 #ifdef KERNEL_PLL
        /*
         * If the kernel is enabled, we need the residual offset to
@@ -2084,7 +2340,7 @@ direct_freq(double fp_offset)
 }
 
 static void
-set_freq(double        freq) /* frequency update */
+set_freq(double freq) /* frequency update */
 {
        char tbuf[80];
 
@@ -2176,14 +2432,13 @@ set_freq(double freq) /* frequency update */
                        if (pps_enable) {
                                if (!(pll_status & STA_PPSTIME))
                                        report_event(EVNT_KERN,
-                                           NULL, "PPS enabled");
+                                               NULL, "PPS enabled");
                                ntv.status |= STA_PPSTIME | STA_PPSFREQ;
                        } else {
                                if (pll_status & STA_PPSTIME)
                                        report_event(EVNT_KERN,
-                                           NULL, "PPS disabled");
-                               ntv.status &= ~(STA_PPSTIME |
-                                   STA_PPSFREQ);
+                                               NULL, "PPS disabled");
+                               ntv.status &= ~(STA_PPSTIME | STA_PPSFREQ);
                        }
                        if (sys_leap == LEAP_ADDSECOND)
                                ntv.status |= STA_INS;
@@ -2199,7 +2454,7 @@ set_freq(double   freq) /* frequency update */
                if (ntp_adjtime(&ntv) == TIME_ERROR) {
                        if (!(ntv.status & STA_PPSSIGNAL))
                                report_event(EVNT_KERN, NULL,
-                                   "PPS no signal");
+                                               "PPS no signal");
                }
                pll_status = ntv.status;
 #ifdef STA_NANO
index 5e48306..3e7fc47 100644 (file)
@@ -3,10 +3,11 @@
  *
  * Author: Adam Tkac <vonsch@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 #include <netinet/ip.h> /* For IPTOS_LOWDELAY definition */
+#include <sys/resource.h> /* setpriority */
 #ifndef IPTOS_LOWDELAY
 # define IPTOS_LOWDELAY 0x10
 #endif
@@ -377,7 +378,7 @@ step_time_once(double offset)
                bb_perror_msg_and_die("settimeofday");
 
        tval = tv.tv_sec;
-       strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Z %Y", localtime(&tval));
+       strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &tval);
 
        bb_error_msg("setting clock to %s (offset %fs)", buf, offset);
 
@@ -709,7 +710,7 @@ recv_and_process_client_pkt(void /*int fd*/)
        msg.m_status = G.synced ? G.leap : LI_ALARM;
        msg.m_status |= (query_status & VERSION_MASK);
        msg.m_status |= ((query_status & MODE_MASK) == MODE_CLIENT) ?
-                        MODE_SERVER : MODE_SYM_PAS;
+                       MODE_SERVER : MODE_SYM_PAS;
        msg.m_stratum = G.stratum;
        msg.m_ppoll = query_ppoll;
        msg.m_precision_exp = G_precision_exp;
@@ -870,7 +871,7 @@ static NOINLINE void ntp_init(char **argv)
                int prec = 0;
                int b;
 # if 0
-               struct timespec tp;
+               struct timespec tp;
                /* We can use sys_clock_getres but assuming 10ms tick should be fine */
                clock_getres(CLOCK_REALTIME, &tp);
                tp.tv_sec = 0;
index 6a766a4..5d71fe8 100644 (file)
@@ -11,7 +11,7 @@
  * This code is derived from software contributed to Berkeley by
  * Mike Muuss.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 /* from ping6.c:
  * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
 #include <netinet/ip_icmp.h>
 #include "libbb.h"
 
+#ifdef __BIONIC__
+/* should be in netinet/ip_icmp.h */
+# define ICMP_DEST_UNREACH    3  /* Destination Unreachable  */
+# define ICMP_SOURCE_QUENCH   4  /* Source Quench    */
+# define ICMP_REDIRECT        5  /* Redirect (change route)  */
+# define ICMP_ECHO            8  /* Echo Request      */
+# define ICMP_TIME_EXCEEDED  11  /* Time Exceeded    */
+# define ICMP_PARAMETERPROB  12  /* Parameter Problem    */
+# define ICMP_TIMESTAMP      13  /* Timestamp Request    */
+# define ICMP_TIMESTAMPREPLY 14  /* Timestamp Reply    */
+# define ICMP_INFO_REQUEST   15  /* Information Request    */
+# define ICMP_INFO_REPLY     16  /* Information Reply    */
+# define ICMP_ADDRESS        17  /* Address Mask Request    */
+# define ICMP_ADDRESSREPLY   18  /* Address Mask Reply    */
+#endif
+
+//config:config PING
+//config:      bool "ping"
+//config:      default y
+//config:      select PLATFORM_LINUX
+//config:      help
+//config:        ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to
+//config:        elicit an ICMP ECHO_RESPONSE from a host or gateway.
+//config:
+//config:config PING6
+//config:      bool "ping6"
+//config:      default y
+//config:      depends on FEATURE_IPV6 && PING
+//config:      help
+//config:        This will give you a ping that can talk IPv6.
+//config:
+//config:config FEATURE_FANCY_PING
+//config:      bool "Enable fancy ping output"
+//config:      default y
+//config:      depends on PING
+//config:      help
+//config:        Make the output from the ping applet include statistics, and at the
+//config:        same time provide full support for ICMP packets.
+
+/* Needs socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), therefore BB_SUID_MAYBE: */
+//applet:IF_PING(APPLET(ping, BB_DIR_BIN, BB_SUID_MAYBE))
+//applet:IF_PING6(APPLET(ping6, BB_DIR_BIN, BB_SUID_MAYBE))
+
+//kbuild:lib-$(CONFIG_PING)  += ping.o
+//kbuild:lib-$(CONFIG_PING6) += ping.o
+
+//usage:#if !ENABLE_FEATURE_FANCY_PING
+//usage:# define ping_trivial_usage
+//usage:       "HOST"
+//usage:# define ping_full_usage "\n\n"
+//usage:       "Send ICMP ECHO_REQUEST packets to network hosts"
+//usage:# define ping6_trivial_usage
+//usage:       "HOST"
+//usage:# define ping6_full_usage "\n\n"
+//usage:       "Send ICMP ECHO_REQUEST packets to network hosts"
+//usage:#else
+//usage:# define ping_trivial_usage
+//usage:       "[OPTIONS] HOST"
+//usage:# define ping_full_usage "\n\n"
+//usage:       "Send ICMP ECHO_REQUEST packets to network hosts\n"
+//usage:       IF_PING6(
+//usage:     "\n       -4,-6           Force IP or IPv6 name resolution"
+//usage:       )
+//usage:     "\n       -c CNT          Send only CNT pings"
+//usage:     "\n       -s SIZE         Send SIZE data bytes in packets (default:56)"
+//usage:     "\n       -t TTL          Set TTL"
+//usage:     "\n       -I IFACE/IP     Use interface or IP address as source"
+//usage:     "\n       -W SEC          Seconds to wait for the first response (default:10)"
+//usage:     "\n                       (after all -c CNT packets are sent)"
+//usage:     "\n       -w SEC          Seconds until ping exits (default:infinite)"
+//usage:     "\n                       (can exit earlier with -c CNT)"
+//usage:     "\n       -q              Quiet, only displays output at start"
+//usage:     "\n                       and when finished"
+//usage:
+//usage:# define ping6_trivial_usage
+//usage:       "[OPTIONS] HOST"
+//usage:# define ping6_full_usage "\n\n"
+//usage:       "Send ICMP ECHO_REQUEST packets to network hosts\n"
+//usage:     "\n       -c CNT          Send only CNT pings"
+//usage:     "\n       -s SIZE         Send SIZE data bytes in packets (default:56)"
+//usage:     "\n       -I IFACE/IP     Use interface or IP address as source"
+//usage:     "\n       -q              Quiet, only displays output at start"
+//usage:     "\n                       and when finished"
+//usage:
+//usage:#endif
+//usage:
+//usage:#define ping_example_usage
+//usage:       "$ ping localhost\n"
+//usage:       "PING slag (127.0.0.1): 56 data bytes\n"
+//usage:       "64 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=20.1 ms\n"
+//usage:       "\n"
+//usage:       "--- debian ping statistics ---\n"
+//usage:       "1 packets transmitted, 1 packets received, 0% packet loss\n"
+//usage:       "round-trip min/avg/max = 20.1/20.1/20.1 ms\n"
+//usage:#define ping6_example_usage
+//usage:       "$ ping6 ip6-localhost\n"
+//usage:       "PING ip6-localhost (::1): 56 data bytes\n"
+//usage:       "64 bytes from ::1: icmp6_seq=0 ttl=64 time=20.1 ms\n"
+//usage:       "\n"
+//usage:       "--- ip6-localhost ping statistics ---\n"
+//usage:       "1 packets transmitted, 1 packets received, 0% packet loss\n"
+//usage:       "round-trip min/avg/max = 20.1/20.1/20.1 ms\n"
+
 #if ENABLE_PING6
 # include <netinet/icmp6.h>
 /* I see RENUMBERED constants in bits/in.h - !!?
@@ -46,31 +149,44 @@ enum {
        MAX_DUP_CHK = (8 * 128),
        MAXWAIT = 10,
        PINGINTERVAL = 1, /* 1 second */
+       pingsock = 0,
 };
 
-/* Common routines */
-
-static int in_cksum(unsigned short *buf, int sz)
+static void
+#if ENABLE_PING6
+create_icmp_socket(len_and_sockaddr *lsa)
+#else
+create_icmp_socket(void)
+#define create_icmp_socket(lsa) create_icmp_socket()
+#endif
 {
-       int nleft = sz;
-       int sum = 0;
-       unsigned short *w = buf;
-       unsigned short ans = 0;
-
-       while (nleft > 1) {
-               sum += *w++;
-               nleft -= 2;
-       }
-
-       if (nleft == 1) {
-               *(unsigned char *) (&ans) = *(unsigned char *) w;
-               sum += ans;
+       int sock;
+#if ENABLE_PING6
+       if (lsa->u.sa.sa_family == AF_INET6)
+               sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+       else
+#endif
+               sock = socket(AF_INET, SOCK_RAW, 1); /* 1 == ICMP */
+       if (sock < 0) {
+               if (errno != EPERM)
+                       bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
+#if defined(__linux__) || defined(__APPLE__)
+               /* We don't have root privileges.  Try SOCK_DGRAM instead.
+                * Linux needs net.ipv4.ping_group_range for this to work.
+                * MacOSX allows ICMP_ECHO, ICMP_TSTAMP or ICMP_MASKREQ
+                */
+#if ENABLE_PING6
+               if (lsa->u.sa.sa_family == AF_INET6)
+                       sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
+               else
+#endif
+                       sock = socket(AF_INET, SOCK_DGRAM, 1); /* 1 == ICMP */
+               if (sock < 0)
+#endif
+               bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
        }
 
-       sum = (sum >> 16) + (sum & 0xFFFF);
-       sum += (sum >> 16);
-       ans = ~sum;
-       return ans;
+       xmove_fd(sock, pingsock);
 }
 
 #if !ENABLE_FEATURE_FANCY_PING
@@ -93,24 +209,26 @@ static void noresp(int ign UNUSED_PARAM)
 static void ping4(len_and_sockaddr *lsa)
 {
        struct icmp *pkt;
-       int pingsock, c;
-
-       pingsock = create_icmp_socket();
+       int c;
 
        pkt = (struct icmp *) G.packet;
-       memset(pkt, 0, sizeof(G.packet));
+       /*memset(pkt, 0, sizeof(G.packet)); already is */
        pkt->icmp_type = ICMP_ECHO;
-       pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(G.packet));
+       pkt->icmp_cksum = inet_cksum((uint16_t *) pkt, sizeof(G.packet));
 
        xsendto(pingsock, G.packet, DEFDATALEN + ICMP_MINLEN, &lsa->u.sa, lsa->len);
 
        /* listen for replies */
        while (1) {
+#if 0
                struct sockaddr_in from;
                socklen_t fromlen = sizeof(from);
 
                c = recvfrom(pingsock, G.packet, sizeof(G.packet), 0,
                                (struct sockaddr *) &from, &fromlen);
+#else
+               c = recv(pingsock, G.packet, sizeof(G.packet), 0);
+#endif
                if (c < 0) {
                        if (errno != EINTR)
                                bb_perror_msg("recvfrom");
@@ -132,13 +250,11 @@ static void ping4(len_and_sockaddr *lsa)
 static void ping6(len_and_sockaddr *lsa)
 {
        struct icmp6_hdr *pkt;
-       int pingsock, c;
+       int c;
        int sockopt;
 
-       pingsock = create_icmp6_socket();
-
        pkt = (struct icmp6_hdr *) G.packet;
-       memset(pkt, 0, sizeof(G.packet));
+       /*memset(pkt, 0, sizeof(G.packet)); already is */
        pkt->icmp6_type = ICMP6_ECHO_REQUEST;
 
        sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
@@ -148,18 +264,21 @@ static void ping6(len_and_sockaddr *lsa)
 
        /* listen for replies */
        while (1) {
+#if 0
                struct sockaddr_in6 from;
                socklen_t fromlen = sizeof(from);
 
                c = recvfrom(pingsock, G.packet, sizeof(G.packet), 0,
                                (struct sockaddr *) &from, &fromlen);
+#else
+               c = recv(pingsock, G.packet, sizeof(G.packet), 0);
+#endif
                if (c < 0) {
                        if (errno != EINTR)
                                bb_perror_msg("recvfrom");
                        continue;
                }
-               if (c >= ICMP_MINLEN) {                 /* icmp6_hdr */
-                       pkt = (struct icmp6_hdr *) G.packet;
+               if (c >= ICMP_MINLEN) { /* icmp6_hdr */
                        if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
                                break;
                }
@@ -207,6 +326,7 @@ static int common_ping_main(sa_family_t af, char **argv)
        signal(SIGALRM, noresp);
        alarm(5); /* give the host 5000ms to respond */
 
+       create_icmp_socket(lsa);
 #if ENABLE_PING6
        if (lsa->u.sa.sa_family == AF_INET6)
                ping6(lsa);
@@ -223,27 +343,29 @@ static int common_ping_main(sa_family_t af, char **argv)
 
 /* Full(er) version */
 
-#define OPT_STRING ("qvc:s:w:W:I:4" IF_PING6("6"))
+#define OPT_STRING ("qvc:s:t:w:W:I:n4" IF_PING6("6"))
 enum {
        OPT_QUIET = 1 << 0,
        OPT_VERBOSE = 1 << 1,
        OPT_c = 1 << 2,
        OPT_s = 1 << 3,
-       OPT_w = 1 << 4,
-       OPT_W = 1 << 5,
-       OPT_I = 1 << 6,
-       OPT_IPV4 = 1 << 7,
-       OPT_IPV6 = (1 << 8) * ENABLE_PING6,
+       OPT_t = 1 << 4,
+       OPT_w = 1 << 5,
+       OPT_W = 1 << 6,
+       OPT_I = 1 << 7,
+       /*OPT_n = 1 << 8, - ignored */
+       OPT_IPV4 = 1 << 9,
+       OPT_IPV6 = (1 << 10) * ENABLE_PING6,
 };
 
 
 struct globals {
-       int pingsock;
        int if_index;
        char *str_I;
        len_and_sockaddr *source_lsa;
        unsigned datalen;
        unsigned pingcount; /* must be int-sized */
+       unsigned opt_ttl;
        unsigned long ntransmitted, nreceived, nrepeats;
        uint16_t myid;
        unsigned tmin, tmax; /* in us */
@@ -263,18 +385,15 @@ struct globals {
                struct sockaddr_in6 sin6;
 #endif
        } pingaddr;
-       char rcvd_tbl[MAX_DUP_CHK / 8];
+       unsigned char rcvd_tbl[MAX_DUP_CHK / 8];
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
-#define pingsock     (G.pingsock    )
 #define if_index     (G.if_index    )
 #define source_lsa   (G.source_lsa  )
 #define str_I        (G.str_I       )
 #define datalen      (G.datalen     )
-#define ntransmitted (G.ntransmitted)
-#define nreceived    (G.nreceived   )
-#define nrepeats     (G.nrepeats    )
 #define pingcount    (G.pingcount   )
+#define opt_ttl      (G.opt_ttl     )
 #define myid         (G.myid        )
 #define tmin         (G.tmin        )
 #define tmax         (G.tmax        )
@@ -290,59 +409,65 @@ void BUG_ping_globals_too_big(void);
 #define INIT_G() do { \
        if (sizeof(G) > COMMON_BUFSIZE) \
                BUG_ping_globals_too_big(); \
-       pingsock = -1; \
        datalen = DEFDATALEN; \
        timeout = MAXWAIT; \
        tmin = UINT_MAX; \
 } while (0)
 
 
-#define        A(bit)          rcvd_tbl[(bit)>>3]      /* identify byte in array */
-#define        B(bit)          (1 << ((bit) & 0x07))   /* identify bit in byte */
-#define        SET(bit)        (A(bit) |= B(bit))
-#define        CLR(bit)        (A(bit) &= (~B(bit)))
-#define        TST(bit)        (A(bit) & B(bit))
-
-/**************************************************************************/
+#define BYTE(bit)      rcvd_tbl[(bit)>>3]
+#define MASK(bit)      (1 << ((bit) & 7))
+#define SET(bit)       (BYTE(bit) |= MASK(bit))
+#define CLR(bit)       (BYTE(bit) &= (~MASK(bit)))
+#define TST(bit)       (BYTE(bit) & MASK(bit))
 
 static void print_stats_and_exit(int junk) NORETURN;
 static void print_stats_and_exit(int junk UNUSED_PARAM)
 {
+       unsigned long ul;
+       unsigned long nrecv;
+
        signal(SIGINT, SIG_IGN);
 
-       printf("\n--- %s ping statistics ---\n", hostname);
-       printf("%lu packets transmitted, ", ntransmitted);
-       printf("%lu packets received, ", nreceived);
-       if (nrepeats)
-               printf("%lu duplicates, ", nrepeats);
-       if (ntransmitted)
-               ntransmitted = (ntransmitted - nreceived) * 100 / ntransmitted;
-       printf("%lu%% packet loss\n", ntransmitted);
+       nrecv = G.nreceived;
+       printf("\n--- %s ping statistics ---\n"
+               "%lu packets transmitted, "
+               "%lu packets received, ",
+               hostname, G.ntransmitted, nrecv
+       );
+       if (G.nrepeats)
+               printf("%lu duplicates, ", G.nrepeats);
+       ul = G.ntransmitted;
+       if (ul != 0)
+               ul = (ul - nrecv) * 100 / ul;
+       printf("%lu%% packet loss\n", ul);
        if (tmin != UINT_MAX) {
-               unsigned tavg = tsum / (nreceived + nrepeats);
+               unsigned tavg = tsum / (nrecv + G.nrepeats);
                printf("round-trip min/avg/max = %u.%03u/%u.%03u/%u.%03u ms\n",
                        tmin / 1000, tmin % 1000,
                        tavg / 1000, tavg % 1000,
                        tmax / 1000, tmax % 1000);
        }
        /* if condition is true, exit with 1 -- 'failure' */
-       exit(nreceived == 0 || (deadline && nreceived < pingcount));
+       exit(nrecv == 0 || (deadline && nrecv < pingcount));
 }
 
-static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt)
+static void sendping_tail(void (*sp)(int), int size_pkt)
 {
        int sz;
 
-       CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
-       ntransmitted++;
+       CLR((uint16_t)G.ntransmitted % MAX_DUP_CHK);
+       G.ntransmitted++;
+
+       size_pkt += datalen;
 
        /* sizeof(pingaddr) can be larger than real sa size, but I think
         * it doesn't matter */
-       sz = xsendto(pingsock, pkt, size_pkt, &pingaddr.sa, sizeof(pingaddr));
+       sz = xsendto(pingsock, G.snd_packet, size_pkt, &pingaddr.sa, sizeof(pingaddr));
        if (sz != size_pkt)
                bb_error_msg_and_die(bb_msg_write_error);
 
-       if (pingcount == 0 || deadline || ntransmitted < pingcount) {
+       if (pingcount == 0 || deadline || G.ntransmitted < pingcount) {
                /* Didn't send all pings yet - schedule next in 1s */
                signal(SIGALRM, sp);
                if (deadline) {
@@ -358,7 +483,7 @@ static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt)
                 * otherwise ping waits for two RTTs. */
                unsigned expire = timeout;
 
-               if (nreceived) {
+               if (G.nreceived) {
                        /* approx. 2*tmax, in seconds (2 RTT) */
                        expire = tmax / (512*1024);
                        if (expire == 0)
@@ -377,7 +502,7 @@ static void sendping4(int junk UNUSED_PARAM)
        pkt->icmp_type = ICMP_ECHO;
        /*pkt->icmp_code = 0;*/
        pkt->icmp_cksum = 0; /* cksum is calculated with this field set to 0 */
-       pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
+       pkt->icmp_seq = htons(G.ntransmitted); /* don't ++ here, it can be a macro */
        pkt->icmp_id = myid;
 
        /* If datalen < 4, we store timestamp _past_ the packet,
@@ -387,28 +512,28 @@ static void sendping4(int junk UNUSED_PARAM)
                /* No hton: we'll read it back on the same machine */
                *(uint32_t*)&pkt->icmp_dun = monotonic_us();
 
-       pkt->icmp_cksum = in_cksum((unsigned short *) pkt, datalen + ICMP_MINLEN);
+       pkt->icmp_cksum = inet_cksum((uint16_t *) pkt, datalen + ICMP_MINLEN);
 
-       sendping_tail(sendping4, pkt, datalen + ICMP_MINLEN);
+       sendping_tail(sendping4, ICMP_MINLEN);
 }
 #if ENABLE_PING6
 static void sendping6(int junk UNUSED_PARAM)
 {
-       struct icmp6_hdr *pkt = alloca(datalen + sizeof(struct icmp6_hdr) + 4);
+       struct icmp6_hdr *pkt = G.snd_packet;
 
        //memset(pkt, 0, datalen + sizeof(struct icmp6_hdr) + 4);
        pkt->icmp6_type = ICMP6_ECHO_REQUEST;
        /*pkt->icmp6_code = 0;*/
        /*pkt->icmp6_cksum = 0;*/
-       pkt->icmp6_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
+       pkt->icmp6_seq = htons(G.ntransmitted); /* don't ++ here, it can be a macro */
        pkt->icmp6_id = myid;
 
        /*if (datalen >= 4)*/
-               *(uint32_t*)(&pkt->icmp6_data8[4]) = monotonic_us();
+               *(bb__aliased_uint32_t*)(&pkt->icmp6_data8[4]) = monotonic_us();
 
-       //TODO? pkt->icmp_cksum = in_cksum(...);
+       //TODO? pkt->icmp_cksum = inet_cksum(...);
 
-       sendping_tail(sendping6, pkt, datalen + sizeof(struct icmp6_hdr));
+       sendping_tail(sendping6, sizeof(struct icmp6_hdr));
 }
 #endif
 
@@ -464,11 +589,10 @@ static void unpack_tail(int sz, uint32_t *tp,
                const char *from_str,
                uint16_t recv_seq, int ttl)
 {
+       unsigned char *b, m;
        const char *dupmsg = " (DUP!)";
        unsigned triptime = triptime; /* for gcc */
 
-       ++nreceived;
-
        if (tp) {
                /* (int32_t) cast is for hypothetical 64-bit unsigned */
                /* (doesn't hurt 32-bit real-world anyway) */
@@ -480,11 +604,15 @@ static void unpack_tail(int sz, uint32_t *tp,
                        tmax = triptime;
        }
 
-       if (TST(recv_seq % MAX_DUP_CHK)) {
-               ++nrepeats;
-               --nreceived;
+       b = &BYTE(recv_seq % MAX_DUP_CHK);
+       m = MASK(recv_seq % MAX_DUP_CHK);
+       /*if TST(recv_seq % MAX_DUP_CHK):*/
+       if (*b & m) {
+               ++G.nrepeats;
        } else {
-               SET(recv_seq % MAX_DUP_CHK);
+               /*SET(recv_seq % MAX_DUP_CHK):*/
+               *b |= m;
+               ++G.nreceived;
                dupmsg += 7;
        }
 
@@ -532,7 +660,7 @@ static void unpack4(char *buf, int sz, struct sockaddr_in *from)
        }
 }
 #if ENABLE_PING6
-static void unpack6(char *packet, int sz, /*struct sockaddr_in6 *from,*/ int hoplimit)
+static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
 {
        struct icmp6_hdr *icmppkt;
        char buf[INET6_ADDRSTRLEN];
@@ -552,7 +680,7 @@ static void unpack6(char *packet, int sz, /*struct sockaddr_in6 *from,*/ int hop
                if (sz >= sizeof(struct icmp6_hdr) + sizeof(uint32_t))
                        tp = (uint32_t *) &icmppkt->icmp6_data8[4];
                unpack_tail(sz, tp,
-                       inet_ntop(AF_INET6, &pingaddr.sin6.sin6_addr,
+                       inet_ntop(AF_INET6, &from->sin6_addr,
                                        buf, sizeof(buf)),
                        recv_seq, hoplimit);
        } else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
@@ -567,7 +695,6 @@ static void ping4(len_and_sockaddr *lsa)
 {
        int sockopt;
 
-       pingsock = create_icmp_socket();
        pingaddr.sin = lsa->u.sin;
        if (source_lsa) {
                if (setsockopt(pingsock, IPPROTO_IP, IP_MULTICAST_IF,
@@ -575,8 +702,6 @@ static void ping4(len_and_sockaddr *lsa)
                        bb_error_msg_and_die("can't set multicast source interface");
                xbind(pingsock, &source_lsa->u.sa, source_lsa->len);
        }
-       if (str_I)
-               setsockopt_bindtodevice(pingsock, str_I);
 
        /* enable broadcast pings */
        setsockopt_broadcast(pingsock);
@@ -586,6 +711,12 @@ static void ping4(len_and_sockaddr *lsa)
        sockopt = (datalen * 2) + 7 * 1024; /* giving it a bit of extra room */
        setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
 
+       if (opt_ttl != 0) {
+               setsockopt(pingsock, IPPROTO_IP, IP_TTL, &opt_ttl, sizeof(opt_ttl));
+               /* above doesnt affect packets sent to bcast IP, so... */
+               setsockopt(pingsock, IPPROTO_IP, IP_MULTICAST_TTL, &opt_ttl, sizeof(opt_ttl));
+       }
+
        signal(SIGINT, print_stats_and_exit);
 
        /* start the ping's going ... */
@@ -605,7 +736,7 @@ static void ping4(len_and_sockaddr *lsa)
                        continue;
                }
                unpack4(G.rcv_packet, c, &from);
-               if (pingcount && nreceived >= pingcount)
+               if (pingcount && G.nreceived >= pingcount)
                        break;
        }
 }
@@ -619,13 +750,9 @@ static void ping6(len_and_sockaddr *lsa)
        struct iovec iov;
        char control_buf[CMSG_SPACE(36)];
 
-       pingsock = create_icmp6_socket();
        pingaddr.sin6 = lsa->u.sin6;
-       /* untested whether "-I addr" really works for IPv6: */
        if (source_lsa)
                xbind(pingsock, &source_lsa->u.sa, source_lsa->len);
-       if (str_I)
-               setsockopt_bindtodevice(pingsock, str_I);
 
 #ifdef ICMP6_FILTER
        {
@@ -637,7 +764,7 @@ static void ping6(len_and_sockaddr *lsa)
                        ICMP6_FILTER_SETPASSALL(&filt);
                }
                if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
-                                          sizeof(filt)) < 0)
+                                       sizeof(filt)) < 0)
                        bb_error_msg_and_die("setsockopt(ICMP6_FILTER)");
        }
 #endif /*ICMP6_FILTER*/
@@ -696,8 +823,8 @@ static void ping6(len_and_sockaddr *lsa)
                                move_from_unaligned_int(hoplimit, CMSG_DATA(mp));
                        }
                }
-               unpack6(G.rcv_packet, c, /*&from,*/ hoplimit);
-               if (pingcount && nreceived >= pingcount)
+               unpack6(G.rcv_packet, c, &from, hoplimit);
+               if (pingcount && G.nreceived >= pingcount)
                        break;
        }
 }
@@ -712,6 +839,11 @@ static void ping(len_and_sockaddr *lsa)
        }
        printf(": %d data bytes\n", datalen);
 
+       create_icmp_socket(lsa);
+       /* untested whether "-I addr" really works for IPv6: */
+       if (str_I)
+               setsockopt_bindtodevice(pingsock, str_I);
+
        G.sizeof_rcv_packet = datalen + MAXIPLEN + MAXICMPLEN;
        G.rcv_packet = xzalloc(G.sizeof_rcv_packet);
 #if ENABLE_PING6
@@ -735,9 +867,9 @@ static int common_ping_main(int opt, char **argv)
 
        INIT_G();
 
-       /* exactly one argument needed; -v and -q don't mix; -c NUM, -w NUM, -W NUM */
-       opt_complementary = "=1:q--v:v--q:c+:w+:W+";
-       opt |= getopt32(argv, OPT_STRING, &pingcount, &str_s, &deadline, &timeout, &str_I);
+       /* exactly one argument needed; -v and -q don't mix; -c NUM, -t NUM, -w NUM, -W NUM */
+       opt_complementary = "=1:q--v:v--q:c+:t+:w+:W+";
+       opt |= getopt32(argv, OPT_STRING, &pingcount, &str_s, &opt_ttl, &deadline, &timeout, &str_I);
        if (opt & OPT_s)
                datalen = xatou16(str_s); // -s
        if (opt & OPT_I) { // -I
index 5fb6af0..72ed8cd 100644 (file)
@@ -3,9 +3,20 @@
  *
  * Copyright 2007 Tito Ragusa <farmatito@tiscali.it>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define pscan_trivial_usage
+//usage:       "[-cb] [-p MIN_PORT] [-P MAX_PORT] [-t TIMEOUT] [-T MIN_RTT] HOST"
+//usage:#define pscan_full_usage "\n\n"
+//usage:       "Scan a host, print all open ports\n"
+//usage:     "\n       -c      Show closed ports too"
+//usage:     "\n       -b      Show blocked ports too"
+//usage:     "\n       -p      Scan from this port (default 1)"
+//usage:     "\n       -P      Scan up to this port (default 1024)"
+//usage:     "\n       -t      Timeout (default 5000 ms)"
+//usage:     "\n       -T      Minimum rtt (default 5 ms, increase for congested hosts)"
+
 #include "libbb.h"
 
 /* debugging */
@@ -76,7 +87,7 @@ int pscan_main(int argc UNUSED_PARAM, char **argv)
                DMSG("rtt %u", rtt_4);
 
                /* The SOCK_STREAM socket type is implemented on the TCP/IP protocol. */
-               set_nport(lsap, htons(port));
+               set_nport(&lsap->u.sa, htons(port));
                s = xsocket(lsap->u.sa.sa_family, SOCK_STREAM, 0);
                /* We need unblocking socket so we don't need to wait for ETIMEOUT. */
                /* Nonblocking connect typically "fails" with errno == EINPROGRESS */
@@ -146,7 +157,7 @@ int pscan_main(int argc UNUSED_PARAM, char **argv)
        }
        if (ENABLE_FEATURE_CLEAN_UP) free(lsap);
 
-       printf("%d closed, %d open, %d timed out (or blocked) ports\n",
+       printf("%u closed, %u open, %u timed out (or blocked) ports\n",
                                        closed_ports,
                                        open_ports,
                                        nports - (closed_ports + open_ports));
index a319962..4235ea7 100644 (file)
@@ -10,7 +10,7 @@
  *              Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
  *              (derived from FvK's 'route.c     1.70    01/04/94')
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  *
  * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
  * remove ridiculous amounts of bloat.
  */
 
+//usage:#define route_trivial_usage
+//usage:       "[{add|del|delete}]"
+//usage:#define route_full_usage "\n\n"
+//usage:       "Edit kernel routing tables\n"
+//usage:     "\n       -n      Don't resolve names"
+//usage:     "\n       -e      Display other/more information"
+//usage:     "\n       -A inet" IF_FEATURE_IPV6("{6}") "       Select address family"
+
 #include <net/route.h>
 #include <net/if.h>
 
@@ -153,7 +161,10 @@ static int kw_lookup(const char *kwtbl, char ***pargs)
 
 static NOINLINE void INET_setroute(int action, char **args)
 {
-       struct rtentry rt;
+       /* char buffer instead of bona-fide struct avoids aliasing warning */
+       char rt_buf[sizeof(struct rtentry)];
+       struct rtentry *const rt = (void *)rt_buf;
+
        const char *netmask = NULL;
        int skfd, isnet, xflag;
 
@@ -166,7 +177,7 @@ static NOINLINE void INET_setroute(int action, char **args)
        }
 
        /* Clean out the RTREQ structure. */
-       memset(&rt, 0, sizeof(rt));
+       memset(rt, 0, sizeof(*rt));
 
        {
                const char *target = *args++;
@@ -178,17 +189,17 @@ static NOINLINE void INET_setroute(int action, char **args)
                        int prefix_len;
 
                        prefix_len = xatoul_range(prefix+1, 0, 32);
-                       mask_in_addr(rt) = htonl( ~(0xffffffffUL >> prefix_len));
+                       mask_in_addr(*rt) = htonl( ~(0xffffffffUL >> prefix_len));
                        *prefix = '\0';
 #if HAVE_NEW_ADDRT
-                       rt.rt_genmask.sa_family = AF_INET;
+                       rt->rt_genmask.sa_family = AF_INET;
 #endif
                } else {
                        /* Default netmask. */
-                       netmask = bb_str_default;
+                       netmask = "default";
                }
                /* Prefer hostname lookup is -host flag (xflag==1) was given. */
-               isnet = INET_resolve(target, (struct sockaddr_in *) &rt.rt_dst,
+               isnet = INET_resolve(target, (struct sockaddr_in *) &rt->rt_dst,
                                                         (xflag & HOST_FLAG));
                if (isnet < 0) {
                        bb_error_msg_and_die("resolving %s", target);
@@ -204,20 +215,20 @@ static NOINLINE void INET_setroute(int action, char **args)
        }
 
        /* Fill in the other fields. */
-       rt.rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST));
+       rt->rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST));
 
        while (*args) {
                int k = kw_lookup(tbl_ipvx, &args);
                const char *args_m1 = args[-1];
 
                if (k & KW_IPVx_FLAG_ONLY) {
-                       rt.rt_flags |= flags_ipvx[k & 3];
+                       rt->rt_flags |= flags_ipvx[k & 3];
                        continue;
                }
 
 #if HAVE_NEW_ADDRT
                if (k == KW_IPVx_METRIC) {
-                       rt.rt_metric = xatoul(args_m1) + 1;
+                       rt->rt_metric = xatoul(args_m1) + 1;
                        continue;
                }
 #endif
@@ -225,7 +236,7 @@ static NOINLINE void INET_setroute(int action, char **args)
                if (k == KW_IPVx_NETMASK) {
                        struct sockaddr mask;
 
-                       if (mask_in_addr(rt)) {
+                       if (mask_in_addr(*rt)) {
                                bb_show_usage();
                        }
 
@@ -234,18 +245,18 @@ static NOINLINE void INET_setroute(int action, char **args)
                        if (isnet < 0) {
                                bb_error_msg_and_die("resolving %s", netmask);
                        }
-                       rt.rt_genmask = full_mask(mask);
+                       rt->rt_genmask = full_mask(mask);
                        continue;
                }
 
                if (k == KW_IPVx_GATEWAY) {
-                       if (rt.rt_flags & RTF_GATEWAY) {
+                       if (rt->rt_flags & RTF_GATEWAY) {
                                bb_show_usage();
                        }
 
                        isnet = INET_resolve(args_m1,
-                                                                (struct sockaddr_in *) &rt.rt_gateway, 1);
-                       rt.rt_flags |= RTF_GATEWAY;
+                                               (struct sockaddr_in *) &rt->rt_gateway, 1);
+                       rt->rt_flags |= RTF_GATEWAY;
 
                        if (isnet) {
                                if (isnet < 0) {
@@ -257,24 +268,24 @@ static NOINLINE void INET_setroute(int action, char **args)
                }
 
                if (k == KW_IPVx_MSS) { /* Check valid MSS bounds. */
-                       rt.rt_flags |= RTF_MSS;
-                       rt.rt_mss = xatoul_range(args_m1, 64, 32768);
+                       rt->rt_flags |= RTF_MSS;
+                       rt->rt_mss = xatoul_range(args_m1, 64, 32768);
                        continue;
                }
 
                if (k == KW_IPVx_WINDOW) {      /* Check valid window bounds. */
-                       rt.rt_flags |= RTF_WINDOW;
-                       rt.rt_window = xatoul_range(args_m1, 128, INT_MAX);
+                       rt->rt_flags |= RTF_WINDOW;
+                       rt->rt_window = xatoul_range(args_m1, 128, INT_MAX);
                        continue;
                }
 
 #ifdef RTF_IRTT
                if (k == KW_IPVx_IRTT) {
-                       rt.rt_flags |= RTF_IRTT;
-                       rt.rt_irtt = xatoul(args_m1);
-                       rt.rt_irtt *= (sysconf(_SC_CLK_TCK) / 100);     /* FIXME */
+                       rt->rt_flags |= RTF_IRTT;
+                       rt->rt_irtt = xatoul(args_m1);
+                       rt->rt_irtt *= (sysconf(_SC_CLK_TCK) / 100);    /* FIXME */
 #if 0                                  /* FIXME: do we need to check anything of this? */
-                       if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) {
+                       if (rt->rt_irtt < 1 || rt->rt_irtt > (120 * HZ)) {
                                bb_error_msg_and_die("bad irtt");
                        }
 #endif
@@ -284,9 +295,9 @@ static NOINLINE void INET_setroute(int action, char **args)
 
                /* Device is special in that it can be the last arg specified
                 * and doesn't requre the dev/device keyword in that case. */
-               if (!rt.rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
+               if (!rt->rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
                        /* Don't use args_m1 here since args may have changed! */
-                       rt.rt_dev = args[-1];
+                       rt->rt_dev = args[-1];
                        continue;
                }
 
@@ -295,41 +306,41 @@ static NOINLINE void INET_setroute(int action, char **args)
        }
 
 #ifdef RTF_REJECT
-       if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev) {
-               rt.rt_dev = (char*)"lo";
+       if ((rt->rt_flags & RTF_REJECT) && !rt->rt_dev) {
+               rt->rt_dev = (char*)"lo";
        }
 #endif
 
        /* sanity checks.. */
-       if (mask_in_addr(rt)) {
-               uint32_t mask = mask_in_addr(rt);
+       if (mask_in_addr(*rt)) {
+               uint32_t mask = mask_in_addr(*rt);
 
                mask = ~ntohl(mask);
-               if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) {
+               if ((rt->rt_flags & RTF_HOST) && mask != 0xffffffff) {
                        bb_error_msg_and_die("netmask %.8x and host route conflict",
                                                                 (unsigned int) mask);
                }
                if (mask & (mask + 1)) {
                        bb_error_msg_and_die("bogus netmask %s", netmask);
                }
-               mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr;
-               if (mask & ~(uint32_t)mask_in_addr(rt)) {
+               mask = ((struct sockaddr_in *) &rt->rt_dst)->sin_addr.s_addr;
+               if (mask & ~(uint32_t)mask_in_addr(*rt)) {
                        bb_error_msg_and_die("netmask and route address conflict");
                }
        }
 
        /* Fill out netmask if still unset */
-       if ((action == RTACTION_ADD) && (rt.rt_flags & RTF_HOST)) {
-               mask_in_addr(rt) = 0xffffffff;
+       if ((action == RTACTION_ADD) && (rt->rt_flags & RTF_HOST)) {
+               mask_in_addr(*rt) = 0xffffffff;
        }
 
        /* Create a socket to the INET kernel. */
        skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
 
        if (action == RTACTION_ADD)
-               xioctl(skfd, SIOCADDRT, &rt);
+               xioctl(skfd, SIOCADDRT, rt);
        else
-               xioctl(skfd, SIOCDELRT, &rt);
+               xioctl(skfd, SIOCDELRT, rt);
 
        if (ENABLE_FEATURE_CLEAN_UP) close(skfd);
 }
@@ -346,7 +357,7 @@ static NOINLINE void INET6_setroute(int action, char **args)
                /* We know args isn't NULL from the check in route_main. */
                const char *target = *args++;
 
-               if (strcmp(target, bb_str_default) == 0) {
+               if (strcmp(target, "default") == 0) {
                        prefix_len = 0;
                        memset(&sa6, 0, sizeof(sa6));
                } else {
@@ -398,7 +409,7 @@ static NOINLINE void INET6_setroute(int action, char **args)
                                bb_error_msg_and_die("resolving %s", args_m1);
                        }
                        memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
-                                  sizeof(struct in6_addr));
+                                       sizeof(struct in6_addr));
                        rt.rtmsg_flags |= RTF_GATEWAY;
                        continue;
                }
@@ -424,7 +435,7 @@ static NOINLINE void INET6_setroute(int action, char **args)
                struct ifreq ifr;
                memset(&ifr, 0, sizeof(ifr));
                strncpy_IFNAMSIZ(ifr.ifr_name, devname);
-               xioctl(skfd, SIOGIFINDEX, &ifr);
+               xioctl(skfd, SIOCGIFINDEX, &ifr);
                rt.rtmsg_ifindex = ifr.ifr_ifindex;
        }
 
@@ -487,17 +498,17 @@ void FAST_FUNC bb_displayroutes(int noresolve, int netstatfmt)
        FILE *fp = xfopen_for_read("/proc/net/route");
 
        printf("Kernel IP routing table\n"
-              "Destination     Gateway         Genmask         Flags %s Iface\n",
+               "Destination     Gateway         Genmask         Flags %s Iface\n",
                        netstatfmt ? "  MSS Window  irtt" : "Metric Ref    Use");
 
        if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */
-               goto ERROR;                /* Empty or missing line, or read error. */
+               goto ERROR;                /* Empty or missing line, or read error. */
        }
        while (1) {
                int r;
                r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
-                                  devname, &d, &g, &flgs, &ref, &use, &metric, &m,
-                                  &mtu, &win, &ir);
+                               devname, &d, &g, &flgs, &ref, &use, &metric, &m,
+                               &mtu, &win, &ir);
                if (r != 11) {
                        if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
                                break;
@@ -534,6 +545,7 @@ void FAST_FUNC bb_displayroutes(int noresolve, int netstatfmt)
                        printf("%-6d %-2d %7d %s\n", metric, ref, use, devname);
                }
        }
+       fclose(fp);
 }
 
 #if ENABLE_FEATURE_IPV6
@@ -555,8 +567,8 @@ static void INET6_displayroutes(void)
        FILE *fp = xfopen_for_read("/proc/net/ipv6_route");
 
        printf("Kernel IPv6 routing table\n%-44s%-40s"
-                         "Flags Metric Ref    Use Iface\n",
-                         "Destination", "Next Hop");
+                       "Flags Metric Ref    Use Iface\n",
+                       "Destination", "Next Hop");
 
        while (1) {
                int r;
@@ -601,13 +613,13 @@ static void INET6_displayroutes(void)
                set_flags(flags, (iflags & IPV6_MASK));
 
                r = 0;
-               do {
+               while (1) {
                        inet_pton(AF_INET6, addr6x + r,
                                          (struct sockaddr *) &snaddr6.sin6_addr);
                        snaddr6.sin6_family = AF_INET6;
                        naddr6 = INET6_rresolve((struct sockaddr_in6 *) &snaddr6,
-                                                  0x0fff /* Apparently, upstream never resolves. */
-                                                  );
+                                               0x0fff /* Apparently, upstream never resolves. */
+                                               );
 
                        if (!r) {                       /* 1st pass */
                                snprintf(addr6, sizeof(addr6), "%s/%d", naddr6, prefix_len);
@@ -620,8 +632,9 @@ static void INET6_displayroutes(void)
                                free(naddr6);
                                break;
                        }
-               } while (1);
+               }
        }
+       fclose(fp);
 }
 
 #endif
index 12a3067..a500da6 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Author: Ignacio Garcia Perez (iggarpe at gmail dot com)
  *
- * License: GPLv2 or later, see LICENSE file in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * There are some differences from the standard net-tools slattach:
  *
  * - The -F options allows disabling of RTS/CTS flow control.
  */
 
+//usage:#define slattach_trivial_usage
+//usage:       "[-cehmLF] [-s SPEED] [-p PROTOCOL] DEVICE"
+//usage:#define slattach_full_usage "\n\n"
+//usage:       "Attach network interface(s) to serial line(s)\n"
+//usage:     "\n       -p PROT Set protocol (slip, cslip, slip6, clisp6 or adaptive)"
+//usage:     "\n       -s SPD  Set line speed"
+//usage:     "\n       -e      Exit after initializing device"
+//usage:     "\n       -h      Exit when the carrier is lost"
+//usage:     "\n       -c PROG Run PROG when the line is hung up"
+//usage:     "\n       -m      Do NOT initialize the line in raw 8 bits mode"
+//usage:     "\n       -L      Enable 3-wire operation"
+//usage:     "\n       -F      Disable RTS/CTS flow control"
+
 #include "libbb.h"
 #include "libiproute/utils.h" /* invarg() */
 
@@ -134,9 +147,9 @@ int slattach_main(int argc UNUSED_PARAM, char **argv)
        int i, encap, opt;
        struct termios state;
        const char *proto = "cslip";
-       const char *extcmd;                             /* Command to execute after hangup */
+       const char *extcmd;   /* Command to execute after hangup */
        const char *baud_str;
-       int baud_code = -1;                             /* Line baud rate (system code) */
+       int baud_code = -1;   /* Line baud rate (system code) */
 
        enum {
                OPT_p_proto  = 1 << 0,
index 6a5a850..533f7c0 100644 (file)
@@ -1,14 +1,33 @@
 /* vi: set sw=4 ts=4: */
 /*
- * tc.c                "tc" utility frontend.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
- *
- * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
  * Bernhard Reutner-Fischer adjusted for busybox
  */
 
+//usage:#define tc_trivial_usage
+/* //usage: "[OPTIONS] OBJECT CMD [dev STRING]" */
+//usage:       "OBJECT CMD [dev STRING]"
+//usage:#define tc_full_usage "\n\n"
+//usage:       "OBJECT: {qdisc|class|filter}\n"
+//usage:       "CMD: {add|del|change|replace|show}\n"
+//usage:       "\n"
+//usage:       "qdisc [ handle QHANDLE ] [ root |"IF_FEATURE_TC_INGRESS(" ingress |")" parent CLASSID ]\n"
+/* //usage: "[ estimator INTERVAL TIME_CONSTANT ]\n" */
+//usage:       "       [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"
+//usage:       "       QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }\n"
+//usage:       "qdisc show [ dev STRING ]"IF_FEATURE_TC_INGRESS(" [ingress]")"\n"
+//usage:       "class [ classid CLASSID ] [ root | parent CLASSID ]\n"
+//usage:       "       [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"
+//usage:       "class show [ dev STRING ] [ root | parent CLASSID ]\n"
+//usage:       "filter [ pref PRIO ] [ protocol PROTO ]\n"
+/* //usage: "\t[ estimator INTERVAL TIME_CONSTANT ]\n" */
+//usage:       "       [ root | classid CLASSID ] [ handle FILTERID ]\n"
+//usage:       "       [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n"
+//usage:       "filter show [ dev STRING ] [ root | parent CLASSID ]"
+
 #include "libbb.h"
 
 #include "libiproute/utils.h"
 
 struct globals {
        int filter_ifindex;
-       __u32 filter_qdisc;
-       __u32 filter_parent;
-       __u32 filter_prio;
-       __u32 filter_proto;
+       uint32_t filter_qdisc;
+       uint32_t filter_parent;
+       uint32_t filter_prio;
+       uint32_t filter_proto;
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
+struct BUG_G_too_big {
+       char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
+};
 #define filter_ifindex (G.filter_ifindex)
 #define filter_qdisc (G.filter_qdisc)
 #define filter_parent (G.filter_parent)
 #define filter_prio (G.filter_prio)
 #define filter_proto (G.filter_proto)
-
-void BUG_tc_globals_too_big(void);
-#define INIT_G() do { \
-       if (sizeof(G) > COMMON_BUFSIZE) \
-               BUG_tc_globals_too_big(); \
-} while (0)
+#define INIT_G() do { } while (0)
 
 /* Allocates a buffer containing the name of a class id.
  * The caller must free the returned memory.  */
@@ -77,8 +94,8 @@ static char* print_tc_classid(uint32_t cid)
 }
 
 /* Get a qdisc handle.  Return 0 on success, !0 otherwise.  */
-static int get_qdisc_handle(__u32 *h, const char *str) {
-       __u32 maj;
+static int get_qdisc_handle(uint32_t *h, const char *str) {
+       uint32_t maj;
        char *p;
 
        maj = TC_H_UNSPEC;
@@ -96,8 +113,8 @@ static int get_qdisc_handle(__u32 *h, const char *str) {
 }
 
 /* Get class ID.  Return 0 on success, !0 otherwise.  */
-static int get_tc_classid(__u32 *h, const char *str) {
-       __u32 maj, min;
+static int get_tc_classid(uint32_t *h, const char *str) {
+       uint32_t maj, min;
        char *p;
 
        maj = TC_H_ROOT;
@@ -374,7 +391,7 @@ static int print_class(const struct sockaddr_nl *who UNUSED_PARAM,
                printf("root ");
        else if (msg->tcm_parent) {
                classid = print_tc_classid(filter_qdisc ?
-                                                                  TC_H_MIN(msg->tcm_parent) : msg->tcm_parent);
+                               TC_H_MIN(msg->tcm_parent) : msg->tcm_parent);
                printf("parent %s ", classid);
                if (ENABLE_FEATURE_CLEAN_UP)
                        free(classid);
@@ -401,9 +418,6 @@ static int print_class(const struct sockaddr_nl *who UNUSED_PARAM,
 static int print_filter(const struct sockaddr_nl *who UNUSED_PARAM,
                                                struct nlmsghdr *hdr, void *arg UNUSED_PARAM)
 {
-       struct tcmsg *msg = NLMSG_DATA(hdr);
-       int len = hdr->nlmsg_len;
-       struct rtattr * tb[TCA_MAX+1];
        return 0;
 }
 
@@ -496,7 +510,7 @@ int tc_main(int argc UNUSED_PARAM, char **argv)
                        if (obj == OBJ_filter)
                                filter_parent = TC_H_ROOT;
                } else if (arg == ARG_parent) {
-                       __u32 handle;
+                       uint32_t handle;
                        if (msg.tcm_parent)
                                duparg(*argv, "parent");
                        if (get_tc_classid(&handle, *argv))
@@ -509,11 +523,12 @@ int tc_main(int argc UNUSED_PARAM, char **argv)
                                duparg(*argv, "handle");
                        /* reject LONG_MIN || LONG_MAX */
                        /* TODO: for fw
-                          if ((slash = strchr(handle, '/')) != NULL)
+                       slash = strchr(handle, '/');
+                       if (slash != NULL)
                                   *slash = '\0';
                         */
                        msg.tcm_handle = get_u32(*argv, "handle");
-                       /* if (slash) {if (get_u32(__u32 &mask, slash+1, NULL)) inv mask; addattr32(n, MAX_MSG, TCA_FW_MASK, mask); */
+                       /* if (slash) {if (get_u32(uint32_t &mask, slash+1, NULL)) inv mask; addattr32(n, MAX_MSG, TCA_FW_MASK, mask); */
                } else if (arg == ARG_classid && obj == OBJ_class && cmd == CMD_change){
                } else if (arg == ARG_pref || arg == ARG_prio) { /* filter::list */
                        if (filter_prio)
index 53e622b..3df6a98 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2007 Denys Vlasenko.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /* Based on ipsvd-0.12.1. This tcpsvd accepts all options
  * - don't know how to retrieve ORIGDST for udp.
  */
 
+//usage:#define tcpsvd_trivial_usage
+//usage:       "[-hEv] [-c N] [-C N[:MSG]] [-b N] [-u USER] [-l NAME] IP PORT PROG"
+/* with not-implemented options: */
+/* //usage:    "[-hpEvv] [-c N] [-C N[:MSG]] [-b N] [-u USER] [-l NAME] [-i DIR|-x CDB] [-t SEC] IP PORT PROG" */
+//usage:#define tcpsvd_full_usage "\n\n"
+//usage:       "Create TCP socket, bind to IP:PORT and listen\n"
+//usage:       "for incoming connection. Run PROG for each connection.\n"
+//usage:     "\n       IP              IP to listen on, 0 = all"
+//usage:     "\n       PORT            Port to listen on"
+//usage:     "\n       PROG ARGS       Program to run"
+//usage:     "\n       -l NAME         Local hostname (else looks up local hostname in DNS)"
+//usage:     "\n       -u USER[:GRP]   Change to user/group after bind"
+//usage:     "\n       -c N            Handle up to N connections simultaneously"
+//usage:     "\n       -b N            Allow a backlog of approximately N TCP SYNs"
+//usage:     "\n       -C N[:MSG]      Allow only up to N connections from the same IP"
+//usage:     "\n                       New connections from this IP address are closed"
+//usage:     "\n                       immediately. MSG is written to the peer before close"
+//usage:     "\n       -h              Look up peer's hostname"
+//usage:     "\n       -E              Don't set up environment variables"
+//usage:     "\n       -v              Verbose"
+//usage:
+//usage:#define udpsvd_trivial_usage
+//usage:       "[-hEv] [-c N] [-u USER] [-l NAME] IP PORT PROG"
+//usage:#define udpsvd_full_usage "\n\n"
+//usage:       "Create UDP socket, bind to IP:PORT and wait\n"
+//usage:       "for incoming packets. Run PROG for each packet,\n"
+//usage:       "redirecting all further packets with same peer ip:port to it.\n"
+//usage:     "\n       IP              IP to listen on, 0 = all"
+//usage:     "\n       PORT            Port to listen on"
+//usage:     "\n       PROG ARGS       Program to run"
+//usage:     "\n       -l NAME         Local hostname (else looks up local hostname in DNS)"
+//usage:     "\n       -u USER[:GRP]   Change to user/group after bind"
+//usage:     "\n       -c N            Handle up to N connections simultaneously"
+//usage:     "\n       -h              Look up peer's hostname"
+//usage:     "\n       -E              Don't set up environment variables"
+//usage:     "\n       -v              Verbose"
+
 #include "libbb.h"
+
 /* Wants <limits.h> etc, thus included after libbb.h: */
+#ifdef __linux__
 #include <linux/types.h> /* for __be32 etc */
 #include <linux/netfilter_ipv4.h>
+#endif
 
 // TODO: move into this file:
 #include "tcpudp_perhost.h"
@@ -384,7 +424,7 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
                 * already bound in parent! This seems to work in Linux.
                 * (otherwise we can move socket to fd #0 only if bind succeeds) */
                close(0);
-               set_nport(localp, htons(local_port));
+               set_nport(&localp->u.sa, htons(local_port));
                xmove_fd(xsocket(localp->u.sa.sa_family, SOCK_DGRAM, 0), 0);
                setsockopt_reuseaddr(0); /* crucial */
                xbind(0, &localp->u.sa, localp->len);
@@ -464,6 +504,7 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
                        /* setup ucspi env */
                        const char *proto = tcp ? "TCP" : "UDP";
 
+#ifdef SO_ORIGINAL_DST
                        /* Extract "original" destination addr:port
                         * from Linux firewall. Useful when you redirect
                         * an outbond connection to local handler, and it needs
@@ -473,6 +514,7 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
                                xsetenv_plain("TCPORIGDSTADDR", addr);
                                free(addr);
                        }
+#endif
                        xsetenv_plain("PROTO", proto);
                        xsetenv_proto(proto, "LOCALADDR", local_addr);
                        xsetenv_proto(proto, "REMOTEADDR", remote_addr);
index 3005f12..1054108 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2007 Denys Vlasenko.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index d370036..3e57576 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2007 Denys Vlasenko.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
index 57997f6..a255797 100644 (file)
@@ -8,7 +8,7 @@
  * Created: Thu Apr  7 13:29:41 1994 too
  * Last modified: Fri Jun  9 14:34:24 2000 too
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * HISTORY
  * Revision 3.1  1994/04/17  11:31:54  too
  *
  */
 
+//usage:#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+//usage:#define telnet_trivial_usage
+//usage:       "[-a] [-l USER] HOST [PORT]"
+//usage:#define telnet_full_usage "\n\n"
+//usage:       "Connect to telnet server\n"
+//usage:     "\n       -a      Automatic login with $USER variable"
+//usage:     "\n       -l USER Automatic login as USER"
+//usage:
+//usage:#else
+//usage:#define telnet_trivial_usage
+//usage:       "HOST [PORT]"
+//usage:#define telnet_full_usage "\n\n"
+//usage:       "Connect to telnet server"
+//usage:#endif
+
 #include <arpa/telnet.h>
 #include <netinet/in.h>
 #include "libbb.h"
 
+#ifdef __BIONIC__
+/* should be in arpa/telnet.h */
+# define IAC         255  /* interpret as command: */
+# define DONT        254  /* you are not to use option */
+# define DO          253  /* please, you use option */
+# define WONT        252  /* I won't use option */
+# define WILL        251  /* I will use option */
+# define SB          250  /* interpret as subnegotiation */
+# define SE          240  /* end sub negotiation */
+# define TELOPT_ECHO   1  /* echo */
+# define TELOPT_SGA    3  /* suppress go ahead */
+# define TELOPT_TTYPE 24  /* terminal type */
+# define TELOPT_NAWS  31  /* window size */
+#endif
+
 #ifdef DOTRACE
-#define TRACE(x, y) do { if (x) printf y; } while (0)
+# define TRACE(x, y) do { if (x) printf y; } while (0)
 #else
-#define TRACE(x, y)
+# define TRACE(x, y)
 #endif
 
 enum {
@@ -42,11 +72,13 @@ enum {
        UF_ECHO = 0x01,
        UF_SGA = 0x02,
 
-       TS_0 = 1,
+       TS_NORMAL = 0,
+       TS_COPY = 1,
        TS_IAC = 2,
        TS_OPT = 3,
        TS_SUB1 = 4,
        TS_SUB2 = 5,
+       TS_CR = 6,
 };
 
 typedef unsigned char byte;
@@ -83,22 +115,20 @@ struct globals {
        }; \
 } while (0)
 
-/* Function prototypes */
+
 static void rawmode(void);
 static void cookmode(void);
 static void do_linemode(void);
 static void will_charmode(void);
 static void telopt(byte c);
-static int subneg(byte c);
+static void subneg(byte c);
 
 static void iac_flush(void)
 {
-       write(netfd, G.iacbuf, G.iaclen);
+       full_write(netfd, G.iacbuf, G.iaclen);
        G.iaclen = 0;
 }
 
-#define write_str(fd, str) write(fd, str, sizeof(str) - 1)
-
 static void doexit(int ev) NORETURN;
 static void doexit(int ev)
 {
@@ -113,7 +143,7 @@ static void con_escape(void)
        if (bb_got_signal) /* came from line mode... go raw */
                rawmode();
 
-       write_str(1, "\r\nConsole escape. Commands are:\r\n\n"
+       full_write1_str("\r\nConsole escape. Commands are:\r\n\n"
                        " l     go to line mode\r\n"
                        " c     go to character mode\r\n"
                        " z     suspend telnet\r\n"
@@ -144,51 +174,45 @@ static void con_escape(void)
                doexit(EXIT_SUCCESS);
        }
 
-       write_str(1, "continuing...\r\n");
+       full_write1_str("continuing...\r\n");
 
        if (bb_got_signal)
                cookmode();
  ret:
        bb_got_signal = 0;
-
 }
 
 static void handle_net_output(int len)
 {
-       /* here we could do smart tricks how to handle 0xFF:s in output
-        * stream like writing twice every sequence of FF:s (thus doing
-        * many write()s. But I think interactive telnet application does
-        * not need to be 100% 8-bit clean, so changing every 0xff:s to
-        * 0x7f:s
-        *
-        * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com)
-        * I don't agree.
-        * first - I cannot use programs like sz/rz
-        * second - the 0x0D is sent as one character and if the next
-        *      char is 0x0A then it's eaten by a server side.
-        * third - why do you have to make 'many write()s'?
-        *      I don't understand.
-        * So I implemented it. It's really useful for me. I hope that
-        * other people will find it interesting too.
-        */
-
-       int i, j;
-       byte *p = (byte*)G.buf;
-       byte outbuf[4*DATABUFSIZE];
-
-       for (i = len, j = 0; i > 0; i--, p++) {
-               if (*p == 0x1d) {
+       byte outbuf[2 * DATABUFSIZE];
+       byte *dst = outbuf;
+       byte *src = (byte*)G.buf;
+       byte *end = src + len;
+
+       while (src < end) {
+               byte c = *src++;
+               if (c == 0x1d) {
                        con_escape();
                        return;
                }
-               outbuf[j++] = *p;
-               if (*p == 0xff)
-                       outbuf[j++] = 0xff;
-               else if (*p == 0x0d)
-                       outbuf[j++] = 0x00;
+               *dst = c;
+               if (c == IAC)
+                       *++dst = c; /* IAC -> IAC IAC */
+               else
+               if (c == '\r' || c == '\n') {
+                       /* Enter key sends '\r' in raw mode and '\n' in cooked one.
+                        *
+                        * See RFC 1123 3.3.1 Telnet End-of-Line Convention.
+                        * Using CR LF instead of other allowed possibilities
+                        * like CR NUL - easier to talk to HTTP/SMTP servers.
+                        */
+                       *dst = '\r'; /* Enter -> CR LF */
+                       *++dst = '\n';
+               }
+               dst++;
        }
-       if (j > 0)
-               write(netfd, outbuf, j);
+       if (dst - outbuf != 0)
+               full_write(netfd, outbuf, dst - outbuf);
 }
 
 static void handle_net_input(int len)
@@ -199,25 +223,44 @@ static void handle_net_input(int len)
        for (i = 0; i < len; i++) {
                byte c = G.buf[i];
 
-               if (G.telstate == 0) { /* most of the time state == 0 */
+               if (G.telstate == TS_NORMAL) { /* most typical state */
                        if (c == IAC) {
                                cstart = i;
                                G.telstate = TS_IAC;
                        }
+                       else if (c == '\r') {
+                               cstart = i + 1;
+                               G.telstate = TS_CR;
+                       }
+                       /* No IACs were seen so far, no need to copy
+                        * bytes within G.buf: */
                        continue;
                }
+
                switch (G.telstate) {
-               case TS_0:
+               case TS_CR:
+                       /* Prev char was CR. If cur one is NUL, ignore it.
+                        * See RFC 1123 section 3.3.1 for discussion of telnet EOL handling.
+                        */
+                       G.telstate = TS_COPY;
+                       if (c == '\0')
+                               break;
+                       /* else: fall through - need to handle CR IAC ... properly */
+
+               case TS_COPY: /* Prev char was ordinary */
+                       /* Similar to NORMAL, but in TS_COPY we need to copy bytes */
                        if (c == IAC)
                                G.telstate = TS_IAC;
                        else
                                G.buf[cstart++] = c;
+                       if (c == '\r')
+                               G.telstate = TS_CR;
                        break;
 
-               case TS_IAC:
-                       if (c == IAC) { /* IAC IAC -> 0xFF */
+               case TS_IAC: /* Prev char was IAC */
+                       if (c == IAC) { /* IAC IAC -> one IAC */
                                G.buf[cstart++] = c;
-                               G.telstate = TS_0;
+                               G.telstate = TS_COPY;
                                break;
                        }
                        /* else */
@@ -229,34 +272,38 @@ static void handle_net_input(int len)
                        case DONT:
                        case WILL:
                        case WONT:
-                               G.telwish =  c;
+                               G.telwish = c;
                                G.telstate = TS_OPT;
                                break;
+                       /* DATA MARK must be added later */
                        default:
-                               G.telstate = TS_0;      /* DATA MARK must be added later */
+                               G.telstate = TS_COPY;
                        }
                        break;
-               case TS_OPT: /* WILL, WONT, DO, DONT */
+
+               case TS_OPT: /* Prev chars were IAC WILL/WONT/DO/DONT */
                        telopt(c);
-                       G.telstate = TS_0;
+                       G.telstate = TS_COPY;
                        break;
+
                case TS_SUB1: /* Subnegotiation */
                case TS_SUB2: /* Subnegotiation */
-                       if (subneg(c))
-                               G.telstate = TS_0;
+                       subneg(c); /* can change G.telstate */
                        break;
                }
        }
-       if (G.telstate) {
+
+       if (G.telstate != TS_NORMAL) {
+               /* We had some IACs, or CR */
                if (G.iaclen)
                        iac_flush();
-               if (G.telstate == TS_0)
-                       G.telstate = 0;
+               if (G.telstate == TS_COPY) /* we aren't in the middle of IAC */
+                       G.telstate = TS_NORMAL;
                len = cstart;
        }
 
        if (len)
-               write(STDOUT_FILENO, G.buf, len);
+               full_write(STDOUT_FILENO, G.buf, len);
 }
 
 static void put_iac(int c)
@@ -334,30 +381,31 @@ static void put_iac_naws(byte c, int x, int y)
        put_iac(SB);
        put_iac(c);
 
-       put_iac((x >> 8) & 0xff);
-       put_iac(x & 0xff);
-       put_iac((y >> 8) & 0xff);
-       put_iac(y & 0xff);
+       /* "... & 0xff" implicitly done below */
+       put_iac(x >> 8);
+       put_iac(x);
+       put_iac(y >> 8);
+       put_iac(y);
 
        put_iac(IAC);
        put_iac(SE);
 }
 #endif
 
-static char const escapecharis[] ALIGN1 = "\r\nEscape character is ";
-
 static void setConMode(void)
 {
        if (G.telflags & UF_ECHO) {
                if (G.charmode == CHM_TRY) {
                        G.charmode = CHM_ON;
-                       printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis);
+                       printf("\r\nEntering %s mode"
+                               "\r\nEscape character is '^%c'.\r\n", "character", ']');
                        rawmode();
                }
        } else {
                if (G.charmode != CHM_OFF) {
                        G.charmode = CHM_OFF;
-                       printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis);
+                       printf("\r\nEntering %s mode"
+                               "\r\nEscape character is '^%c'.\r\n", "line", 'C');
                        cookmode();
                }
        }
@@ -496,7 +544,7 @@ static void telopt(byte c)
 }
 
 /* subnegotiation -- ignore all (except TTYPE,NAWS) */
-static int subneg(byte c)
+static void subneg(byte c)
 {
        switch (G.telstate) {
        case TS_SUB1:
@@ -514,12 +562,13 @@ static int subneg(byte c)
 #endif
                break;
        case TS_SUB2:
-               if (c == SE)
-                       return TRUE;
+               if (c == SE) {
+                       G.telstate = TS_COPY;
+                       return;
+               }
                G.telstate = TS_SUB1;
-               /* break; */
+               break;
        }
-       return FALSE;
 }
 
 static void rawmode(void)
@@ -534,21 +583,13 @@ static void cookmode(void)
                tcsetattr(0, TCSADRAIN, &G.termios_def);
 }
 
-/* poll gives smaller (-70 bytes) code */
-#define USE_POLL 1
-
 int telnet_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int telnet_main(int argc UNUSED_PARAM, char **argv)
 {
        char *host;
        int port;
        int len;
-#ifdef USE_POLL
        struct pollfd ufds[2];
-#else
-       fd_set readfds;
-       int maxfd;
-#endif
 
        INIT_G();
 
@@ -586,63 +627,39 @@ int telnet_main(int argc UNUSED_PARAM, char **argv)
 
        signal(SIGINT, record_signo);
 
-#ifdef USE_POLL
-       ufds[0].fd = 0; ufds[1].fd = netfd;
-       ufds[0].events = ufds[1].events = POLLIN;
-#else
-       FD_ZERO(&readfds);
-       FD_SET(STDIN_FILENO, &readfds);
-       FD_SET(netfd, &readfds);
-       maxfd = netfd + 1;
-#endif
+       ufds[0].fd = STDIN_FILENO;
+       ufds[0].events = POLLIN;
+       ufds[1].fd = netfd;
+       ufds[1].events = POLLIN;
 
        while (1) {
-#ifndef USE_POLL
-               fd_set rfds = readfds;
-
-               switch (select(maxfd, &rfds, NULL, NULL, NULL))
-#else
-               switch (poll(ufds, 2, -1))
-#endif
-               {
-               case 0:
-                       /* timeout */
-               case -1:
+               if (poll(ufds, 2, -1) < 0) {
                        /* error, ignore and/or log something, bay go to loop */
                        if (bb_got_signal)
                                con_escape();
                        else
                                sleep(1);
-                       break;
-               default:
+                       continue;
+               }
 
-#ifdef USE_POLL
-                       if (ufds[0].revents)
-#else
-                       if (FD_ISSET(STDIN_FILENO, &rfds))
-#endif
-                       {
-                               len = safe_read(STDIN_FILENO, G.buf, DATABUFSIZE);
-                               if (len <= 0)
-                                       doexit(EXIT_SUCCESS);
-                               TRACE(0, ("Read con: %d\n", len));
-                               handle_net_output(len);
-                       }
+// FIXME: reads can block. Need full bidirectional buffering.
 
-#ifdef USE_POLL
-                       if (ufds[1].revents)
-#else
-                       if (FD_ISSET(netfd, &rfds))
-#endif
-                       {
-                               len = safe_read(netfd, G.buf, DATABUFSIZE);
-                               if (len <= 0) {
-                                       full_write1_str("Connection closed by foreign host\r\n");
-                                       doexit(EXIT_FAILURE);
-                               }
-                               TRACE(0, ("Read netfd (%d): %d\n", netfd, len));
-                               handle_net_input(len);
+               if (ufds[0].revents) {
+                       len = safe_read(STDIN_FILENO, G.buf, DATABUFSIZE);
+                       if (len <= 0)
+                               doexit(EXIT_SUCCESS);
+                       TRACE(0, ("Read con: %d\n", len));
+                       handle_net_output(len);
+               }
+
+               if (ufds[1].revents) {
+                       len = safe_read(netfd, G.buf, DATABUFSIZE);
+                       if (len <= 0) {
+                               full_write1_str("Connection closed by foreign host\r\n");
+                               doexit(EXIT_FAILURE);
                        }
+                       TRACE(0, ("Read netfd (%d): %d\n", netfd, len));
+                       handle_net_input(len);
                }
        } /* while (1) */
 }
index ea66a25..9e7a84c 100644 (file)
@@ -3,7 +3,7 @@
  * Simple telnet server
  * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * ---------------------------------------------------------------------------
  * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
  * Vladimir Oleynik <dzo@simtreas.ru> 2001
  * Set process group corrections, initial busybox port
  */
+
+//usage:#define telnetd_trivial_usage
+//usage:       "[OPTIONS]"
+//usage:#define telnetd_full_usage "\n\n"
+//usage:       "Handle incoming telnet connections"
+//usage:       IF_NOT_FEATURE_TELNETD_STANDALONE(" via inetd") "\n"
+//usage:     "\n       -l LOGIN        Exec LOGIN on connect"
+//usage:     "\n       -f ISSUE_FILE   Display ISSUE_FILE instead of /etc/issue"
+//usage:     "\n       -K              Close connection as soon as login exits"
+//usage:     "\n                       (normally wait until all programs close slave pty)"
+//usage:       IF_FEATURE_TELNETD_STANDALONE(
+//usage:     "\n       -p PORT         Port to listen on"
+//usage:     "\n       -b ADDR[:PORT]  Address to bind to"
+//usage:     "\n       -F              Run in foreground"
+//usage:     "\n       -i              Inetd mode"
+//usage:       IF_FEATURE_TELNETD_INETD_WAIT(
+//usage:     "\n       -w SEC          Inetd 'wait' mode, linger time SEC"
+//usage:     "\n       -S              Log to syslog (implied by -i or without -F and -w)"
+//usage:       )
+//usage:       )
+
 #define DEBUG 0
 
 #include "libbb.h"
 #endif
 #include <arpa/telnet.h>
 
-#if ENABLE_FEATURE_UTMP
-# include <utmp.h> /* LOGIN_PROCESS */
-#endif
-
 
 struct tsession {
        struct tsession *next;
@@ -108,6 +125,7 @@ remove_iacs(struct tsession *ts, int *pnum_totty)
                        /* We map \r\n ==> \r for pragmatic reasons.
                         * Many client implementations send \r\n when
                         * the user hits the CarriageReturn key.
+                        * See RFC 1123 3.3.1 Telnet End-of-Line Convention.
                         */
                        if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0'))
                                ptr++;
@@ -141,7 +159,7 @@ remove_iacs(struct tsession *ts, int *pnum_totty)
                if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
                        struct winsize ws;
                        if ((ptr+8) >= end)
-                               break;  /* incomplete, can't process */
+                               break;  /* incomplete, can't process */
                        ws.ws_col = (ptr[3] << 8) | ptr[4];
                        ws.ws_row = (ptr[5] << 8) | ptr[6];
                        ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
@@ -273,8 +291,8 @@ make_new_session(
                static const char iacs_to_send[] ALIGN1 = {
                        IAC, DO, TELOPT_ECHO,
                        IAC, DO, TELOPT_NAWS,
-               /* This requires telnetd.ctrlSQ.patch (incomplete) */
-               /*      IAC, DO, TELOPT_LFLOW, */
+                       /* This requires telnetd.ctrlSQ.patch (incomplete) */
+                       /*IAC, DO, TELOPT_LFLOW,*/
                        IAC, WILL, TELOPT_ECHO,
                        IAC, WILL, TELOPT_SGA
                };
@@ -314,6 +332,8 @@ make_new_session(
        /* Restore default signal handling ASAP */
        bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
 
+       pid = getpid();
+
        if (ENABLE_FEATURE_UTMP) {
                len_and_sockaddr *lsa = get_peer_lsa(sock);
                char *hostname = NULL;
@@ -335,7 +355,6 @@ make_new_session(
        xopen(tty_name, O_RDWR); /* becomes our ctty */
        xdup2(0, 1);
        xdup2(0, 2);
-       pid = getpid();
        tcsetpgrp(0, pid); /* switch this tty's process group to us */
 
        /* The pseudo-terminal allocated to the client is configured to operate
index 43ae136..630fdaf 100644 (file)
  *
  * tftpd added by Denys Vlasenko & Vladimir Dronnikov
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define tftp_trivial_usage
+//usage:       "[OPTIONS] HOST [PORT]"
+//usage:#define tftp_full_usage "\n\n"
+//usage:       "Transfer a file from/to tftp server\n"
+//usage:     "\n       -l FILE Local FILE"
+//usage:     "\n       -r FILE Remote FILE"
+//usage:       IF_FEATURE_TFTP_GET(
+//usage:     "\n       -g      Get file"
+//usage:       )
+//usage:       IF_FEATURE_TFTP_PUT(
+//usage:     "\n       -p      Put file"
+//usage:       )
+//usage:       IF_FEATURE_TFTP_BLOCKSIZE(
+//usage:     "\n       -b SIZE Transfer blocks of SIZE octets"
+//usage:       )
+//usage:
+//usage:#define tftpd_trivial_usage
+//usage:       "[-cr] [-u USER] [DIR]"
+//usage:#define tftpd_full_usage "\n\n"
+//usage:       "Transfer a file on tftp client's request\n"
+//usage:       "\n"
+//usage:       "tftpd should be used as an inetd service.\n"
+//usage:       "tftpd's line for inetd.conf:\n"
+//usage:       "       69 dgram udp nowait root tftpd tftpd -l /files/to/serve\n"
+//usage:       "It also can be ran from udpsvd:\n"
+//usage:       "       udpsvd -vE 0.0.0.0 69 tftpd /files/to/serve\n"
+//usage:     "\n       -r      Prohibit upload"
+//usage:     "\n       -c      Allow file creation via upload"
+//usage:     "\n       -u      Access files as USER"
+//usage:     "\n       -l      Log to syslog (inetd mode requires this)"
+
 #include "libbb.h"
+#include <syslog.h>
 
 #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
 
@@ -59,6 +92,7 @@ enum {
        TFTPD_OPT_r = (1 << 8) * ENABLE_TFTPD,
        TFTPD_OPT_c = (1 << 9) * ENABLE_TFTPD,
        TFTPD_OPT_u = (1 << 10) * ENABLE_TFTPD,
+       TFTPD_OPT_l = (1 << 11) * ENABLE_TFTPD,
 };
 
 #if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
@@ -82,7 +116,7 @@ enum {
 struct globals {
        /* u16 TFTP_ERROR; u16 reason; both network-endian, then error text: */
        uint8_t error_pkt[4 + 32];
-       char *user_opt;
+       struct passwd *pw;
        /* used in tftpd_main(), a bit big for stack: */
        char block_buf[TFTP_BLKSIZE_DEFAULT];
 #if ENABLE_FEATURE_TFTP_PROGRESS_BAR
@@ -96,48 +130,28 @@ struct globals {
 struct BUG_G_too_big {
        char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
 };
-#define block_buf        (G.block_buf   )
-#define user_opt         (G.user_opt    )
-#define error_pkt        (G.error_pkt   )
 #define INIT_G() do { } while (0)
 
-#define error_pkt_reason (error_pkt[3])
-#define error_pkt_str    (error_pkt + 4)
+#define G_error_pkt_reason (G.error_pkt[3])
+#define G_error_pkt_str    ((char*)(G.error_pkt + 4))
 
 #if ENABLE_FEATURE_TFTP_PROGRESS_BAR
-/* SIGALRM logic nicked from the wget applet */
-static void progress_meter(int flag)
+static void tftp_progress_update(void)
 {
-       /* We can be called from signal handler */
-       int save_errno = errno;
-
-       if (flag == -1) { /* first call to progress_meter */
-               bb_progress_init(&G.pmt);
-       }
-
-       bb_progress_update(&G.pmt, G.file, 0, G.pos, G.size);
-
-       if (flag == 0) {
-               /* last call to progress_meter */
-               alarm(0);
-               bb_putchar_stderr('\n');
-       } else {
-               if (flag == -1) { /* first call to progress_meter */
-                       signal_SA_RESTART_empty_mask(SIGALRM, progress_meter);
-               }
-               alarm(1);
-       }
-
-       errno = save_errno;
+       bb_progress_update(&G.pmt, 0, G.pos, G.size);
 }
 static void tftp_progress_init(void)
 {
-       progress_meter(-1);
+       bb_progress_init(&G.pmt, G.file);
+       tftp_progress_update();
 }
 static void tftp_progress_done(void)
 {
-       if (G.pmt.inited)
-               progress_meter(0);
+       if (is_bb_progress_inited(&G.pmt)) {
+               tftp_progress_update();
+               bb_putchar_stderr('\n');
+               bb_progress_free(&G.pmt);
+       }
 }
 #else
 # define tftp_progress_init() ((void)0)
@@ -255,12 +269,11 @@ static int tftp_protocol(
                xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len);
 
                /* Is there an error already? Send pkt and bail out */
-               if (error_pkt_reason || error_pkt_str[0])
+               if (G_error_pkt_reason || G_error_pkt_str[0])
                        goto send_err_pkt;
 
-               if (user_opt) {
-                       struct passwd *pw = xgetpwnam(user_opt);
-                       change_identity(pw); /* initgroups, setgid, setuid */
+               if (G.pw) {
+                       change_identity(G.pw); /* initgroups, setgid, setuid */
                }
        }
 
@@ -312,8 +325,8 @@ static int tftp_protocol(
                /* Open file (must be after changing user) */
                local_fd = open(local_file, open_mode, 0666);
                if (local_fd < 0) {
-                       error_pkt_reason = ERR_NOFILE;
-                       strcpy((char*)error_pkt_str, "can't open file");
+                       G_error_pkt_reason = ERR_NOFILE;
+                       strcpy(G_error_pkt_str, "can't open file");
                        goto send_err_pkt;
                }
 /* gcc 4.3.1 would NOT optimize it out as it should! */
@@ -439,6 +452,7 @@ static int tftp_protocol(
                                finished = 1;
                        }
                        cp += len;
+                       IF_FEATURE_TFTP_PROGRESS_BAR(G.pos += len;)
                }
  send_pkt:
                /* Send packet */
@@ -447,7 +461,7 @@ static int tftp_protocol(
                /* NB: send_len value is preserved in code below
                 * for potential resend */
 
-               retries = TFTP_NUM_RETRIES;     /* re-initialize */
+               retries = TFTP_NUM_RETRIES;  /* re-initialize */
                waittime_ms = TFTP_TIMEOUT_MS;
 
  send_again:
@@ -460,9 +474,8 @@ static int tftp_protocol(
                xsendto(socket_fd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len);
 
 #if ENABLE_FEATURE_TFTP_PROGRESS_BAR
-               if (ENABLE_TFTP && remote_file) { /* tftp */
-                       G.pos = (block_nr - 1) * (uoff_t)blksize;
-               }
+               if (is_bb_progress_inited(&G.pmt))
+                       tftp_progress_update();
 #endif
                /* Was it final ACK? then exit */
                if (finished && (opcode == TFTP_ACK))
@@ -479,6 +492,7 @@ static int tftp_protocol(
                case 0:
                        retries--;
                        if (retries == 0) {
+                               tftp_progress_done();
                                bb_error_msg("timeout");
                                goto ret; /* no err packet sent */
                        }
@@ -557,7 +571,7 @@ static int tftp_protocol(
                                if (res) {
                                        blksize = tftp_blksize_check(res, blksize);
                                        if (blksize < 0) {
-                                               error_pkt_reason = ERR_BAD_OPT;
+                                               G_error_pkt_reason = ERR_BAD_OPT;
                                                goto send_err_pkt;
                                        }
                                        io_bufsize = blksize + 4;
@@ -596,13 +610,14 @@ static int tftp_protocol(
                        if (recv_blk == block_nr) {
                                int sz = full_write(local_fd, &rbuf[4], len - 4);
                                if (sz != len - 4) {
-                                       strcpy((char*)error_pkt_str, bb_msg_write_error);
-                                       error_pkt_reason = ERR_WRITE;
+                                       strcpy(G_error_pkt_str, bb_msg_write_error);
+                                       G_error_pkt_reason = ERR_WRITE;
                                        goto send_err_pkt;
                                }
                                if (sz != blksize) {
                                        finished = 1;
                                }
+                               IF_FEATURE_TFTP_PROGRESS_BAR(G.pos += sz;)
                                continue; /* send ACK */
                        }
 /* Disabled to cope with servers with Sorcerer's Apprentice Syndrome */
@@ -645,12 +660,12 @@ static int tftp_protocol(
        return finished == 0; /* returns 1 on failure */
 
  send_read_err_pkt:
-       strcpy((char*)error_pkt_str, bb_msg_read_error);
+       strcpy(G_error_pkt_str, bb_msg_read_error);
  send_err_pkt:
-       if (error_pkt_str[0])
-               bb_error_msg("%s", (char*)error_pkt_str);
-       error_pkt[1] = TFTP_ERROR;
-       xsendto(socket_fd, error_pkt, 4 + 1 + strlen((char*)error_pkt_str),
+       if (G_error_pkt_str[0])
+               bb_error_msg("%s", G_error_pkt_str);
+       G.error_pkt[1] = TFTP_ERROR;
+       xsendto(socket_fd, G.error_pkt, 4 + 1 + strlen(G_error_pkt_str),
                        &peer_lsa->u.sa, peer_lsa->len);
        return EXIT_FAILURE;
 #undef remote_file
@@ -742,7 +757,7 @@ int tftpd_main(int argc UNUSED_PARAM, char **argv)
 {
        len_and_sockaddr *our_lsa;
        len_and_sockaddr *peer_lsa;
-       char *local_file, *mode;
+       char *local_file, *mode, *user_opt;
        const char *error_msg;
        int opt, result, opcode;
        IF_FEATURE_TFTP_BLOCKSIZE(int blksize = TFTP_BLKSIZE_DEFAULT;)
@@ -764,19 +779,28 @@ int tftpd_main(int argc UNUSED_PARAM, char **argv)
        peer_lsa->len = our_lsa->len;
 
        /* Shifting to not collide with TFTP_OPTs */
-       opt = option_mask32 = TFTPD_OPT | (getopt32(argv, "rcu:", &user_opt) << 8);
+       opt = option_mask32 = TFTPD_OPT | (getopt32(argv, "rcu:l", &user_opt) << 8);
        argv += optind;
-       if (argv[0])
-               xchdir(argv[0]);
+       if (opt & TFTPD_OPT_l) {
+               openlog(applet_name, LOG_PID, LOG_DAEMON);
+               logmode = LOGMODE_SYSLOG;
+       }
+       if (opt & TFTPD_OPT_u) {
+               /* Must be before xchroot */
+               G.pw = xgetpwnam(user_opt);
+       }
+       if (argv[0]) {
+               xchroot(argv[0]);
+       }
 
-       result = recv_from_to(STDIN_FILENO, block_buf, sizeof(block_buf),
+       result = recv_from_to(STDIN_FILENO, G.block_buf, sizeof(G.block_buf),
                        0 /* flags */,
                        &peer_lsa->u.sa, &our_lsa->u.sa, our_lsa->len);
 
        error_msg = "malformed packet";
-       opcode = ntohs(*(uint16_t*)block_buf);
-       if (result < 4 || result >= sizeof(block_buf)
-        || block_buf[result-1] != '\0'
+       opcode = ntohs(*(uint16_t*)G.block_buf);
+       if (result < 4 || result >= sizeof(G.block_buf)
+        || G.block_buf[result-1] != '\0'
         || (IF_FEATURE_TFTP_PUT(opcode != TFTP_RRQ) /* not download */
             IF_GETPUT(&&)
             IF_FEATURE_TFTP_GET(opcode != TFTP_WRQ) /* not upload */
@@ -784,26 +808,27 @@ int tftpd_main(int argc UNUSED_PARAM, char **argv)
        ) {
                goto err;
        }
-       local_file = block_buf + 2;
+       local_file = G.block_buf + 2;
        if (local_file[0] == '.' || strstr(local_file, "/.")) {
                error_msg = "dot in file name";
                goto err;
        }
        mode = local_file + strlen(local_file) + 1;
-       if (mode >= block_buf + result || strcmp(mode, "octet") != 0) {
+       /* RFC 1350 says mode string is case independent */
+       if (mode >= G.block_buf + result || strcasecmp(mode, "octet") != 0) {
                goto err;
        }
 # if ENABLE_FEATURE_TFTP_BLOCKSIZE
        {
                char *res;
                char *opt_str = mode + sizeof("octet");
-               int opt_len = block_buf + result - opt_str;
+               int opt_len = G.block_buf + result - opt_str;
                if (opt_len > 0) {
                        res = tftp_get_option("blksize", opt_str, opt_len);
                        if (res) {
                                blksize = tftp_blksize_check(res, 65564);
                                if (blksize < 0) {
-                                       error_pkt_reason = ERR_BAD_OPT;
+                                       G_error_pkt_reason = ERR_BAD_OPT;
                                        /* will just send error pkt */
                                        goto do_proto;
                                }
@@ -821,7 +846,7 @@ int tftpd_main(int argc UNUSED_PARAM, char **argv)
        if (!ENABLE_FEATURE_TFTP_PUT || opcode == TFTP_WRQ) {
                if (opt & TFTPD_OPT_r) {
                        /* This would mean "disk full" - not true */
-                       /*error_pkt_reason = ERR_WRITE;*/
+                       /*G_error_pkt_reason = ERR_WRITE;*/
                        error_msg = bb_msg_write_error;
                        goto err;
                }
@@ -830,7 +855,7 @@ int tftpd_main(int argc UNUSED_PARAM, char **argv)
                IF_GETPUT(option_mask32 |= TFTP_OPT_PUT;) /* will send file's data */
        }
 
-       /* NB: if error_pkt_str or error_pkt_reason is set up,
+       /* NB: if G_error_pkt_str or G_error_pkt_reason is set up,
         * tftp_protocol() just sends one error pkt and returns */
 
  do_proto:
@@ -845,7 +870,7 @@ int tftpd_main(int argc UNUSED_PARAM, char **argv)
 
        return result;
  err:
-       strcpy((char*)error_pkt_str, error_msg);
+       strcpy(G_error_pkt_str, error_msg);
        goto do_proto;
 }
 
index 2d3e770..97a7a19 100644 (file)
  *     Tue Dec 20 03:50:13 PST 1988
  */
 
+//usage:#define traceroute_trivial_usage
+//usage:       "[-"IF_TRACEROUTE6("46")"FIldnrv] [-f 1ST_TTL] [-m MAXTTL] [-p PORT] [-q PROBES]\n"
+//usage:       "       [-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-g GATEWAY] [-i IFACE]\n"
+//usage:       "       [-z PAUSE_MSEC] HOST [BYTES]"
+//usage:#define traceroute_full_usage "\n\n"
+//usage:       "Trace the route to HOST\n"
+//usage:       IF_TRACEROUTE6(
+//usage:     "\n       -4,-6   Force IP or IPv6 name resolution"
+//usage:       )
+//usage:     "\n       -F      Set the don't fragment bit"
+//usage:     "\n       -I      Use ICMP ECHO instead of UDP datagrams"
+//usage:     "\n       -l      Display the TTL value of the returned packet"
+//usage:     "\n       -d      Set SO_DEBUG options to socket"
+//usage:     "\n       -n      Print numeric addresses"
+//usage:     "\n       -r      Bypass routing tables, send directly to HOST"
+//usage:     "\n       -v      Verbose"
+//usage:     "\n       -m      Max time-to-live (max number of hops)"
+//usage:     "\n       -p      Base UDP port number used in probes"
+//usage:     "\n               (default 33434)"
+//usage:     "\n       -q      Number of probes per TTL (default 3)"
+//usage:     "\n       -s      IP address to use as the source address"
+//usage:     "\n       -t      Type-of-service in probe packets (default 0)"
+//usage:     "\n       -w      Time in seconds to wait for a response (default 3)"
+//usage:     "\n       -g      Loose source route gateway (8 max)"
+//usage:
+//usage:#define traceroute6_trivial_usage
+//usage:       "[-dnrv] [-m MAXTTL] [-p PORT] [-q PROBES]\n"
+//usage:       "       [-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-i IFACE]\n"
+//usage:       "       HOST [BYTES]"
+//usage:#define traceroute6_full_usage "\n\n"
+//usage:       "Trace the route to HOST\n"
+//usage:     "\n       -d      Set SO_DEBUG options to socket"
+//usage:     "\n       -n      Print numeric addresses"
+//usage:     "\n       -r      Bypass routing tables, send directly to HOST"
+//usage:     "\n       -v      Verbose"
+//usage:     "\n       -m      Max time-to-live (max number of hops)"
+//usage:     "\n       -p      Base UDP port number used in probes"
+//usage:     "\n               (default is 33434)"
+//usage:     "\n       -q      Number of probes per TTL (default 3)"
+//usage:     "\n       -s      IP address to use as the source address"
+//usage:     "\n       -t      Type-of-service in probe packets (default 0)"
+//usage:     "\n       -w      Time in seconds to wait for a response (default 3)"
+
 #define TRACEROUTE_SO_DEBUG 0
 
 /* TODO: undefs were uncommented - ??! we have config system for that! */
 #endif
 
 
-#define OPT_STRING "FIlnrdvxt:i:m:p:q:s:w:z:f:" \
-                   IF_FEATURE_TRACEROUTE_SOURCE_ROUTE("g:") \
-                   "4" IF_TRACEROUTE6("6")
+#define OPT_STRING \
+       "FIlnrdvxt:i:m:p:q:s:w:z:f:" \
+       IF_FEATURE_TRACEROUTE_SOURCE_ROUTE("g:") \
+       "4" IF_TRACEROUTE6("6")
 enum {
        OPT_DONT_FRAGMNT = (1 << 0),    /* F */
        OPT_USE_ICMP     = (1 << 1) * ENABLE_FEATURE_TRACEROUTE_USE_ICMP, /* I */
@@ -353,56 +397,28 @@ static len_and_sockaddr* dup_sockaddr(const len_and_sockaddr *lsa)
 
 
 static int
-wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to)
+wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to, unsigned *timestamp_us, int *left_ms)
 {
        struct pollfd pfd[1];
        int read_len = 0;
 
        pfd[0].fd = rcvsock;
        pfd[0].events = POLLIN;
-       if (safe_poll(pfd, 1, waittime * 1000) > 0) {
+       if (*left_ms >= 0 && safe_poll(pfd, 1, *left_ms) > 0) {
+               unsigned t;
+
                read_len = recv_from_to(rcvsock,
                                recv_pkt, sizeof(recv_pkt),
-                               /*flags:*/ 0,
+                               /*flags:*/ MSG_DONTWAIT,
                                &from_lsa->u.sa, to, from_lsa->len);
+               t = monotonic_us();
+               *left_ms -= (t - *timestamp_us) / 1000;
+               *timestamp_us = t;
        }
 
        return read_len;
 }
 
-/*
- * Checksum routine for Internet Protocol family headers (C Version)
- */
-static uint16_t
-in_cksum(uint16_t *addr, int len)
-{
-       int nleft = len;
-       uint16_t *w = addr;
-       uint16_t answer;
-       int sum = 0;
-
-       /*
-        * Our algorithm is simple, using a 32 bit accumulator (sum),
-        * we add sequential 16 bit words to it, and at the end, fold
-        * back all the carry bits from the top 16 bits into the lower
-        * 16 bits.
-        */
-       while (nleft > 1) {
-               sum += *w++;
-               nleft -= 2;
-       }
-
-       /* mop up an odd byte, if necessary */
-       if (nleft == 1)
-               sum += *(unsigned char *)w;
-
-       /* add back carry outs from top 16 bits to low 16 bits */
-       sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
-       sum += (sum >> 16);                     /* add carry */
-       answer = ~sum;                          /* truncate to 16 bits */
-       return answer;
-}
-
 static void
 send_probe(int seq, int ttl)
 {
@@ -429,7 +445,7 @@ send_probe(int seq, int ttl)
 
                        /* Always calculate checksum for icmp packets */
                        outicmp->icmp_cksum = 0;
-                       outicmp->icmp_cksum = in_cksum((uint16_t *)outicmp,
+                       outicmp->icmp_cksum = inet_cksum((uint16_t *)outicmp,
                                                packlen - (sizeof(*outip) + optlen));
                        if (outicmp->icmp_cksum == 0)
                                outicmp->icmp_cksum = 0xffff;
@@ -482,7 +498,7 @@ send_probe(int seq, int ttl)
                if (!(option_mask32 & OPT_USE_ICMP)) {
                        out = outdata;
                        len -= sizeof(*outudp);
-                       set_nport(dest_lsa, htons(port + seq));
+                       set_nport(&dest_lsa->u.sa, htons(port + seq));
                }
        }
 
@@ -666,7 +682,6 @@ packet_ok(int read_len, len_and_sockaddr *from_lsa,
                                return (type == ICMP6_TIME_EXCEEDED ? -1 : (code<<8)+1);
                        }
                }
-
        }
 
 # if ENABLE_FEATURE_TRACEROUTE_VERBOSE
@@ -686,7 +701,7 @@ packet_ok(int read_len, len_and_sockaddr *from_lsa,
                        type, pr_type(type), icp->icmp6_code);
 
                read_len -= sizeof(struct icmp6_hdr);
-               for (i = 0; i < read_len ; i++) {
+               for (i = 0; i < read_len; i++) {
                        if (i % 16 == 0)
                                printf("%04x:", i);
                        if (i % 4 == 0)
@@ -752,7 +767,8 @@ print(int read_len, const struct sockaddr *from, const struct sockaddr *to)
                } else
 #endif
                {
-                       read_len -= ((struct ip*)recv_pkt)->ip_hl << 2;
+                       struct ip *ip4packet = (struct ip*)recv_pkt;
+                       read_len -= ip4packet->ip_hl << 2;
                }
                printf(" %d bytes to %s", read_len, ina);
                free(ina);
@@ -774,9 +790,10 @@ print_delta_ms(unsigned t1p, unsigned t2p)
 static int
 common_traceroute_main(int op, char **argv)
 {
-       int i;
        int minpacket;
+#ifdef IP_TOS
        int tos = 0;
+#endif
        int max_ttl = 30;
        int nprobes = 3;
        int first_ttl = 1;
@@ -790,6 +807,7 @@ common_traceroute_main(int op, char **argv)
        char *waittime_str;
        char *pausemsecs_str;
        char *first_ttl_str;
+       char *dest_str;
 #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
        llist_t *source_route_list = NULL;
        int lsrr = 0;
@@ -822,8 +840,10 @@ common_traceroute_main(int op, char **argv)
        if (op & OPT_IP_CHKSUM)
                bb_error_msg("warning: ip checksums disabled");
 #endif
+#ifdef IP_TOS
        if (op & OPT_TOS)
                tos = xatou_range(tos_str, 0, 255);
+#endif
        if (op & OPT_MAX_TTL)
                max_ttl = xatou_range(max_ttl_str, 1, 255);
        if (op & OPT_PORT)
@@ -928,6 +948,7 @@ common_traceroute_main(int op, char **argv)
 #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE && defined IP_OPTIONS
                if (lsrr > 0) {
                        unsigned char optlist[MAX_IPOPTLEN];
+                       unsigned size;
 
                        /* final hop */
                        gwlist[lsrr] = dest_lsa->u.sin.sin_addr.s_addr;
@@ -937,14 +958,14 @@ common_traceroute_main(int op, char **argv)
                        optlist[0] = IPOPT_NOP;
                        /* loose source route option */
                        optlist[1] = IPOPT_LSRR;
-                       i = lsrr * sizeof(gwlist[0]);
-                       optlist[2] = i + 3;
+                       size = lsrr * sizeof(gwlist[0]);
+                       optlist[2] = size + 3;
                        /* pointer to LSRR addresses */
                        optlist[3] = IPOPT_MINOFF;
-                       memcpy(optlist + 4, gwlist, i);
+                       memcpy(optlist + 4, gwlist, size);
 
                        if (setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS,
-                                       (char *)optlist, i + sizeof(gwlist[0])) < 0) {
+                                       (char *)optlist, size + sizeof(gwlist[0])) < 0) {
                                bb_perror_msg_and_die("IP_OPTIONS");
                        }
                }
@@ -1018,10 +1039,10 @@ common_traceroute_main(int op, char **argv)
                int probe_fd = xsocket(af, SOCK_DGRAM, 0);
                if (op & OPT_DEVICE)
                        setsockopt_bindtodevice(probe_fd, device);
-               set_nport(dest_lsa, htons(1025));
+               set_nport(&dest_lsa->u.sa, htons(1025));
                /* dummy connect. makes kernel pick source IP (and port) */
                xconnect(probe_fd, &dest_lsa->u.sa, dest_lsa->len);
-               set_nport(dest_lsa, htons(port));
+               set_nport(&dest_lsa->u.sa, htons(port));
 
                /* read IP and port */
                source_lsa = get_sock_lsa(probe_fd);
@@ -1031,7 +1052,7 @@ common_traceroute_main(int op, char **argv)
                close(probe_fd);
 
                /* bind our sockets to this IP (but not port) */
-               set_nport(source_lsa, 0);
+               set_nport(&source_lsa->u.sa, 0);
                xbind(sndsock, &source_lsa->u.sa, source_lsa->len);
                xbind(rcvsock, &source_lsa->u.sa, source_lsa->len);
 
@@ -1043,8 +1064,12 @@ common_traceroute_main(int op, char **argv)
        xsetgid(getgid());
        xsetuid(getuid());
 
-       printf("traceroute to %s (%s)", argv[0],
-                       xmalloc_sockaddr2dotted_noport(&dest_lsa->u.sa));
+       dest_str = xmalloc_sockaddr2dotted_noport(&dest_lsa->u.sa);
+       printf("traceroute to %s (%s)", argv[0], dest_str);
+       if (ENABLE_FEATURE_CLEAN_UP) {
+               free(dest_str);
+       }
+
        if (op & OPT_SOURCE)
                printf(" from %s", source);
        printf(", %d hops max, %d byte packets\n", max_ttl, packlen);
@@ -1058,28 +1083,34 @@ common_traceroute_main(int op, char **argv)
                int unreachable = 0; /* counter */
                int gotlastaddr = 0; /* flags */
                int got_there = 0;
-               int first = 1;
 
                printf("%2d", ttl);
                for (probe = 0; probe < nprobes; ++probe) {
                        int read_len;
                        unsigned t1;
                        unsigned t2;
+                       int left_ms;
                        struct ip *ip;
 
-                       if (!first && pausemsecs > 0)
-                               usleep(pausemsecs * 1000);
                        fflush_all();
+                       if (probe != 0 && pausemsecs > 0)
+                               usleep(pausemsecs * 1000);
 
-                       t1 = monotonic_us();
                        send_probe(++seq, ttl);
+                       t2 = t1 = monotonic_us();
+
+                       left_ms = waittime * 1000;
+                       while ((read_len = wait_for_reply(from_lsa, to, &t2, &left_ms)) != 0) {
+                               int icmp_code;
+
+                               /* Recv'ed a packet, or read error */
+                               /* t2 = monotonic_us() - set by wait_for_reply */
 
-                       first = 0;
-                       while ((read_len = wait_for_reply(from_lsa, to)) != 0) {
-                               t2 = monotonic_us();
-                               i = packet_ok(read_len, from_lsa, to, seq);
+                               if (read_len < 0)
+                                       continue;
+                               icmp_code = packet_ok(read_len, from_lsa, to, seq);
                                /* Skip short packet */
-                               if (i == 0)
+                               if (icmp_code == 0)
                                        continue;
 
                                if (!gotlastaddr
@@ -1098,10 +1129,10 @@ common_traceroute_main(int op, char **argv)
                                                printf(" (%d)", ip->ip_ttl);
 
                                /* time exceeded in transit */
-                               if (i == -1)
+                               if (icmp_code == -1)
                                        break;
-                               i--;
-                               switch (i) {
+                               icmp_code--;
+                               switch (icmp_code) {
 #if ENABLE_TRACEROUTE6
                                case ICMP6_DST_UNREACH_NOPORT << 8:
                                        got_there = 1;
@@ -1174,16 +1205,18 @@ common_traceroute_main(int op, char **argv)
                                        ++unreachable;
                                        break;
                                default:
-                                       printf(" !<%d>", i);
+                                       printf(" !<%d>", icmp_code);
                                        ++unreachable;
                                        break;
                                }
                                break;
-                       }
+                       } /* while (wait and read a packet) */
+
                        /* there was no packet at all? */
                        if (read_len == 0)
                                printf("  *");
-               }
+               } /* for (nprobes) */
+
                bb_putchar('\n');
                if (got_there
                 || (unreachable > 0 && unreachable >= nprobes - 1)
@@ -1192,6 +1225,12 @@ common_traceroute_main(int op, char **argv)
                }
        }
 
+       if (ENABLE_FEATURE_CLEAN_UP) {
+               free(to);
+               free(lastaddr);
+               free(from_lsa);
+       }
+
        return 0;
 }
 
index 02ff71d..3a0870e 100644 (file)
@@ -7,8 +7,26 @@
  * Original code:
  *      Jeff Dike
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define tunctl_trivial_usage
+//usage:       "[-f device] ([-t name] | -d name)" IF_FEATURE_TUNCTL_UG(" [-u owner] [-g group] [-b]")
+//usage:#define tunctl_full_usage "\n\n"
+//usage:       "Create or delete tun interfaces\n"
+//usage:     "\n       -f name         tun device (/dev/net/tun)"
+//usage:     "\n       -t name         Create iface 'name'"
+//usage:     "\n       -d name         Delete iface 'name'"
+//usage:       IF_FEATURE_TUNCTL_UG(
+//usage:     "\n       -u owner        Set iface owner"
+//usage:     "\n       -g group        Set iface group"
+//usage:     "\n       -b              Brief output"
+//usage:       )
+//usage:
+//usage:#define tunctl_example_usage
+//usage:       "# tunctl\n"
+//usage:       "# tunctl -d tun0\n"
+
 #include <netinet/in.h>
 #include <net/if.h>
 #include <linux/if_tun.h>
index aac8856..6bfa398 100644 (file)
@@ -8,6 +8,7 @@ INSERT
 config UDHCPD
        bool "udhcp server (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.
@@ -38,7 +39,21 @@ config FEATURE_UDHCPD_WRITE_LEASES_EARLY
          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
+         rewriting remains undisturbed.
+
+config FEATURE_UDHCPD_BASE_IP_ON_MAC
+       bool "Select IP address based on client MAC"
+       default n
+       depends on UDHCPD
+       help
+         If selected, udhcpd will base its selection of IP address to offer
+         on the client's hardware address. Otherwise udhcpd uses the next
+         consecutive free address.
+
+         This reduces the frequency of IP address changes for clients
+         which let their lease expire, and makes consecutive DHCPOFFERS
+         for the same client to (almost always) contain the same
+         IP address.
 
 config DHCPD_LEASES_FILE
        string "Absolute path to lease file"
@@ -51,6 +66,7 @@ config DHCPD_LEASES_FILE
 config UDHCPC
        bool "udhcp client (udhcpc)"
        default y
+       select PLATFORM_LINUX
        help
          udhcpc is a DHCP client geared primarily toward embedded systems,
          while striving to be fully functional and RFC compliant.
@@ -70,7 +86,7 @@ config FEATURE_UDHCPC_ARPING
 
 config FEATURE_UDHCP_PORT
        bool "Enable '-P port' option for udhcpd and udhcpc"
-       default y
+       default n
        depends on UDHCPD || UDHCPC
        help
          At the cost of ~300 bytes, enables -P port option.
@@ -83,7 +99,7 @@ config UDHCP_DEBUG
        depends on UDHCPD || UDHCPC || DHCPRELAY
        help
          Verbosity can be increased with multiple -v options.
-         This options controls how high it can be cranked up.
+         This option controls how high it can be cranked up.
 
          Bigger values result in bigger code. Levels above 1
          are very verbose and useful for debugging only.
@@ -97,6 +113,14 @@ config FEATURE_UDHCP_RFC3397
          search lists via option 119, specified in RFC 3397,
          and SIP servers option 120, specified in RFC 3361.
 
+config FEATURE_UDHCP_8021Q
+       bool "Support for 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_DEFAULT_SCRIPT
        string "Absolute path to config script"
        default "/usr/share/udhcpc/default.script"
index f845bc1..b8767ba 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+# Licensed under GPLv2 or later, see file LICENSE in this source tree.
 #
 
 lib-y:=
index 7c8c244..b43e52e 100644 (file)
@@ -3,7 +3,7 @@
  * Mostly stolen from: dhcpcd - DHCP client daemon
  * by Yoichi Hariguchi <yoichi@fore.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include <netinet/if_ether.h>
 #include <net/if_arp.h>
@@ -118,8 +118,13 @@ int FAST_FUNC arpping(uint32_t test_nip,
                                break;
                        }
                }
-               timeout_ms -= (unsigned)monotonic_ms() - prevTime;
-       } while (timeout_ms > 0);
+               timeout_ms -= (unsigned)monotonic_ms() - prevTime + 1;
+
+               /* We used to check "timeout_ms > 0", but
+                * this is more under/overflow-resistant
+                * (people did see overflows here when system time jumps):
+                */
+       } while ((unsigned)timeout_ms <= 2000);
 
  ret:
        close(s);
index 90a07ed..fe322db 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "common.h"
 
@@ -29,15 +29,19 @@ const struct dhcp_optflag dhcp_optflags[] = {
 //     { OPTION_IP | OPTION_LIST                 , 0x07 }, /* DHCP_LOG_SERVER    */
 //     { OPTION_IP | OPTION_LIST                 , 0x08 }, /* DHCP_COOKIE_SERVER */
        { OPTION_IP | OPTION_LIST                 , 0x09 }, /* DHCP_LPR_SERVER    */
-       { OPTION_STRING               | OPTION_REQ, 0x0c }, /* DHCP_HOST_NAME     */
+       { OPTION_STRING_HOST          | OPTION_REQ, 0x0c }, /* DHCP_HOST_NAME     */
        { OPTION_U16                              , 0x0d }, /* DHCP_BOOT_SIZE     */
-       { OPTION_STRING               | OPTION_REQ, 0x0f }, /* DHCP_DOMAIN_NAME   */
+       { OPTION_STRING_HOST          | OPTION_REQ, 0x0f }, /* DHCP_DOMAIN_NAME   */
        { OPTION_IP                               , 0x10 }, /* DHCP_SWAP_SERVER   */
        { OPTION_STRING                           , 0x11 }, /* DHCP_ROOT_PATH     */
        { OPTION_U8                               , 0x17 }, /* DHCP_IP_TTL        */
        { OPTION_U16                              , 0x1a }, /* DHCP_MTU           */
+//TODO: why do we request DHCP_BROADCAST? Can't we assume that
+//in the unlikely case it is different from typical N.N.255.255,
+//server would let us know anyway?
        { OPTION_IP                   | OPTION_REQ, 0x1c }, /* DHCP_BROADCAST     */
-       { OPTION_STRING                           , 0x28 }, /* DHCP_NIS_DOMAIN    */
+       { OPTION_IP_PAIR | OPTION_LIST            , 0x21 }, /* DHCP_ROUTES        */
+       { OPTION_STRING_HOST                      , 0x28 }, /* DHCP_NIS_DOMAIN    */
        { OPTION_IP | OPTION_LIST                 , 0x29 }, /* DHCP_NIS_SERVER    */
        { OPTION_IP | OPTION_LIST     | OPTION_REQ, 0x2a }, /* DHCP_NTP_SERVER    */
        { OPTION_IP | OPTION_LIST                 , 0x2c }, /* DHCP_WINS_SERVER   */
@@ -45,7 +49,7 @@ const struct dhcp_optflag dhcp_optflags[] = {
        { OPTION_IP                               , 0x36 }, /* DHCP_SERVER_ID     */
        { OPTION_STRING                           , 0x38 }, /* DHCP_ERR_MESSAGE   */
 //TODO: must be combined with 'sname' and 'file' handling:
-       { OPTION_STRING                           , 0x42 }, /* DHCP_TFTP_SERVER_NAME */
+       { OPTION_STRING_HOST                      , 0x42 }, /* DHCP_TFTP_SERVER_NAME */
        { OPTION_STRING                           , 0x43 }, /* DHCP_BOOT_FILE     */
 //TODO: not a string, but a set of LASCII strings:
 //     { OPTION_STRING                           , 0x4D }, /* DHCP_USER_CLASS    */
@@ -53,7 +57,14 @@ const struct dhcp_optflag dhcp_optflags[] = {
        { OPTION_DNS_STRING | OPTION_LIST         , 0x77 }, /* DHCP_DOMAIN_SEARCH */
        { OPTION_SIP_SERVERS                      , 0x78 }, /* DHCP_SIP_SERVERS   */
 #endif
-       { OPTION_STATIC_ROUTES                    , 0x79 }, /* DHCP_STATIC_ROUTES */
+       { OPTION_STATIC_ROUTES | OPTION_LIST      , 0x79 }, /* DHCP_STATIC_ROUTES */
+#if ENABLE_FEATURE_UDHCP_8021Q
+       { OPTION_U16                              , 0x84 }, /* DHCP_VLAN_ID       */
+       { OPTION_U8                               , 0x85 }, /* DHCP_VLAN_PRIORITY */
+#endif
+       { OPTION_STRING                           , 0xd1 }, /* DHCP_PXE_CONF_FILE */
+       { OPTION_6RD                              , 0xd4 }, /* DHCP_6RD           */
+       { OPTION_STATIC_ROUTES | OPTION_LIST      , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */
        { OPTION_STRING                           , 0xfc }, /* DHCP_WPAD          */
 
        /* Options below have no match in dhcp_option_strings[],
@@ -66,9 +77,10 @@ const struct dhcp_optflag dhcp_optflags[] = {
        { OPTION_IP                               , 0x32 }, /* DHCP_REQUESTED_IP  */
        { OPTION_U8                               , 0x35 }, /* DHCP_MESSAGE_TYPE  */
        { OPTION_U16                              , 0x39 }, /* DHCP_MAX_SIZE      */
-       { OPTION_STRING                           , 0x3c }, /* DHCP_VENDOR        */
-//FIXME: handling of this option is not exactly correct:
-       { OPTION_STRING                           , 0x3d }, /* DHCP_CLIENT_ID     */
+//looks like these opts will work just fine even without these defs:
+//     { OPTION_STRING                           , 0x3c }, /* DHCP_VENDOR        */
+//     /* not really a string: */
+//     { OPTION_STRING                           , 0x3d }, /* DHCP_CLIENT_ID     */
        { 0, 0 } /* zeroed terminating entry */
 };
 
@@ -95,6 +107,7 @@ const char dhcp_option_strings[] ALIGN1 =
        "ipttl" "\0"       /* DHCP_IP_TTL         */
        "mtu" "\0"         /* DHCP_MTU            */
        "broadcast" "\0"   /* DHCP_BROADCAST      */
+       "routes" "\0"      /* DHCP_ROUTES         */
        "nisdomain" "\0"   /* DHCP_NIS_DOMAIN     */
        "nissrv" "\0"      /* DHCP_NIS_SERVER     */
        "ntpsrv" "\0"      /* DHCP_NTP_SERVER     */
@@ -111,9 +124,14 @@ const char dhcp_option_strings[] ALIGN1 =
 // is not handled yet by "string->option" conversion code:
        "sipsrv" "\0"      /* DHCP_SIP_SERVERS    */
 #endif
-// doesn't work in udhcpd.conf since OPTION_STATIC_ROUTES
-// is not handled yet by "string->option" conversion code:
        "staticroutes" "\0"/* DHCP_STATIC_ROUTES  */
+#if ENABLE_FEATURE_UDHCP_8021Q
+       "vlanid" "\0"      /* DHCP_VLAN_ID        */
+       "vlanpriority" "\0"/* DHCP_VLAN_PRIORITY  */
+#endif
+       "pxeconffile" "\0" /* DHCP_PXE_CONF_FILE  */
+       "ip6rd" "\0"       /* DHCP_6RD            */
+       "msstaticroutes""\0"/* DHCP_MS_STATIC_ROUTES */
        "wpad" "\0"        /* DHCP_WPAD           */
        ;
 
@@ -130,6 +148,7 @@ const uint8_t dhcp_option_lengths[] ALIGN1 = {
        [OPTION_IP_PAIR] = 8,
 //     [OPTION_BOOLEAN] = 1,
        [OPTION_STRING] =  1,  /* ignored by udhcp_str2optset */
+       [OPTION_STRING_HOST] = 1,  /* ignored by udhcp_str2optset */
 #if ENABLE_FEATURE_UDHCP_RFC3397
        [OPTION_DNS_STRING] = 1,  /* ignored by both udhcp_str2optset and xmalloc_optname_optval */
        [OPTION_SIP_SERVERS] = 1,
@@ -141,6 +160,7 @@ const uint8_t dhcp_option_lengths[] ALIGN1 = {
        [OPTION_S32] =     4,
        /* Just like OPTION_STRING, we use minimum length here */
        [OPTION_STATIC_ROUTES] = 5,
+       [OPTION_6RD] =    22,  /* ignored by udhcp_str2optset */
 };
 
 
@@ -318,7 +338,8 @@ int FAST_FUNC udhcp_str2nip(const char *str, void *arg)
        lsa = host_and_af2sockaddr(str, 0, AF_INET);
        if (!lsa)
                return 0;
-       *(uint32_t*)arg = lsa->u.sin.sin_addr.s_addr;
+       /* arg maybe unaligned */
+       move_to_unaligned32((uint32_t*)arg, lsa->u.sin.sin_addr.s_addr);
        free(lsa);
        return 1;
 }
@@ -352,25 +373,28 @@ static NOINLINE void attach_option(
                char *buffer,
                int length)
 {
-       struct option_set *existing, *new, **curr;
-       char *allocated = NULL;
+       struct option_set *existing;
+       char *allocated;
 
-       existing = udhcp_find_option(*opt_list, optflag->code);
-       if (!existing) {
-               log2("Attaching option %02x to list", optflag->code);
-               allocated = allocate_tempopt_if_needed(optflag, buffer, &length);
+       allocated = allocate_tempopt_if_needed(optflag, buffer, &length);
 #if ENABLE_FEATURE_UDHCP_RFC3397
-               if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_DNS_STRING) {
-                       /* reuse buffer and length for RFC1035-formatted string */
-                       allocated = buffer = (char *)dname_enc(NULL, 0, buffer, &length);
-               }
+       if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_DNS_STRING) {
+               /* reuse buffer and length for RFC1035-formatted string */
+               allocated = buffer = (char *)dname_enc(NULL, 0, buffer, &length);
+       }
 #endif
+
+       existing = udhcp_find_option(*opt_list, optflag->code);
+       if (!existing) {
+               struct option_set *new, **curr;
+
                /* make a new option */
+               log2("Attaching option %02x to list", optflag->code);
                new = xmalloc(sizeof(*new));
                new->data = xmalloc(length + OPT_DATA);
                new->data[OPT_CODE] = optflag->code;
                new->data[OPT_LEN] = length;
-               memcpy(new->data + OPT_DATA, buffer, length);
+               memcpy(new->data + OPT_DATA, (allocated ? allocated : buffer), length);
 
                curr = opt_list;
                while (*curr && (*curr)->data[OPT_CODE] < optflag->code)
@@ -386,24 +410,19 @@ static NOINLINE void attach_option(
 
                /* add it to an existing option */
                log2("Attaching option %02x to existing member of list", optflag->code);
-               allocated = allocate_tempopt_if_needed(optflag, buffer, &length);
                old_len = existing->data[OPT_LEN];
-#if ENABLE_FEATURE_UDHCP_RFC3397
-               if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_DNS_STRING) {
-                       /* reuse buffer and length for RFC1035-formatted string */
-                       allocated = buffer = (char *)dname_enc(existing->data + OPT_DATA, old_len, buffer, &length);
-               }
-#endif
                if (old_len + length < 255) {
                        /* actually 255 is ok too, but adding a space can overlow it */
 
                        existing->data = xrealloc(existing->data, OPT_DATA + 1 + old_len + length);
-                       if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_STRING) {
+                       if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_STRING
+                        || (optflag->flags & OPTION_TYPE_MASK) == OPTION_STRING_HOST
+                       ) {
                                /* add space separator between STRING options in a list */
                                existing->data[OPT_DATA + old_len] = ' ';
                                old_len++;
                        }
-                       memcpy(existing->data + OPT_DATA + old_len, buffer, length);
+                       memcpy(existing->data + OPT_DATA + old_len, (allocated ? allocated : buffer), length);
                        existing->data[OPT_LEN] = old_len + length;
                } /* else, ignore the data, we could put this in a second option in the future */
        } /* else, ignore the new data */
@@ -415,13 +434,14 @@ static NOINLINE void attach_option(
 int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg)
 {
        struct option_set **opt_list = arg;
-       char *opt, *val, *endptr;
+       char *opt, *val;
        char *str;
        const struct dhcp_optflag *optflag;
        struct dhcp_optflag bin_optflag;
        unsigned optcode;
        int retval, length;
-       char buffer[8] ALIGNED(4);
+       /* IP_PAIR needs 8 bytes, STATIC_ROUTES needs 9 max */
+       char buffer[9] ALIGNED(4);
        uint16_t *result_u16 = (uint16_t *) buffer;
        uint32_t *result_u32 = (uint32_t *) buffer;
 
@@ -462,6 +482,7 @@ int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg)
                                retval = udhcp_str2nip(val, buffer + 4);
                        break;
                case OPTION_STRING:
+               case OPTION_STRING_HOST:
 #if ENABLE_FEATURE_UDHCP_RFC3397
                case OPTION_DNS_STRING:
 #endif
@@ -478,34 +499,53 @@ int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg)
 //                     break;
 //             }
                case OPTION_U8:
-                       buffer[0] = strtoul(val, &endptr, 0);
-                       retval = (endptr[0] == '\0');
+                       buffer[0] = bb_strtou32(val, NULL, 0);
+                       retval = (errno == 0);
                        break;
                /* htonX are macros in older libc's, using temp var
                 * in code below for safety */
                /* TODO: use bb_strtoX? */
                case OPTION_U16: {
-                       unsigned long tmp = strtoul(val, &endptr, 0);
+                       uint32_t tmp = bb_strtou32(val, NULL, 0);
                        *result_u16 = htons(tmp);
-                       retval = (endptr[0] == '\0' /*&& tmp < 0x10000*/);
+                       retval = (errno == 0 /*&& tmp < 0x10000*/);
                        break;
                }
 //             case OPTION_S16: {
-//                     long tmp = strtol(val, &endptr, 0);
+//                     long tmp = bb_strtoi32(val, NULL, 0);
 //                     *result_u16 = htons(tmp);
-//                     retval = (endptr[0] == '\0');
+//                     retval = (errno == 0);
 //                     break;
 //             }
                case OPTION_U32: {
-                       unsigned long tmp = strtoul(val, &endptr, 0);
+                       uint32_t tmp = bb_strtou32(val, NULL, 0);
                        *result_u32 = htonl(tmp);
-                       retval = (endptr[0] == '\0');
+                       retval = (errno == 0);
                        break;
                }
                case OPTION_S32: {
-                       long tmp = strtol(val, &endptr, 0);
+                       int32_t tmp = bb_strtoi32(val, NULL, 0);
                        *result_u32 = htonl(tmp);
-                       retval = (endptr[0] == '\0');
+                       retval = (errno == 0);
+                       break;
+               }
+               case OPTION_STATIC_ROUTES: {
+                       /* Input: "a.b.c.d/m" */
+                       /* Output: mask(1 byte),pfx(0-4 bytes),gw(4 bytes) */
+                       unsigned mask;
+                       char *slash = strchr(val, '/');
+                       if (slash) {
+                               *slash = '\0';
+                               retval = udhcp_str2nip(val, buffer + 1);
+                               buffer[0] = mask = bb_strtou(slash + 1, NULL, 10);
+                               val = strtok(NULL, ", \t/-");
+                               if (!val || mask > 32 || errno)
+                                       retval = 0;
+                               if (retval) {
+                                       length = ((mask + 7) >> 3) + 5;
+                                       retval = udhcp_str2nip(val, buffer + (length - 4));
+                               }
+                       }
                        break;
                }
                case OPTION_BIN: /* handled in attach_option() */
@@ -516,7 +556,26 @@ int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg)
                }
                if (retval)
                        attach_option(opt_list, optflag, opt, length);
-       } while (retval && optflag->flags & OPTION_LIST);
+       } while (retval && (optflag->flags & OPTION_LIST));
 
        return retval;
 }
+
+/* note: ip is a pointer to an IPv6 in network order, possibly misaliged */
+int FAST_FUNC sprint_nip6(char *dest, /*const char *pre,*/ const uint8_t *ip)
+{
+       char hexstrbuf[16 * 2];
+       bin2hex(hexstrbuf, (void*)ip, 16);
+       return sprintf(dest, /* "%s" */
+               "%.4s:%.4s:%.4s:%.4s:%.4s:%.4s:%.4s:%.4s",
+               /* pre, */
+               hexstrbuf + 0 * 4,
+               hexstrbuf + 1 * 4,
+               hexstrbuf + 2 * 4,
+               hexstrbuf + 3 * 4,
+               hexstrbuf + 4 * 4,
+               hexstrbuf + 5 * 4,
+               hexstrbuf + 6 * 4,
+               hexstrbuf + 7 * 4
+       );
+}
index ce81d18..5e70d60 100644 (file)
@@ -3,7 +3,7 @@
  * Russ Dill <Russ.Dill@asu.edu> September 2001
  * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #ifndef UDHCP_COMMON_H
 #define UDHCP_COMMON_H 1
@@ -14,7 +14,7 @@
 
 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 
-extern const uint8_t MAC_BCAST_ADDR[6]; /* six all-ones */
+extern const uint8_t MAC_BCAST_ADDR[6] ALIGN2; /* six all-ones */
 
 
 /*** DHCP packet ***/
@@ -63,14 +63,14 @@ struct udp_dhcp_packet {
 } PACKED;
 
 enum {
-       IP_UPD_DHCP_SIZE = sizeof(struct ip_udp_dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS,
-       UPD_DHCP_SIZE    = sizeof(struct udp_dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS,
+       IP_UDP_DHCP_SIZE = sizeof(struct ip_udp_dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS,
+       UDP_DHCP_SIZE    = sizeof(struct udp_dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS,
        DHCP_SIZE        = sizeof(struct dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS,
 };
 
 /* Let's see whether compiler understood us right */
 struct BUG_bad_sizeof_struct_ip_udp_dhcp_packet {
-       char c[IP_UPD_DHCP_SIZE == 576 ? 1 : -1];
+       char c[IP_UDP_DHCP_SIZE == 576 ? 1 : -1];
 };
 
 
@@ -80,6 +80,9 @@ enum {
        OPTION_IP = 1,
        OPTION_IP_PAIR,
        OPTION_STRING,
+       /* Opts of STRING_HOST type will be sanitized before they are passed
+        * to udhcpc script's environment: */
+       OPTION_STRING_HOST,
 //     OPTION_BOOLEAN,
        OPTION_U8,
        OPTION_U16,
@@ -88,6 +91,7 @@ enum {
        OPTION_S32,
        OPTION_BIN,
        OPTION_STATIC_ROUTES,
+       OPTION_6RD,
 #if ENABLE_FEATURE_UDHCP_RFC3397
        OPTION_DNS_STRING,  /* RFC1035 compressed domain name list */
        OPTION_SIP_SERVERS,
@@ -103,7 +107,7 @@ enum {
 /* DHCP option codes (partial list). See RFC 2132 and
  * http://www.iana.org/assignments/bootp-dhcp-parameters/
  * Commented out options are handled by common option machinery,
- * uncommented ones have spacial cases (grep for them to see).
+ * uncommented ones have special cases (grep for them to see).
  */
 #define DHCP_PADDING            0x00
 #define DHCP_SUBNET             0x01
@@ -123,6 +127,7 @@ enum {
 //#define DHCP_IP_TTL           0x17
 //#define DHCP_MTU              0x1a
 //#define DHCP_BROADCAST        0x1c
+//#define DHCP_ROUTES           0x21
 //#define DHCP_NIS_DOMAIN       0x28
 //#define DHCP_NIS_SERVER       0x29
 //#define DHCP_NTP_SERVER       0x2a
@@ -144,6 +149,10 @@ enum {
 //#define DHCP_DOMAIN_SEARCH    0x77 /* RFC 3397. set of ASCIZ string, DNS-style compressed */
 //#define DHCP_SIP_SERVERS      0x78 /* RFC 3361. flag byte, then: 0: domain names, 1: IP addrs */
 //#define DHCP_STATIC_ROUTES    0x79 /* RFC 3442. (mask,ip,router) tuples */
+//#define DHCP_VLAN_ID          0x84 /* 802.1P VLAN ID */
+//#define DHCP_VLAN_PRIORITY    0x85 /* 802.1Q VLAN priority */
+//#define DHCP_PXE_CONF_FILE    0xd1 /* RFC 5071 Configuration File */
+//#define DHCP_MS_STATIC_ROUTES 0xf9 /* Microsoft's pre-RFC 3442 code for 0x79? */
 //#define DHCP_WPAD             0xfc /* MSIE's Web Proxy Autodiscovery Protocol */
 #define DHCP_END                0xff
 
@@ -179,8 +188,8 @@ struct option_set {
 };
 
 extern const struct dhcp_optflag dhcp_optflags[];
-extern const char dhcp_option_strings[];
-extern const uint8_t dhcp_option_lengths[];
+extern const char dhcp_option_strings[] ALIGN1;
+extern const uint8_t dhcp_option_lengths[] ALIGN1;
 
 unsigned FAST_FUNC udhcp_option_idx(const char *name);
 
@@ -244,6 +253,7 @@ struct option_set *udhcp_find_option(struct option_set *opt_list, uint8_t code)
 /*** Logging ***/
 
 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
+# define IF_UDHCP_VERBOSE(...) __VA_ARGS__
 extern unsigned dhcp_verbose;
 # define log1(...) do { if (dhcp_verbose >= 1) bb_info_msg(__VA_ARGS__); } while (0)
 # if CONFIG_UDHCP_DEBUG >= 2
@@ -259,6 +269,7 @@ void udhcp_dump_packet(struct dhcp_packet *packet) FAST_FUNC;
 #  define log3(...) ((void)0)
 # endif
 #else
+# define IF_UDHCP_VERBOSE(...)
 # define udhcp_dump_packet(...) ((void)0)
 # define log1(...) ((void)0)
 # define log2(...) ((void)0)
@@ -273,8 +284,6 @@ int FAST_FUNC udhcp_str2nip(const char *str, void *arg);
 /* 2nd param is "struct option_set**" */
 int FAST_FUNC udhcp_str2optset(const char *str, void *arg);
 
-uint16_t udhcp_checksum(void *addr, int count) FAST_FUNC;
-
 void udhcp_init_header(struct dhcp_packet *packet, char type) FAST_FUNC;
 
 int udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd) FAST_FUNC;
@@ -303,6 +312,9 @@ int arpping(uint32_t test_nip,
                uint8_t *from_mac,
                const char *interface) FAST_FUNC;
 
+/* note: ip is a pointer to an IPv6 in network order, possibly misaliged */
+int sprint_nip6(char *dest, /*const char *pre,*/ const uint8_t *ip) FAST_FUNC;
+
 POP_SAVED_FUNCTION_VISIBILITY
 
 #endif
diff --git a/networking/udhcp/d6_common.h b/networking/udhcp/d6_common.h
new file mode 100644 (file)
index 0000000..eb211ea
--- /dev/null
@@ -0,0 +1,127 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2011 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#ifndef UDHCP_D6_COMMON_H
+#define UDHCP_D6_COMMON_H 1
+
+#include <netinet/ip6.h>
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+
+/*** DHCPv6 packet ***/
+
+/* DHCPv6 protocol. See RFC 3315 */
+#define D6_MSG_SOLICIT              1
+#define D6_MSG_ADVERTISE            2
+#define D6_MSG_REQUEST              3
+#define D6_MSG_CONFIRM              4
+#define D6_MSG_RENEW                5
+#define D6_MSG_REBIND               6
+#define D6_MSG_REPLY                7
+#define D6_MSG_RELEASE              8
+#define D6_MSG_DECLINE              9
+#define D6_MSG_RECONFIGURE         10
+#define D6_MSG_INFORMATION_REQUEST 11
+#define D6_MSG_RELAY_FORW          12
+#define D6_MSG_RELAY_REPL          13
+
+struct d6_packet {
+       union {
+               uint8_t d6_msg_type;
+               uint32_t d6_xid32;
+       } d6_u;
+       uint8_t d6_options[576 - sizeof(struct iphdr) - sizeof(struct udphdr) - 4
+                       + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS];
+} PACKED;
+#define d6_msg_type d6_u.d6_msg_type
+#define d6_xid32    d6_u.d6_xid32
+
+struct ip6_udp_d6_packet {
+       struct ip6_hdr ip6;
+       struct udphdr udp;
+       struct d6_packet data;
+} PACKED;
+
+struct udp_d6_packet {
+       struct udphdr udp;
+       struct d6_packet data;
+} PACKED;
+
+/*** Options ***/
+
+struct d6_option {
+       uint8_t code_hi;
+       uint8_t code;
+       uint8_t len_hi;
+       uint8_t len;
+       uint8_t data[1];
+} PACKED;
+
+#define D6_OPT_CLIENTID       1
+#define D6_OPT_SERVERID       2
+#define D6_OPT_IA_NA          3
+#define D6_OPT_IA_TA          4
+#define D6_OPT_IAADDR         5
+#define D6_OPT_ORO            6
+#define D6_OPT_PREFERENCE     7
+#define D6_OPT_ELAPSED_TIME   8
+#define D6_OPT_RELAY_MSG      9
+#define D6_OPT_AUTH          11
+#define D6_OPT_UNICAST       12
+#define D6_OPT_STATUS_CODE   13
+#define D6_OPT_RAPID_COMMIT  14
+#define D6_OPT_USER_CLASS    15
+#define D6_OPT_VENDOR_CLASS  16
+#define D6_OPT_VENDOR_OPTS   17
+#define D6_OPT_INTERFACE_ID  18
+#define D6_OPT_RECONF_MSG    19
+#define D6_OPT_RECONF_ACCEPT 20
+
+#define D6_OPT_IA_PD         25
+#define D6_OPT_IAPREFIX      26
+
+/*** Other shared functions ***/
+
+struct client6_data_t {
+       struct d6_option *server_id;
+       struct d6_option *ia_na;
+       char **env_ptr;
+       unsigned env_idx;
+};
+
+#define client6_data (*(struct client6_data_t*)(&bb_common_bufsiz1[COMMON_BUFSIZE - sizeof(struct client6_data_t)]))
+
+int FAST_FUNC d6_listen_socket(int port, const char *inf);
+
+int FAST_FUNC d6_recv_kernel_packet(
+               struct in6_addr *peer_ipv6,
+               struct d6_packet *packet, int fd
+);
+
+int FAST_FUNC d6_send_raw_packet(
+               struct d6_packet *d6_pkt, unsigned d6_pkt_size,
+               struct in6_addr *src_ipv6, int source_port,
+               struct in6_addr *dst_ipv6, int dest_port, const uint8_t *dest_arp,
+               int ifindex
+);
+
+int FAST_FUNC d6_send_kernel_packet(
+               struct d6_packet *d6_pkt, unsigned d6_pkt_size,
+               struct in6_addr *src_ipv6, int source_port,
+               struct in6_addr *dst_ipv6, int dest_port
+);
+
+#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
+void FAST_FUNC d6_dump_packet(struct d6_packet *packet);
+#else
+# define d6_dump_packet(packet) ((void)0)
+#endif
+
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
new file mode 100644 (file)
index 0000000..b0f0798
--- /dev/null
@@ -0,0 +1,1492 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * DHCPv6 client.
+ *
+ * 2011-11.
+ * WARNING: THIS CODE IS INCOMPLETE. IT IS NOWHERE NEAR
+ * TO BE READY FOR PRODUCTION USE.
+ *
+ * Copyright (C) 2011 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//config:config UDHCPC6
+//config:      bool "udhcp client for DHCPv6 (udhcpc6)"
+//config:      default n  # not yet ready
+//config:      depends on FEATURE_IPV6
+//config:      help
+//config:        udhcpc6 is a DHCPv6 client
+
+//applet:IF_UDHCPC6(APPLET(udhcpc6, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_UDHCPC6) += d6_dhcpc.o d6_packet.o d6_socket.o common.o socket.o signalpipe.o
+
+
+#include <syslog.h>
+/* Override ENABLE_FEATURE_PIDFILE - ifupdown needs our pidfile to always exist */
+#define WANT_PIDFILE 1
+#include "common.h"
+#include "dhcpd.h"
+#include "dhcpc.h"
+#include "d6_common.h"
+
+#include <netinet/if_ether.h>
+#include <netpacket/packet.h>
+#include <linux/filter.h>
+
+/* "struct client_config_t client_config" is in bb_common_bufsiz1 */
+
+
+#if ENABLE_LONG_OPTS
+static const char udhcpc6_longopts[] ALIGN1 =
+       "interface\0"      Required_argument "i"
+       "now\0"            No_argument       "n"
+       "pidfile\0"        Required_argument "p"
+       "quit\0"           No_argument       "q"
+       "release\0"        No_argument       "R"
+       "request\0"        Required_argument "r"
+       "script\0"         Required_argument "s"
+       "timeout\0"        Required_argument "T"
+       "retries\0"        Required_argument "t"
+       "tryagain\0"       Required_argument "A"
+       "syslog\0"         No_argument       "S"
+       "request-option\0" Required_argument "O"
+       "no-default-options\0" No_argument   "o"
+       "foreground\0"     No_argument       "f"
+       "background\0"     No_argument       "b"
+///    IF_FEATURE_UDHCPC_ARPING("arping\0"     No_argument       "a")
+       IF_FEATURE_UDHCP_PORT("client-port\0"   Required_argument "P")
+       ;
+#endif
+/* Must match getopt32 option string order */
+enum {
+       OPT_i = 1 << 0,
+       OPT_n = 1 << 1,
+       OPT_p = 1 << 2,
+       OPT_q = 1 << 3,
+       OPT_R = 1 << 4,
+       OPT_r = 1 << 5,
+       OPT_s = 1 << 6,
+       OPT_T = 1 << 7,
+       OPT_t = 1 << 8,
+       OPT_S = 1 << 9,
+       OPT_A = 1 << 10,
+       OPT_O = 1 << 11,
+       OPT_o = 1 << 12,
+       OPT_x = 1 << 13,
+       OPT_f = 1 << 14,
+/* The rest has variable bit positions, need to be clever */
+       OPTBIT_f = 14,
+       USE_FOR_MMU(             OPTBIT_b,)
+       ///IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
+       IF_FEATURE_UDHCP_PORT(   OPTBIT_P,)
+       USE_FOR_MMU(             OPT_b = 1 << OPTBIT_b,)
+       ///IF_FEATURE_UDHCPC_ARPING(OPT_a = 1 << OPTBIT_a,)
+       IF_FEATURE_UDHCP_PORT(   OPT_P = 1 << OPTBIT_P,)
+};
+
+
+/*** Utility functions ***/
+
+static void *d6_find_option(uint8_t *option, uint8_t *option_end, unsigned code)
+{
+       /* "length minus 4" */
+       int len_m4 = option_end - option - 4;
+       while (len_m4 >= 0) {
+               /* Next option's len is too big? */
+               if (option[3] > len_m4)
+                       return NULL; /* yes. bogus packet! */
+               /* So far we treat any opts with code >255
+                * or len >255 as bogus, and stop at once.
+                * This simplifies big-endian handling.
+                */
+               if (option[0] != 0 || option[2] != 0)
+                       return NULL;
+               /* Option seems to be valid */
+               /* Does its code match? */
+               if (option[1] == code)
+                       return option; /* yes! */
+               option += option[3] + 4;
+               len_m4 -= option[3] + 4;
+       }
+       return NULL;
+}
+
+static void *d6_copy_option(uint8_t *option, uint8_t *option_end, unsigned code)
+{
+       uint8_t *opt = d6_find_option(option, option_end, code);
+       if (!opt)
+               return opt;
+       return memcpy(xmalloc(opt[3] + 4), opt, opt[3] + 4);
+}
+
+static void *d6_store_blob(void *dst, const void *src, unsigned len)
+{
+       memcpy(dst, src, len);
+       return dst + len;
+}
+
+
+/*** Script execution code ***/
+
+static char** new_env(void)
+{
+       client6_data.env_ptr = xrealloc_vector(client6_data.env_ptr, 3, client6_data.env_idx);
+       return &client6_data.env_ptr[client6_data.env_idx++];
+}
+
+/* put all the parameters into the environment */
+static void option_to_env(uint8_t *option, uint8_t *option_end)
+{
+       /* "length minus 4" */
+       int len_m4 = option_end - option - 4;
+       while (len_m4 >= 0) {
+               uint32_t v32;
+               char ipv6str[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")];
+
+               if (option[0] != 0 || option[2] != 0)
+                       break;
+
+               switch (option[1]) {
+               //case D6_OPT_CLIENTID:
+               //case D6_OPT_SERVERID:
+               case D6_OPT_IA_NA:
+               case D6_OPT_IA_PD:
+                       option_to_env(option + 16, option + 4 + option[3]);
+                       break;
+               //case D6_OPT_IA_TA:
+               case D6_OPT_IAADDR:
+/*   0                   1                   2                   3
+ *  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
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |          OPTION_IAADDR        |          option-len           |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                                                               |
+ * |                         IPv6 address                          |
+ * |                                                               |
+ * |                                                               |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                      preferred-lifetime                       |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                        valid-lifetime                         |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+                       sprint_nip6(ipv6str, option + 4);
+                       *new_env() = xasprintf("ipv6=%s", ipv6str);
+
+                       move_from_unaligned32(v32, option + 4 + 16 + 4);
+                       *new_env() = xasprintf("lease=%u", (unsigned)v32);
+                       break;
+
+               //case D6_OPT_ORO:
+               //case D6_OPT_PREFERENCE:
+               //case D6_OPT_ELAPSED_TIME:
+               //case D6_OPT_RELAY_MSG:
+               //case D6_OPT_AUTH:
+               //case D6_OPT_UNICAST:
+               //case D6_OPT_STATUS_CODE:
+               //case D6_OPT_RAPID_COMMIT:
+               //case D6_OPT_USER_CLASS:
+               //case D6_OPT_VENDOR_CLASS:
+               //case D6_OPT_VENDOR_OPTS:
+               //case D6_OPT_INTERFACE_ID:
+               //case D6_OPT_RECONF_MSG:
+               //case D6_OPT_RECONF_ACCEPT:
+
+               case D6_OPT_IAPREFIX:
+/*  0                   1                   2                   3
+ *  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
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |        OPTION_IAPREFIX        |         option-length         |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                      preferred-lifetime                       |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                        valid-lifetime                         |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | prefix-length |                                               |
+ * +-+-+-+-+-+-+-+-+          IPv6 prefix                          |
+ * |                           (16 octets)                         |
+ * |                                                               |
+ * |               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |               |
+ * +-+-+-+-+-+-+-+-+
+ */
+                       //move_from_unaligned32(v32, option + 4 + 4);
+                       //*new_env() = xasprintf("lease=%u", (unsigned)v32);
+
+                       sprint_nip6(ipv6str, option + 4 + 4 + 1);
+                       *new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4]));
+               }
+               option += 4 + option[3];
+               len_m4 -= 4 + option[3];
+       }
+}
+
+static char **fill_envp(struct d6_packet *packet)
+{
+       char **envp, **curr;
+
+       client6_data.env_ptr = NULL;
+       client6_data.env_idx = 0;
+
+       *new_env() = xasprintf("interface=%s", client_config.interface);
+
+       if (packet)
+               option_to_env(packet->d6_options, packet->d6_options + sizeof(packet->d6_options));
+
+       envp = curr = client6_data.env_ptr;
+       while (*curr)
+               putenv(*curr++);
+
+       return envp;
+}
+
+/* Call a script with a par file and env vars */
+static void d6_run_script(struct d6_packet *packet, const char *name)
+{
+       char **envp, **curr;
+       char *argv[3];
+
+       envp = fill_envp(packet);
+
+       /* call script */
+       log1("Executing %s %s", client_config.script, name);
+       argv[0] = (char*) client_config.script;
+       argv[1] = (char*) name;
+       argv[2] = NULL;
+       spawn_and_wait(argv);
+
+       for (curr = envp; *curr; curr++) {
+               log2(" %s", *curr);
+               bb_unsetenv_and_free(*curr);
+       }
+       free(envp);
+}
+
+
+/*** Sending/receiving packets ***/
+
+static ALWAYS_INLINE uint32_t random_xid(void)
+{
+       uint32_t t = rand() & htonl(0x00ffffff);
+       return t;
+}
+
+/* Initialize the packet with the proper defaults */
+static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid)
+{
+       struct d6_option *clientid;
+
+       memset(packet, 0, sizeof(*packet));
+
+       packet->d6_xid32 = xid;
+       packet->d6_msg_type = type;
+
+       clientid = (void*)client_config.clientid;
+       return d6_store_blob(packet->d6_options, clientid, clientid->len + 2+2);
+}
+
+static uint8_t *add_d6_client_options(uint8_t *ptr)
+{
+       return ptr;
+       //uint8_t c;
+       //int i, end, len;
+
+       /* Add a "param req" option with the list of options we'd like to have
+        * from stubborn DHCP servers. Pull the data from the struct in common.c.
+        * No bounds checking because it goes towards the head of the packet. */
+       //...
+
+       /* Add -x options if any */
+       //...
+}
+
+static int d6_mcast_from_client_config_ifindex(struct d6_packet *packet, uint8_t *end)
+{
+       static const uint8_t FF02__1_2[16] = {
+               0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,
+       };
+
+       return d6_send_raw_packet(
+               packet, (end - (uint8_t*) packet),
+               /*src*/ NULL, CLIENT_PORT6,
+               /*dst*/ (struct in6_addr*)FF02__1_2, SERVER_PORT6, MAC_BCAST_ADDR,
+               client_config.ifindex
+       );
+}
+
+/* Milticast a DHCPv6 Solicit packet to the network, with an optionally requested IP.
+ *
+ * RFC 3315 17.1.1. Creation of Solicit Messages
+ *
+ * The client MUST include a Client Identifier option to identify itself
+ * to the server.  The client includes IA options for any IAs to which
+ * it wants the server to assign addresses.  The client MAY include
+ * addresses in the IAs as a hint to the server about addresses for
+ * which the client has a preference. ...
+ *
+ * The client uses IA_NA options to request the assignment of non-
+ * temporary addresses and uses IA_TA options to request the assignment
+ * of temporary addresses.  Either IA_NA or IA_TA options, or a
+ * combination of both, can be included in DHCP messages.
+ *
+ * The client SHOULD include an Option Request option (see section 22.7)
+ * to indicate the options the client is interested in receiving.  The
+ * client MAY additionally include instances of those options that are
+ * identified in the Option Request option, with data values as hints to
+ * the server about parameter values the client would like to have
+ * returned.
+ *
+ * The client includes a Reconfigure Accept option (see section 22.20)
+ * if the client is willing to accept Reconfigure messages from the
+ * server.
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |        OPTION_CLIENTID        |          option-len           |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      .                                                               .
+      .                              DUID                             .
+      .                        (variable length)                      .
+      .                                                               .
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |          OPTION_IA_NA         |          option-len           |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                        IAID (4 octets)                        |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                              T1                               |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                              T2                               |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                                                               |
+      .                         IA_NA-options                         .
+      .                                                               .
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |          OPTION_IAADDR        |          option-len           |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                                                               |
+      |                         IPv6 address                          |
+      |                                                               |
+      |                                                               |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                      preferred-lifetime                       |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                        valid-lifetime                         |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      .                                                               .
+      .                        IAaddr-options                         .
+      .                                                               .
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |           OPTION_ORO          |           option-len          |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |    requested-option-code-1    |    requested-option-code-2    |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                              ...                              |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     OPTION_RECONF_ACCEPT      |               0               |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/* NOINLINE: limit stack usage in caller */
+static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ipv6)
+{
+       struct d6_packet packet;
+       uint8_t *opt_ptr;
+       unsigned len;
+
+       /* Fill in: msg type, client id */
+       opt_ptr = init_d6_packet(&packet, D6_MSG_SOLICIT, xid);
+
+       /* Create new IA_NA, optionally with included IAADDR with requested IP */
+       free(client6_data.ia_na);
+       len = requested_ipv6 ? 2+2+4+4+4 + 2+2+16+4+4 : 2+2+4+4+4;
+       client6_data.ia_na = xzalloc(len);
+       client6_data.ia_na->code = D6_OPT_IA_NA;
+       client6_data.ia_na->len = len - 4;
+       *(uint32_t*)client6_data.ia_na->data = rand(); /* IAID */
+       if (requested_ipv6) {
+               struct d6_option *iaaddr = (void*)(client6_data.ia_na->data + 4+4+4);
+               iaaddr->code = D6_OPT_IAADDR;
+               iaaddr->len = 16+4+4;
+               memcpy(iaaddr->data, requested_ipv6, 16);
+       }
+       opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, len);
+
+       /* Add options:
+        * "param req" option according to -O, options specified with -x
+        */
+       opt_ptr = add_d6_client_options(opt_ptr);
+
+       bb_info_msg("Sending discover...");
+       return d6_mcast_from_client_config_ifindex(&packet, opt_ptr);
+}
+
+/* Multicast a DHCPv6 request message
+ *
+ * RFC 3315 18.1.1. Creation and Transmission of Request Messages
+ *
+ * The client uses a Request message to populate IAs with addresses and
+ * obtain other configuration information.  The client includes one or
+ * more IA options in the Request message.  The server then returns
+ * addresses and other information about the IAs to the client in IA
+ * options in a Reply message.
+ *
+ * The client generates a transaction ID and inserts this value in the
+ * "transaction-id" field.
+ *
+ * The client places the identifier of the destination server in a
+ * Server Identifier option.
+ *
+ * The client MUST include a Client Identifier option to identify itself
+ * to the server.  The client adds any other appropriate options,
+ * including one or more IA options (if the client is requesting that
+ * the server assign it some network addresses).
+ *
+ * The client MUST include an Option Request option (see section 22.7)
+ * to indicate the options the client is interested in receiving.  The
+ * client MAY include options with data values as hints to the server
+ * about parameter values the client would like to have returned.
+ *
+ * The client includes a Reconfigure Accept option (see section 22.20)
+ * indicating whether or not the client is willing to accept Reconfigure
+ * messages from the server.
+ */
+/* NOINLINE: limit stack usage in caller */
+static NOINLINE int send_d6_select(uint32_t xid)
+{
+       struct d6_packet packet;
+       uint8_t *opt_ptr;
+
+       /* Fill in: msg type, client id */
+       opt_ptr = init_d6_packet(&packet, D6_MSG_REQUEST, xid);
+
+       /* server id */
+       opt_ptr = d6_store_blob(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
+       /* IA NA (contains requested IP) */
+       opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
+
+       /* Add options:
+        * "param req" option according to -O, options specified with -x
+        */
+       opt_ptr = add_d6_client_options(opt_ptr);
+
+       bb_info_msg("Sending select...");
+       return d6_mcast_from_client_config_ifindex(&packet, opt_ptr);
+}
+
+/* Unicast or broadcast a DHCP renew message
+ *
+ * RFC 3315 18.1.3. Creation and Transmission of Renew Messages
+ *
+ * To extend the valid and preferred lifetimes for the addresses
+ * associated with an IA, the client sends a Renew message to the server
+ * from which the client obtained the addresses in the IA containing an
+ * IA option for the IA.  The client includes IA Address options in the
+ * IA option for the addresses associated with the IA.  The server
+ * determines new lifetimes for the addresses in the IA according to the
+ * administrative configuration of the server.  The server may also add
+ * new addresses to the IA.  The server may remove addresses from the IA
+ * by setting the preferred and valid lifetimes of those addresses to
+ * zero.
+ *
+ * The server controls the time at which the client contacts the server
+ * to extend the lifetimes on assigned addresses through the T1 and T2
+ * parameters assigned to an IA.
+ *
+ * At time T1 for an IA, the client initiates a Renew/Reply message
+ * exchange to extend the lifetimes on any addresses in the IA.  The
+ * client includes an IA option with all addresses currently assigned to
+ * the IA in its Renew message.
+ *
+ * If T1 or T2 is set to 0 by the server (for an IA_NA) or there are no
+ * T1 or T2 times (for an IA_TA), the client may send a Renew or Rebind
+ * message, respectively, at the client's discretion.
+ *
+ * The client sets the "msg-type" field to RENEW.  The client generates
+ * a transaction ID and inserts this value in the "transaction-id"
+ * field.
+ *
+ * The client places the identifier of the destination server in a
+ * Server Identifier option.
+ *
+ * The client MUST include a Client Identifier option to identify itself
+ * to the server.  The client adds any appropriate options, including
+ * one or more IA options.  The client MUST include the list of
+ * addresses the client currently has associated with the IAs in the
+ * Renew message.
+ *
+ * The client MUST include an Option Request option (see section 22.7)
+ * to indicate the options the client is interested in receiving.  The
+ * client MAY include options with data values as hints to the server
+ * about parameter values the client would like to have returned.
+ */
+/* NOINLINE: limit stack usage in caller */
+static NOINLINE int send_d6_renew(uint32_t xid, struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
+{
+       struct d6_packet packet;
+       uint8_t *opt_ptr;
+
+       /* Fill in: msg type, client id */
+       opt_ptr = init_d6_packet(&packet, DHCPREQUEST, xid);
+
+       /* server id */
+       opt_ptr = d6_store_blob(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
+       /* IA NA (contains requested IP) */
+       opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
+
+       /* Add options:
+        * "param req" option according to -O, options specified with -x
+        */
+       opt_ptr = add_d6_client_options(opt_ptr);
+
+       bb_info_msg("Sending renew...");
+       if (server_ipv6)
+               return d6_send_kernel_packet(
+                       &packet, (opt_ptr - (uint8_t*) &packet),
+                       our_cur_ipv6, CLIENT_PORT6,
+                       server_ipv6, SERVER_PORT6
+               );
+       return d6_mcast_from_client_config_ifindex(&packet, opt_ptr);
+}
+
+/* Unicast a DHCP release message */
+static int send_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
+{
+       struct d6_packet packet;
+       uint8_t *opt_ptr;
+
+       /* Fill in: msg type, client id */
+       opt_ptr = init_d6_packet(&packet, D6_MSG_RELEASE, random_xid());
+       /* server id */
+       opt_ptr = d6_store_blob(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
+       /* IA NA (contains our current IP) */
+       opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
+
+       bb_info_msg("Sending release...");
+       return d6_send_kernel_packet(
+               &packet, (opt_ptr - (uint8_t*) &packet),
+               our_cur_ipv6, CLIENT_PORT6,
+               server_ipv6, SERVER_PORT6
+       );
+}
+
+/* Returns -1 on errors that are fatal for the socket, -2 for those that aren't */
+/* NOINLINE: limit stack usage in caller */
+static NOINLINE int d6_recv_raw_packet(struct in6_addr *peer_ipv6
+       UNUSED_PARAM
+       , struct d6_packet *d6_pkt, int fd)
+{
+       int bytes;
+       struct ip6_udp_d6_packet packet;
+
+       bytes = safe_read(fd, &packet, sizeof(packet));
+       if (bytes < 0) {
+               log1("Packet read error, ignoring");
+               /* NB: possible down interface, etc. Caller should pause. */
+               return bytes; /* returns -1 */
+       }
+
+       if (bytes < (int) (sizeof(packet.ip6) + sizeof(packet.udp))) {
+               log1("Packet is too short, ignoring");
+               return -2;
+       }
+
+       if (bytes < sizeof(packet.ip6) + ntohs(packet.ip6.ip6_plen)) {
+               /* packet is bigger than sizeof(packet), we did partial read */
+               log1("Oversized packet, ignoring");
+               return -2;
+       }
+
+       /* ignore any extra garbage bytes */
+       bytes = sizeof(packet.ip6) + ntohs(packet.ip6.ip6_plen);
+
+       /* make sure its the right packet for us, and that it passes sanity checks */
+       if (packet.ip6.ip6_nxt != IPPROTO_UDP
+        || (packet.ip6.ip6_vfc >> 4) != 6
+        || packet.udp.dest != htons(CLIENT_PORT6)
+       /* || bytes > (int) sizeof(packet) - can't happen */
+        || packet.udp.len != packet.ip6.ip6_plen
+       ) {
+               log1("Unrelated/bogus packet, ignoring");
+               return -2;
+       }
+
+//How to do this for ipv6?
+//     /* verify UDP checksum. IP header has to be modified for this */
+//     memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
+//     /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
+//     packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
+//     check = packet.udp.check;
+//     packet.udp.check = 0;
+//     if (check && check != inet_cksum((uint16_t *)&packet, bytes)) {
+//             log1("Packet with bad UDP checksum received, ignoring");
+//             return -2;
+//     }
+
+       log1("Received a packet");
+       d6_dump_packet(&packet.data);
+
+       bytes -= sizeof(packet.ip6) + sizeof(packet.udp);
+       memcpy(d6_pkt, &packet.data, bytes);
+       return bytes;
+}
+
+
+/*** Main ***/
+
+static int sockfd = -1;
+
+#define LISTEN_NONE   0
+#define LISTEN_KERNEL 1
+#define LISTEN_RAW    2
+static smallint listen_mode;
+
+/* initial state: (re)start DHCP negotiation */
+#define INIT_SELECTING  0
+/* discover was sent, DHCPOFFER reply received */
+#define REQUESTING      1
+/* select/renew was sent, DHCPACK reply received */
+#define BOUND           2
+/* half of lease passed, want to renew it by sending unicast renew requests */
+#define RENEWING        3
+/* renew requests were not answered, lease is almost over, send broadcast renew */
+#define REBINDING       4
+/* manually requested renew (SIGUSR1) */
+#define RENEW_REQUESTED 5
+/* release, possibly manually requested (SIGUSR2) */
+#define RELEASED        6
+static smallint state;
+
+static int d6_raw_socket(int ifindex)
+{
+       int fd;
+       struct sockaddr_ll sock;
+
+       /*
+        * Comment:
+        *
+        *      I've selected not to see LL header, so BPF doesn't see it, too.
+        *      The filter may also pass non-IP and non-ARP packets, but we do
+        *      a more complete check when receiving the message in userspace.
+        *
+        * and filter shamelessly stolen from:
+        *
+        *      http://www.flamewarmaster.de/software/dhcpclient/
+        *
+        * There are a few other interesting ideas on that page (look under
+        * "Motivation").  Use of netlink events is most interesting.  Think
+        * of various network servers listening for events and reconfiguring.
+        * That would obsolete sending HUP signals and/or make use of restarts.
+        *
+        * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
+        * License: GPL v2.
+        *
+        * TODO: make conditional?
+        */
+#if 0
+       static const struct sock_filter filter_instr[] = {
+               /* load 9th byte (protocol) */
+               BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
+               /* jump to L1 if it is IPPROTO_UDP, else to L4 */
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6),
+               /* L1: load halfword from offset 6 (flags and frag offset) */
+               BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6),
+               /* jump to L4 if any bits in frag offset field are set, else to L2 */
+               BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0),
+               /* L2: skip IP header (load index reg with header len) */
+               BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0),
+               /* load udp destination port from halfword[header_len + 2] */
+               BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2),
+               /* jump to L3 if udp dport is CLIENT_PORT6, else to L4 */
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1),
+               /* L3: accept packet */
+               BPF_STMT(BPF_RET|BPF_K, 0xffffffff),
+               /* L4: discard packet */
+               BPF_STMT(BPF_RET|BPF_K, 0),
+       };
+       static const struct sock_fprog filter_prog = {
+               .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
+               /* casting const away: */
+               .filter = (struct sock_filter *) filter_instr,
+       };
+#endif
+
+       log1("Opening raw socket on ifindex %d", ifindex); //log2?
+
+       fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6));
+       log1("Got raw socket fd %d", fd); //log2?
+
+       sock.sll_family = AF_PACKET;
+       sock.sll_protocol = htons(ETH_P_IPV6);
+       sock.sll_ifindex = ifindex;
+       xbind(fd, (struct sockaddr *) &sock, sizeof(sock));
+
+#if 0
+       if (CLIENT_PORT6 == 546) {
+               /* Use only if standard port is in use */
+               /* Ignoring error (kernel may lack support for this) */
+               if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
+                               sizeof(filter_prog)) >= 0)
+                       log1("Attached filter to raw socket fd %d", fd); // log?
+       }
+#endif
+
+       log1("Created raw socket");
+
+       return fd;
+}
+
+static void change_listen_mode(int new_mode)
+{
+       log1("Entering listen mode: %s",
+               new_mode != LISTEN_NONE
+                       ? (new_mode == LISTEN_KERNEL ? "kernel" : "raw")
+                       : "none"
+       );
+
+       listen_mode = new_mode;
+       if (sockfd >= 0) {
+               close(sockfd);
+               sockfd = -1;
+       }
+       if (new_mode == LISTEN_KERNEL)
+               sockfd = udhcp_listen_socket(/*INADDR_ANY,*/ CLIENT_PORT6, client_config.interface);
+       else if (new_mode != LISTEN_NONE)
+               sockfd = d6_raw_socket(client_config.ifindex);
+       /* else LISTEN_NONE: sockfd stays closed */
+}
+
+/* Called only on SIGUSR1 */
+static void perform_renew(void)
+{
+       bb_info_msg("Performing a DHCP renew");
+       switch (state) {
+       case BOUND:
+               change_listen_mode(LISTEN_KERNEL);
+       case RENEWING:
+       case REBINDING:
+               state = RENEW_REQUESTED;
+               break;
+       case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
+               d6_run_script(NULL, "deconfig");
+       case REQUESTING:
+       case RELEASED:
+               change_listen_mode(LISTEN_RAW);
+               state = INIT_SELECTING;
+               break;
+       case INIT_SELECTING:
+               break;
+       }
+}
+
+static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
+{
+       /* send release packet */
+       if (state == BOUND || state == RENEWING || state == REBINDING) {
+               bb_info_msg("Unicasting a release");
+               send_d6_release(server_ipv6, our_cur_ipv6); /* unicast */
+               d6_run_script(NULL, "deconfig");
+       }
+       bb_info_msg("Entering released state");
+
+       change_listen_mode(LISTEN_NONE);
+       state = RELEASED;
+}
+
+///static uint8_t* alloc_dhcp_option(int code, const char *str, int extra)
+///{
+///    uint8_t *storage;
+///    int len = strnlen(str, 255);
+///    storage = xzalloc(len + extra + OPT_DATA);
+///    storage[OPT_CODE] = code;
+///    storage[OPT_LEN] = len + extra;
+///    memcpy(storage + extra + OPT_DATA, str, len);
+///    return storage;
+///}
+
+#if BB_MMU
+static void client_background(void)
+{
+       bb_daemonize(0);
+       logmode &= ~LOGMODE_STDIO;
+       /* rewrite pidfile, as our pid is different now */
+       write_pidfile(client_config.pidfile);
+}
+#endif
+
+//usage:#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
+//usage:# define IF_UDHCP_VERBOSE(...) __VA_ARGS__
+//usage:#else
+//usage:# define IF_UDHCP_VERBOSE(...)
+//usage:#endif
+//usage:#define udhcpc6_trivial_usage
+//usage:       "[-fbnq"IF_UDHCP_VERBOSE("v")"oR] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]\n"
+//usage:       "       [-x OPT:VAL]... [-O OPT]..." IF_FEATURE_UDHCP_PORT(" [-P N]")
+//usage:#define udhcpc6_full_usage "\n"
+//usage:       IF_LONG_OPTS(
+//usage:     "\n       -i,--interface IFACE    Interface to use (default eth0)"
+//usage:     "\n       -p,--pidfile FILE       Create pidfile"
+//usage:     "\n       -s,--script PROG        Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")"
+//usage:     "\n       -B,--broadcast          Request broadcast replies"
+//usage:     "\n       -t,--retries N          Send up to N discover packets"
+//usage:     "\n       -T,--timeout N          Pause between packets (default 3 seconds)"
+//usage:     "\n       -A,--tryagain N         Wait N seconds after failure (default 20)"
+//usage:     "\n       -f,--foreground         Run in foreground"
+//usage:       USE_FOR_MMU(
+//usage:     "\n       -b,--background         Background if lease is not obtained"
+//usage:       )
+//usage:     "\n       -n,--now                Exit if lease is not obtained"
+//usage:     "\n       -q,--quit               Exit after obtaining lease"
+//usage:     "\n       -R,--release            Release IP on exit"
+//usage:     "\n       -S,--syslog             Log to syslog too"
+//usage:       IF_FEATURE_UDHCP_PORT(
+//usage:     "\n       -P,--client-port N      Use port N (default 546)"
+//usage:       )
+////usage:     IF_FEATURE_UDHCPC_ARPING(
+////usage:     "\n     -a,--arping             Use arping to validate offered address"
+////usage:     )
+//usage:     "\n       -O,--request-option OPT Request option OPT from server (cumulative)"
+//usage:     "\n       -o,--no-default-options Don't request any options (unless -O is given)"
+//usage:     "\n       -r,--request IP         Request this IP address"
+//usage:     "\n       -x OPT:VAL              Include option OPT in sent packets (cumulative)"
+//usage:     "\n                               Examples of string, numeric, and hex byte opts:"
+//usage:     "\n                               -x hostname:bbox - option 12"
+//usage:     "\n                               -x lease:3600 - option 51 (lease time)"
+//usage:     "\n                               -x 0x3d:0100BEEFC0FFEE - option 61 (client id)"
+//usage:       IF_UDHCP_VERBOSE(
+//usage:     "\n       -v                      Verbose"
+//usage:       )
+//usage:       )
+//usage:       IF_NOT_LONG_OPTS(
+//usage:     "\n       -i IFACE        Interface to use (default eth0)"
+//usage:     "\n       -p FILE         Create pidfile"
+//usage:     "\n       -s PROG         Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")"
+//usage:     "\n       -B              Request broadcast replies"
+//usage:     "\n       -t N            Send up to N discover packets"
+//usage:     "\n       -T N            Pause between packets (default 3 seconds)"
+//usage:     "\n       -A N            Wait N seconds (default 20) after failure"
+//usage:     "\n       -f              Run in foreground"
+//usage:       USE_FOR_MMU(
+//usage:     "\n       -b              Background if lease is not obtained"
+//usage:       )
+//usage:     "\n       -n              Exit if lease is not obtained"
+//usage:     "\n       -q              Exit after obtaining lease"
+//usage:     "\n       -R              Release IP on exit"
+//usage:     "\n       -S              Log to syslog too"
+//usage:       IF_FEATURE_UDHCP_PORT(
+//usage:     "\n       -P N            Use port N (default 546)"
+//usage:       )
+////usage:     IF_FEATURE_UDHCPC_ARPING(
+////usage:     "\n     -a              Use arping to validate offered address"
+////usage:     )
+//usage:     "\n       -O OPT          Request option OPT from server (cumulative)"
+//usage:     "\n       -o              Don't request any options (unless -O is given)"
+//usage:     "\n       -r IP           Request this IP address"
+//usage:     "\n       -x OPT:VAL      Include option OPT in sent packets (cumulative)"
+//usage:     "\n                       Examples of string, numeric, and hex byte opts:"
+//usage:     "\n                       -x hostname:bbox - option 12"
+//usage:     "\n                       -x lease:3600 - option 51 (lease time)"
+//usage:     "\n                       -x 0x3d:0100BEEFC0FFEE - option 61 (client id)"
+//usage:       IF_UDHCP_VERBOSE(
+//usage:     "\n       -v              Verbose"
+//usage:       )
+//usage:       )
+//usage:     "\nSignals:"
+//usage:     "\n       USR1    Renew lease"
+//usage:     "\n       USR2    Release lease"
+
+
+int udhcpc6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
+{
+       const char *str_r;
+       IF_FEATURE_UDHCP_PORT(char *str_P;)
+       void *clientid_mac_ptr;
+       llist_t *list_O = NULL;
+       llist_t *list_x = NULL;
+       int tryagain_timeout = 20;
+       int discover_timeout = 3;
+       int discover_retries = 3;
+       struct in6_addr srv6_buf;
+       struct in6_addr ipv6_buf;
+       struct in6_addr *requested_ipv6;
+       uint32_t xid = 0;
+       int packet_num;
+       int timeout; /* must be signed */
+       unsigned already_waited_sec;
+       unsigned opt;
+       int max_fd;
+       int retval;
+       fd_set rfds;
+
+       /* Default options */
+       IF_FEATURE_UDHCP_PORT(SERVER_PORT6 = 547;)
+       IF_FEATURE_UDHCP_PORT(CLIENT_PORT6 = 546;)
+       client_config.interface = "eth0";
+       client_config.script = CONFIG_UDHCPC_DEFAULT_SCRIPT;
+
+       /* Parse command line */
+       /* O,x: list; -T,-t,-A take numeric param */
+       opt_complementary = "O::x::T+:t+:A+" IF_UDHCP_VERBOSE(":vv") ;
+       IF_LONG_OPTS(applet_long_options = udhcpc6_longopts;)
+       opt = getopt32(argv, "i:np:qRr:s:T:t:SA:O:ox:f"
+               USE_FOR_MMU("b")
+               ///IF_FEATURE_UDHCPC_ARPING("a")
+               IF_FEATURE_UDHCP_PORT("P:")
+               "v"
+               , &client_config.interface, &client_config.pidfile, &str_r /* i,p */
+               , &client_config.script /* s */
+               , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */
+               , &list_O
+               , &list_x
+               IF_FEATURE_UDHCP_PORT(, &str_P)
+               IF_UDHCP_VERBOSE(, &dhcp_verbose)
+       );
+       requested_ipv6 = NULL;
+       if (opt & OPT_r) {
+               if (inet_pton(AF_INET6, str_r, &ipv6_buf) <= 0)
+                       bb_error_msg_and_die("bad IPv6 address '%s'", str_r);
+               requested_ipv6 = &ipv6_buf;
+       }
+#if ENABLE_FEATURE_UDHCP_PORT
+       if (opt & OPT_P) {
+               CLIENT_PORT6 = xatou16(str_P);
+               SERVER_PORT6 = CLIENT_PORT6 + 1;
+       }
+#endif
+       while (list_O) {
+               char *optstr = llist_pop(&list_O);
+               unsigned n = bb_strtou(optstr, NULL, 0);
+               if (errno || n > 254) {
+                       n = udhcp_option_idx(optstr);
+                       n = dhcp_optflags[n].code;
+               }
+               client_config.opt_mask[n >> 3] |= 1 << (n & 7);
+       }
+       if (!(opt & OPT_o)) {
+               /*
+               unsigned i, n;
+               for (i = 0; (n = dhcp_optflags[i].code) != 0; i++) {
+                       if (dhcp_optflags[i].flags & OPTION_REQ) {
+                               client_config.opt_mask[n >> 3] |= 1 << (n & 7);
+                       }
+               }
+               */
+       }
+       while (list_x) {
+               char *optstr = llist_pop(&list_x);
+               char *colon = strchr(optstr, ':');
+               if (colon)
+                       *colon = ' ';
+               /* now it looks similar to udhcpd's config file line:
+                * "optname optval", using the common routine: */
+               udhcp_str2optset(optstr, &client_config.options);
+       }
+
+       if (udhcp_read_interface(client_config.interface,
+                       &client_config.ifindex,
+                       NULL,
+                       client_config.client_mac)
+       ) {
+               return 1;
+       }
+
+       /* Create client ID based on mac, set clientid_mac_ptr */
+       {
+               struct d6_option *clientid;
+               clientid = xzalloc(2+2+2+2+6);
+               clientid->code = D6_OPT_CLIENTID;
+               clientid->len = 2+2+6;
+               clientid->data[1] = 3; /* DUID-LL */
+               clientid->data[3] = 1; /* ethernet */
+               clientid_mac_ptr = clientid->data + 2+2;
+               memcpy(clientid_mac_ptr, client_config.client_mac, 6);
+               client_config.clientid = (void*)clientid;
+       }
+
+#if !BB_MMU
+       /* on NOMMU reexec (i.e., background) early */
+       if (!(opt & OPT_f)) {
+               bb_daemonize_or_rexec(0 /* flags */, argv);
+               logmode = LOGMODE_NONE;
+       }
+#endif
+       if (opt & OPT_S) {
+               openlog(applet_name, LOG_PID, LOG_DAEMON);
+               logmode |= LOGMODE_SYSLOG;
+       }
+
+       /* Make sure fd 0,1,2 are open */
+       bb_sanitize_stdio();
+       /* Equivalent of doing a fflush after every \n */
+       setlinebuf(stdout);
+       /* Create pidfile */
+       write_pidfile(client_config.pidfile);
+       /* Goes to stdout (unless NOMMU) and possibly syslog */
+       bb_info_msg("%s (v"BB_VER") started", applet_name);
+       /* Set up the signal pipe */
+       udhcp_sp_setup();
+       /* We want random_xid to be random... */
+       srand(monotonic_us());
+
+       state = INIT_SELECTING;
+       d6_run_script(NULL, "deconfig");
+       change_listen_mode(LISTEN_RAW);
+       packet_num = 0;
+       timeout = 0;
+       already_waited_sec = 0;
+
+       /* Main event loop. select() waits on signal pipe and possibly
+        * on sockfd.
+        * "continue" statements in code below jump to the top of the loop.
+        */
+       for (;;) {
+               struct timeval tv;
+               struct d6_packet packet;
+               uint8_t *packet_end;
+               /* silence "uninitialized!" warning */
+               unsigned timestamp_before_wait = timestamp_before_wait;
+
+               //bb_error_msg("sockfd:%d, listen_mode:%d", sockfd, listen_mode);
+
+               /* Was opening raw or udp socket here
+                * if (listen_mode != LISTEN_NONE && sockfd < 0),
+                * but on fast network renew responses return faster
+                * than we open sockets. Thus this code is moved
+                * to change_listen_mode(). Thus we open listen socket
+                * BEFORE we send renew request (see "case BOUND:"). */
+
+               max_fd = udhcp_sp_fd_set(&rfds, sockfd);
+
+               tv.tv_sec = timeout - already_waited_sec;
+               tv.tv_usec = 0;
+               retval = 0;
+               /* If we already timed out, fall through with retval = 0, else... */
+               if ((int)tv.tv_sec > 0) {
+                       log1("Waiting on select %u seconds", (int)tv.tv_sec);
+                       timestamp_before_wait = (unsigned)monotonic_sec();
+                       retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
+                       if (retval < 0) {
+                               /* EINTR? A signal was caught, don't panic */
+                               if (errno == EINTR) {
+                                       already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait;
+                                       continue;
+                               }
+                               /* Else: an error occured, panic! */
+                               bb_perror_msg_and_die("select");
+                       }
+               }
+
+               /* If timeout dropped to zero, time to become active:
+                * resend discover/renew/whatever
+                */
+               if (retval == 0) {
+                       /* When running on a bridge, the ifindex may have changed
+                        * (e.g. if member interfaces were added/removed
+                        * or if the status of the bridge changed).
+                        * Refresh ifindex and client_mac:
+                        */
+                       if (udhcp_read_interface(client_config.interface,
+                                       &client_config.ifindex,
+                                       NULL,
+                                       client_config.client_mac)
+                       ) {
+                               goto ret0; /* iface is gone? */
+                       }
+                       memcpy(clientid_mac_ptr, client_config.client_mac, 6);
+
+                       /* We will restart the wait in any case */
+                       already_waited_sec = 0;
+
+                       switch (state) {
+                       case INIT_SELECTING:
+                               if (!discover_retries || packet_num < discover_retries) {
+                                       if (packet_num == 0)
+                                               xid = random_xid();
+                                       /* multicast */
+                                       send_d6_discover(xid, requested_ipv6);
+                                       timeout = discover_timeout;
+                                       packet_num++;
+                                       continue;
+                               }
+ leasefail:
+                               d6_run_script(NULL, "leasefail");
+#if BB_MMU /* -b is not supported on NOMMU */
+                               if (opt & OPT_b) { /* background if no lease */
+                                       bb_info_msg("No lease, forking to background");
+                                       client_background();
+                                       /* do not background again! */
+                                       opt = ((opt & ~OPT_b) | OPT_f);
+                               } else
+#endif
+                               if (opt & OPT_n) { /* abort if no lease */
+                                       bb_info_msg("No lease, failing");
+                                       retval = 1;
+                                       goto ret;
+                               }
+                               /* wait before trying again */
+                               timeout = tryagain_timeout;
+                               packet_num = 0;
+                               continue;
+                       case REQUESTING:
+                               if (!discover_retries || packet_num < discover_retries) {
+                                       /* send multicast select packet */
+                                       send_d6_select(xid);
+                                       timeout = discover_timeout;
+                                       packet_num++;
+                                       continue;
+                               }
+                               /* Timed out, go back to init state.
+                                * "discover...select...discover..." loops
+                                * were seen in the wild. Treat them similarly
+                                * to "no response to discover" case */
+                               change_listen_mode(LISTEN_RAW);
+                               state = INIT_SELECTING;
+                               goto leasefail;
+                       case BOUND:
+                               /* 1/2 lease passed, enter renewing state */
+                               state = RENEWING;
+                               client_config.first_secs = 0; /* make secs field count from 0 */
+                               change_listen_mode(LISTEN_KERNEL);
+                               log1("Entering renew state");
+                               /* fall right through */
+                       case RENEW_REQUESTED: /* manual (SIGUSR1) renew */
+                       case_RENEW_REQUESTED:
+                       case RENEWING:
+                               if (timeout > 60) {
+                                       /* send an unicast renew request */
+                       /* Sometimes observed to fail (EADDRNOTAVAIL) to bind
+                        * a new UDP socket for sending inside send_renew.
+                        * I hazard to guess existing listening socket
+                        * is somehow conflicting with it, but why is it
+                        * not deterministic then?! Strange.
+                        * Anyway, it does recover by eventually failing through
+                        * into INIT_SELECTING state.
+                        */
+                                       send_d6_renew(xid, &srv6_buf, requested_ipv6);
+                                       timeout >>= 1;
+                                       continue;
+                               }
+                               /* Timed out, enter rebinding state */
+                               log1("Entering rebinding state");
+                               state = REBINDING;
+                               /* fall right through */
+                       case REBINDING:
+                               /* Switch to bcast receive */
+                               change_listen_mode(LISTEN_RAW);
+                               /* Lease is *really* about to run out,
+                                * try to find DHCP server using broadcast */
+                               if (timeout > 0) {
+                                       /* send a broadcast renew request */
+                                       send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6);
+                                       timeout >>= 1;
+                                       continue;
+                               }
+                               /* Timed out, enter init state */
+                               bb_info_msg("Lease lost, entering init state");
+                               d6_run_script(NULL, "deconfig");
+                               state = INIT_SELECTING;
+                               client_config.first_secs = 0; /* make secs field count from 0 */
+                               /*timeout = 0; - already is */
+                               packet_num = 0;
+                               continue;
+                       /* case RELEASED: */
+                       }
+                       /* yah, I know, *you* say it would never happen */
+                       timeout = INT_MAX;
+                       continue; /* back to main loop */
+               } /* if select timed out */
+
+               /* select() didn't timeout, something happened */
+
+               /* Is it a signal? */
+               /* note: udhcp_sp_read checks FD_ISSET before reading */
+               switch (udhcp_sp_read(&rfds)) {
+               case SIGUSR1:
+                       client_config.first_secs = 0; /* make secs field count from 0 */
+                       already_waited_sec = 0;
+                       perform_renew();
+                       if (state == RENEW_REQUESTED) {
+                               /* We might be either on the same network
+                                * (in which case renew might work),
+                                * or we might be on a completely different one
+                                * (in which case renew won't ever succeed).
+                                * For the second case, must make sure timeout
+                                * is not too big, or else we can send
+                                * futile renew requests for hours.
+                                * (Ab)use -A TIMEOUT value (usually 20 sec)
+                                * as a cap on the timeout.
+                                */
+                               if (timeout > tryagain_timeout)
+                                       timeout = tryagain_timeout;
+                               goto case_RENEW_REQUESTED;
+                       }
+                       /* Start things over */
+                       packet_num = 0;
+                       /* Kill any timeouts, user wants this to hurry along */
+                       timeout = 0;
+                       continue;
+               case SIGUSR2:
+                       perform_d6_release(&srv6_buf, requested_ipv6);
+                       timeout = INT_MAX;
+                       continue;
+               case SIGTERM:
+                       bb_info_msg("Received SIGTERM");
+                       goto ret0;
+               }
+
+               /* Is it a packet? */
+               if (listen_mode == LISTEN_NONE || !FD_ISSET(sockfd, &rfds))
+                       continue; /* no */
+
+               {
+                       int len;
+
+                       /* A packet is ready, read it */
+                       if (listen_mode == LISTEN_KERNEL)
+                               len = d6_recv_kernel_packet(&srv6_buf, &packet, sockfd);
+                       else
+                               len = d6_recv_raw_packet(&srv6_buf, &packet, sockfd);
+                       if (len == -1) {
+                               /* Error is severe, reopen socket */
+                               bb_info_msg("Read error: %s, reopening socket", strerror(errno));
+                               sleep(discover_timeout); /* 3 seconds by default */
+                               change_listen_mode(listen_mode); /* just close and reopen */
+                       }
+                       /* If this packet will turn out to be unrelated/bogus,
+                        * we will go back and wait for next one.
+                        * Be sure timeout is properly decreased. */
+                       already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait;
+                       if (len < 0)
+                               continue;
+                       packet_end = (uint8_t*)&packet + len;
+               }
+
+               if ((packet.d6_xid32 & htonl(0x00ffffff)) != xid) {
+                       log1("xid %x (our is %x), ignoring packet",
+                               (unsigned)(packet.d6_xid32 & htonl(0x00ffffff)), (unsigned)xid);
+                       continue;
+               }
+
+               switch (state) {
+               case INIT_SELECTING:
+                       if (packet.d6_msg_type == D6_MSG_ADVERTISE)
+                               goto type_is_ok;
+                       /* DHCPv6 has "Rapid Commit", when instead of Advertise,
+                        * server sends Reply right away.
+                        * Fall through to check for this case.
+                        */
+               case REQUESTING:
+               case RENEWING:
+               case RENEW_REQUESTED:
+               case REBINDING:
+                       if (packet.d6_msg_type == D6_MSG_REPLY) {
+                               uint32_t lease_seconds;
+                               struct d6_option *option, *iaaddr;
+ type_is_ok:
+                               option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE);
+                               if (option && option->data[4] != 0) {
+                                       /* return to init state */
+                                       bb_info_msg("Received DHCP NAK (%u)", option->data[4]);
+                                       d6_run_script(&packet, "nak");
+                                       if (state != REQUESTING)
+                                               d6_run_script(NULL, "deconfig");
+                                       change_listen_mode(LISTEN_RAW);
+                                       sleep(3); /* avoid excessive network traffic */
+                                       state = INIT_SELECTING;
+                                       client_config.first_secs = 0; /* make secs field count from 0 */
+                                       requested_ipv6 = NULL;
+                                       timeout = 0;
+                                       packet_num = 0;
+                                       already_waited_sec = 0;
+                                       continue;
+                               }
+                               option = d6_copy_option(packet.d6_options, packet_end, D6_OPT_SERVERID);
+                               if (!option) {
+                                       bb_error_msg("no server ID, ignoring packet");
+                                       continue;
+                                       /* still selecting - this server looks bad */
+                               }
+//Note: we do not bother comparing server IDs in Advertise and Reply msgs.
+//server_id variable is used solely for creation of proper server_id option
+//in outgoing packets. (why DHCPv6 even introduced it is a mystery).
+                               free(client6_data.server_id);
+                               client6_data.server_id = option;
+                               if (packet.d6_msg_type == D6_MSG_ADVERTISE) {
+                                       /* enter requesting state */
+                                       state = REQUESTING;
+                                       timeout = 0;
+                                       packet_num = 0;
+                                       already_waited_sec = 0;
+                                       continue;
+                               }
+                               /* It's a D6_MSG_REPLY */
+/*
+ * RFC 3315 18.1.8. Receipt of Reply Messages
+ *
+ * Upon the receipt of a valid Reply message in response to a Solicit
+ * (with a Rapid Commit option), Request, Confirm, Renew, Rebind or
+ * Information-request message, the client extracts the configuration
+ * information contained in the Reply.  The client MAY choose to report
+ * any status code or message from the status code option in the Reply
+ * message.
+ *
+ * The client SHOULD perform duplicate address detection [17] on each of
+ * the addresses in any IAs it receives in the Reply message before
+ * using that address for traffic.  If any of the addresses are found to
+ * be in use on the link, the client sends a Decline message to the
+ * server as described in section 18.1.7.
+ *
+ * If the Reply was received in response to a Solicit (with a Rapid
+ * Commit option), Request, Renew or Rebind message, the client updates
+ * the information it has recorded about IAs from the IA options
+ * contained in the Reply message:
+ *
+ * -  Record T1 and T2 times.
+ *
+ * -  Add any new addresses in the IA option to the IA as recorded by
+ *    the client.
+ *
+ * -  Update lifetimes for any addresses in the IA option that the
+ *    client already has recorded in the IA.
+ *
+ * -  Discard any addresses from the IA, as recorded by the client, that
+ *    have a valid lifetime of 0 in the IA Address option.
+ *
+ * -  Leave unchanged any information about addresses the client has
+ *    recorded in the IA but that were not included in the IA from the
+ *    server.
+ *
+ * Management of the specific configuration information is detailed in
+ * the definition of each option in section 22.
+ *
+ * If the client receives a Reply message with a Status Code containing
+ * UnspecFail, the server is indicating that it was unable to process
+ * the message due to an unspecified failure condition.  If the client
+ * retransmits the original message to the same server to retry the
+ * desired operation, the client MUST limit the rate at which it
+ * retransmits the message and limit the duration of the time during
+ * which it retransmits the message.
+ *
+ * When the client receives a Reply message with a Status Code option
+ * with the value UseMulticast, the client records the receipt of the
+ * message and sends subsequent messages to the server through the
+ * interface on which the message was received using multicast.  The
+ * client resends the original message using multicast.
+ *
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |          OPTION_IA_NA         |          option-len           |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                        IAID (4 octets)                        |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                              T1                               |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                              T2                               |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                                                               |
+ * .                         IA_NA-options                         .
+ * .                                                               .
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |          OPTION_IAADDR        |          option-len           |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                                                               |
+ * |                         IPv6 address                          |
+ * |                                                               |
+ * |                                                               |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                      preferred-lifetime                       |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                        valid-lifetime                         |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * .                                                               .
+ * .                        IAaddr-options                         .
+ * .                                                               .
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+                               free(client6_data.ia_na);
+                               client6_data.ia_na = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_NA);
+                               if (!client6_data.ia_na) {
+                                       bb_error_msg("no %s option, ignoring packet", "IA_NA");
+                                       continue;
+                               }
+                               if (client6_data.ia_na->len < (4 + 4 + 4) + (2 + 2 + 16 + 4 + 4)) {
+                                       bb_error_msg("IA_NA option is too short:%d bytes", client6_data.ia_na->len);
+                                       continue;
+                               }
+                               iaaddr = d6_find_option(client6_data.ia_na->data + 4 + 4 + 4,
+                                               client6_data.ia_na->data + client6_data.ia_na->len,
+                                               D6_OPT_IAADDR
+                               );
+                               if (!iaaddr) {
+                                       bb_error_msg("no %s option, ignoring packet", "IAADDR");
+                                       continue;
+                               }
+                               if (iaaddr->len < (16 + 4 + 4)) {
+                                       bb_error_msg("IAADDR option is too short:%d bytes", iaaddr->len);
+                                       continue;
+                               }
+                               /* Note: the address is sufficiently aligned for cast:
+                                * we _copied_ IA-NA, and copy is always well-aligned.
+                                */
+                               requested_ipv6 = (struct in6_addr*) iaaddr->data;
+                               move_from_unaligned32(lease_seconds, iaaddr->data + 16 + 4);
+                               lease_seconds = ntohl(lease_seconds);
+                               /* paranoia: must not be too small and not prone to overflows */
+                               if (lease_seconds < 0x10)
+                                       lease_seconds = 0x10;
+/// TODO: check for 0 lease time?
+                               if (lease_seconds >= 0x10000000)
+                                       lease_seconds = 0x0fffffff;
+                               /* enter bound state */
+                               timeout = lease_seconds / 2;
+                               bb_info_msg("Lease obtained, lease time %u",
+                                       /*inet_ntoa(temp_addr),*/ (unsigned)lease_seconds);
+                               d6_run_script(&packet, state == REQUESTING ? "bound" : "renew");
+
+                               state = BOUND;
+                               change_listen_mode(LISTEN_NONE);
+                               if (opt & OPT_q) { /* quit after lease */
+                                       goto ret0;
+                               }
+                               /* future renew failures should not exit (JM) */
+                               opt &= ~OPT_n;
+#if BB_MMU /* NOMMU case backgrounded earlier */
+                               if (!(opt & OPT_f)) {
+                                       client_background();
+                                       /* do not background again! */
+                                       opt = ((opt & ~OPT_b) | OPT_f);
+                               }
+#endif
+                               already_waited_sec = 0;
+                               continue; /* back to main loop */
+                       }
+                       continue;
+               /* case BOUND: - ignore all packets */
+               /* case RELEASED: - ignore all packets */
+               }
+               /* back to main loop */
+       } /* for (;;) - main loop ends */
+
+ ret0:
+       if (opt & OPT_R) /* release on quit */
+               perform_d6_release(&srv6_buf, requested_ipv6);
+       retval = 0;
+ ret:
+       /*if (client_config.pidfile) - remove_pidfile has its own check */
+               remove_pidfile(client_config.pidfile);
+       return retval;
+}
diff --git a/networking/udhcp/d6_packet.c b/networking/udhcp/d6_packet.c
new file mode 100644 (file)
index 0000000..79b2946
--- /dev/null
@@ -0,0 +1,172 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2011 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "common.h"
+#include "d6_common.h"
+#include "dhcpd.h"
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netpacket/packet.h>
+
+#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
+void FAST_FUNC d6_dump_packet(struct d6_packet *packet)
+{
+       if (dhcp_verbose < 2)
+               return;
+
+       bb_info_msg(
+               " xid %x"
+               , packet->d6_xid32
+       );
+       //*bin2hex(buf, (void *) packet->chaddr, sizeof(packet->chaddr)) = '\0';
+       //bb_info_msg(" chaddr %s", buf);
+}
+#endif
+
+int FAST_FUNC d6_recv_kernel_packet(struct in6_addr *peer_ipv6
+       UNUSED_PARAM
+       , struct d6_packet *packet, int fd)
+{
+       int bytes;
+
+       memset(packet, 0, sizeof(*packet));
+       bytes = safe_read(fd, packet, sizeof(*packet));
+       if (bytes < 0) {
+               log1("Packet read error, ignoring");
+               return bytes; /* returns -1 */
+       }
+
+       if (bytes < offsetof(struct d6_packet, d6_options)) {
+               bb_info_msg("Packet with bad magic, ignoring");
+               return -2;
+       }
+       log1("Received a packet");
+       d6_dump_packet(packet);
+
+       return bytes;
+}
+
+/* Construct a ipv6+udp header for a packet, send packet */
+int FAST_FUNC d6_send_raw_packet(
+               struct d6_packet *d6_pkt, unsigned d6_pkt_size,
+               struct in6_addr *src_ipv6, int source_port,
+               struct in6_addr *dst_ipv6, int dest_port, const uint8_t *dest_arp,
+               int ifindex)
+{
+       struct sockaddr_ll dest_sll;
+       struct ip6_udp_d6_packet packet;
+       int fd;
+       int result = -1;
+       const char *msg;
+
+       fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6));
+       if (fd < 0) {
+               msg = "socket(%s)";
+               goto ret_msg;
+       }
+
+       memset(&dest_sll, 0, sizeof(dest_sll));
+       memset(&packet, 0, offsetof(struct ip6_udp_d6_packet, data));
+       packet.data = *d6_pkt; /* struct copy */
+
+       dest_sll.sll_family = AF_PACKET;
+       dest_sll.sll_protocol = htons(ETH_P_IPV6);
+       dest_sll.sll_ifindex = ifindex;
+       dest_sll.sll_halen = 6;
+       memcpy(dest_sll.sll_addr, dest_arp, 6);
+
+       if (bind(fd, (struct sockaddr *)&dest_sll, sizeof(dest_sll)) < 0) {
+               msg = "bind(%s)";
+               goto ret_close;
+       }
+
+       packet.ip6.ip6_vfc = (6 << 4); /* 4 bits version, top 4 bits of tclass */
+       if (src_ipv6)
+               packet.ip6.ip6_src = *src_ipv6; /* struct copy */
+       packet.ip6.ip6_dst = *dst_ipv6; /* struct copy */
+       packet.udp.source = htons(source_port);
+       packet.udp.dest = htons(dest_port);
+       /* size, excluding IP header: */
+       packet.udp.len = htons(sizeof(struct udphdr) + d6_pkt_size);
+       packet.ip6.ip6_plen = packet.udp.len;
+       /*
+        * Someone was smoking weed (at least) while inventing UDP checksumming:
+        * UDP checksum skips first four bytes of IPv6 header.
+        * 'next header' field should be summed as if it is one more byte
+        * to the right, therefore we write its value (IPPROTO_UDP)
+        * into ip6_hlim, and its 'real' location remains zero-filled for now.
+        */
+       packet.ip6.ip6_hlim = IPPROTO_UDP;
+       packet.udp.check = inet_cksum(
+                               (uint16_t *)&packet + 2,
+                               offsetof(struct ip6_udp_d6_packet, data) - 4 + d6_pkt_size
+       );
+       /* fix 'hop limit' and 'next header' after UDP checksumming */
+       packet.ip6.ip6_hlim = 1; /* observed Windows machines to use hlim=1 */
+       packet.ip6.ip6_nxt = IPPROTO_UDP;
+
+       d6_dump_packet(d6_pkt);
+       result = sendto(fd, &packet, offsetof(struct ip6_udp_d6_packet, data) + d6_pkt_size,
+                       /*flags:*/ 0,
+                       (struct sockaddr *) &dest_sll, sizeof(dest_sll)
+       );
+       msg = "sendto";
+ ret_close:
+       close(fd);
+       if (result < 0) {
+ ret_msg:
+               bb_perror_msg(msg, "PACKET");
+       }
+       return result;
+}
+
+/* Let the kernel do all the work for packet generation */
+int FAST_FUNC d6_send_kernel_packet(
+               struct d6_packet *d6_pkt, unsigned d6_pkt_size,
+               struct in6_addr *src_ipv6, int source_port,
+               struct in6_addr *dst_ipv6, int dest_port)
+{
+       struct sockaddr_in6 sa;
+       int fd;
+       int result = -1;
+       const char *msg;
+
+       fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+       if (fd < 0) {
+               msg = "socket(%s)";
+               goto ret_msg;
+       }
+       setsockopt_reuseaddr(fd);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sin6_family = AF_INET6;
+       sa.sin6_port = htons(source_port);
+       sa.sin6_addr = *src_ipv6; /* struct copy */
+       if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
+               msg = "bind(%s)";
+               goto ret_close;
+       }
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sin6_family = AF_INET6;
+       sa.sin6_port = htons(dest_port);
+       sa.sin6_addr = *dst_ipv6; /* struct copy */
+       if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
+               msg = "connect";
+               goto ret_close;
+       }
+
+       d6_dump_packet(d6_pkt);
+       result = safe_write(fd, d6_pkt, d6_pkt_size);
+       msg = "write";
+ ret_close:
+       close(fd);
+       if (result < 0) {
+ ret_msg:
+               bb_perror_msg(msg, "UDP");
+       }
+       return result;
+}
diff --git a/networking/udhcp/d6_socket.c b/networking/udhcp/d6_socket.c
new file mode 100644 (file)
index 0000000..56f69f6
--- /dev/null
@@ -0,0 +1,34 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2011 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "common.h"
+#include "d6_common.h"
+#include <net/if.h>
+
+int FAST_FUNC d6_listen_socket(int port, const char *inf)
+{
+       int fd;
+       struct sockaddr_in6 addr;
+
+       log1("Opening listen socket on *:%d %s", port, inf);
+       fd = xsocket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+
+       setsockopt_reuseaddr(fd);
+       if (setsockopt_broadcast(fd) == -1)
+               bb_perror_msg_and_die("SO_BROADCAST");
+
+       /* NB: bug 1032 says this doesn't work on ethernet aliases (ethN:M) */
+       if (setsockopt_bindtodevice(fd, inf))
+               xfunc_die(); /* warning is already printed */
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin6_family = AF_INET6;
+       addr.sin6_port = htons(port);
+       /* addr.sin6_addr is all-zeros */
+       xbind(fd, (struct sockaddr *)&addr, sizeof(addr));
+
+       return fd;
+}
index de1b798..8dee916 100644 (file)
 #include "dhcpd.h"
 #include "dhcpc.h"
 
-#include <asm/types.h>
-#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION)
-# include <netpacket/packet.h>
-# include <net/ethernet.h>
-#else
-# include <linux/if_packet.h>
-# include <linux/if_ether.h>
-#endif
+#include <netinet/if_ether.h>
 #include <linux/filter.h>
+#include <linux/if_packet.h>
+
+/* "struct client_config_t client_config" is in bb_common_bufsiz1 */
 
-/* struct client_config_t client_config is in bb_common_bufsiz1 */
+
+#if ENABLE_LONG_OPTS
+static const char udhcpc_longopts[] ALIGN1 =
+       "clientid-none\0"  No_argument       "C"
+       "vendorclass\0"    Required_argument "V"
+       "hostname\0"       Required_argument "H"
+       "fqdn\0"           Required_argument "F"
+       "interface\0"      Required_argument "i"
+       "now\0"            No_argument       "n"
+       "pidfile\0"        Required_argument "p"
+       "quit\0"           No_argument       "q"
+       "release\0"        No_argument       "R"
+       "request\0"        Required_argument "r"
+       "script\0"         Required_argument "s"
+       "timeout\0"        Required_argument "T"
+       "retries\0"        Required_argument "t"
+       "tryagain\0"       Required_argument "A"
+       "syslog\0"         No_argument       "S"
+       "request-option\0" Required_argument "O"
+       "no-default-options\0" No_argument   "o"
+       "foreground\0"     No_argument       "f"
+       "background\0"     No_argument       "b"
+       "broadcast\0"      No_argument       "B"
+       IF_FEATURE_UDHCPC_ARPING("arping\0"     No_argument       "a")
+       IF_FEATURE_UDHCP_PORT("client-port\0"   Required_argument "P")
+       ;
+#endif
+/* Must match getopt32 option string order */
+enum {
+       OPT_C = 1 << 0,
+       OPT_V = 1 << 1,
+       OPT_H = 1 << 2,
+       OPT_h = 1 << 3,
+       OPT_F = 1 << 4,
+       OPT_i = 1 << 5,
+       OPT_n = 1 << 6,
+       OPT_p = 1 << 7,
+       OPT_q = 1 << 8,
+       OPT_R = 1 << 9,
+       OPT_r = 1 << 10,
+       OPT_s = 1 << 11,
+       OPT_T = 1 << 12,
+       OPT_t = 1 << 13,
+       OPT_S = 1 << 14,
+       OPT_A = 1 << 15,
+       OPT_O = 1 << 16,
+       OPT_o = 1 << 17,
+       OPT_x = 1 << 18,
+       OPT_f = 1 << 19,
+       OPT_B = 1 << 20,
+/* The rest has variable bit positions, need to be clever */
+       OPTBIT_B = 20,
+       USE_FOR_MMU(             OPTBIT_b,)
+       IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
+       IF_FEATURE_UDHCP_PORT(   OPTBIT_P,)
+       USE_FOR_MMU(             OPT_b = 1 << OPTBIT_b,)
+       IF_FEATURE_UDHCPC_ARPING(OPT_a = 1 << OPTBIT_a,)
+       IF_FEATURE_UDHCP_PORT(   OPT_P = 1 << OPTBIT_P,)
+};
 
 
 /*** Script execution code ***/
@@ -45,7 +99,9 @@ static const uint8_t len_of_option_as_string[] = {
        [OPTION_IP              ] = sizeof("255.255.255.255 "),
        [OPTION_IP_PAIR         ] = sizeof("255.255.255.255 ") * 2,
        [OPTION_STATIC_ROUTES   ] = sizeof("255.255.255.255/32 255.255.255.255 "),
+       [OPTION_6RD             ] = sizeof("32 128 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 255.255.255.255 "),
        [OPTION_STRING          ] = 1,
+       [OPTION_STRING_HOST     ] = 1,
 #if ENABLE_FEATURE_UDHCP_RFC3397
        [OPTION_DNS_STRING      ] = 1, /* unused */
        /* Hmmm, this severely overestimates size if SIP_SERVERS option
@@ -80,6 +136,63 @@ static int mton(uint32_t mask)
        return i;
 }
 
+/* Check if a given label represents a valid DNS label
+ * Return pointer to the first character after the label upon success,
+ * NULL otherwise.
+ * See RFC1035, 2.3.1
+ */
+/* We don't need to be particularly anal. For example, allowing _, hyphen
+ * at the end, or leading and trailing dots would be ok, since it
+ * can't be used for attacks. (Leading hyphen can be, if someone uses
+ * cmd "$hostname"
+ * in the script: then hostname may be treated as an option)
+ */
+static const char *valid_domain_label(const char *label)
+{
+       unsigned char ch;
+       unsigned pos = 0;
+
+       for (;;) {
+               ch = *label;
+               if ((ch|0x20) < 'a' || (ch|0x20) > 'z') {
+                       if (pos == 0) {
+                               /* label must begin with letter */
+                               return NULL;
+                       }
+                       if (ch < '0' || ch > '9') {
+                               if (ch == '\0' || ch == '.')
+                                       return label;
+                               /* DNS allows only '-', but we are more permissive */
+                               if (ch != '-' && ch != '_')
+                                       return NULL;
+                       }
+               }
+               label++;
+               pos++;
+               //Do we want this?
+               //if (pos > 63) /* NS_MAXLABEL; labels must be 63 chars or less */
+               //      return NULL;
+       }
+}
+
+/* Check if a given name represents a valid DNS name */
+/* See RFC1035, 2.3.1 */
+static int good_hostname(const char *name)
+{
+       //const char *start = name;
+
+       for (;;) {
+               name = valid_domain_label(name);
+               if (!name)
+                       return 0;
+               if (!name[0])
+                       return 1;
+                       //Do we want this?
+                       //return ((name - start) < 1025); /* NS_MAXDNAME */
+               name++;
+       }
+}
+
 /* Create "opt_name=opt_value" string */
 static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_optflag *optflag, const char *opt_name)
 {
@@ -87,27 +200,25 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
        int len, type, optlen;
        char *dest, *ret;
 
-       /* option points to OPT_DATA, need to go back and get OPT_LEN */
-       len = option[OPT_LEN - OPT_DATA];
+       /* option points to OPT_DATA, need to go back to get OPT_LEN */
+       len = option[-OPT_DATA + OPT_LEN];
+
        type = optflag->flags & OPTION_TYPE_MASK;
        optlen = dhcp_option_lengths[type];
-       upper_length = len_of_option_as_string[type] * ((unsigned)len / (unsigned)optlen);
+       upper_length = len_of_option_as_string[type]
+               * ((unsigned)(len + optlen - 1) / (unsigned)optlen);
 
        dest = ret = xmalloc(upper_length + strlen(opt_name) + 2);
        dest += sprintf(ret, "%s=", opt_name);
 
        while (len >= optlen) {
                switch (type) {
-               case OPTION_IP_PAIR:
-                       dest += sprint_nip(dest, "", option);
-                       *dest++ = '/';
-                       option += 4;
-                       optlen = 4;
                case OPTION_IP:
+               case OPTION_IP_PAIR:
                        dest += sprint_nip(dest, "", option);
-// TODO: it can be a list only if (optflag->flags & OPTION_LIST).
-// Should we bail out/warn if we see multi-ip option which is
-// not allowed to be such? For example, DHCP_BROADCAST...
+                       if (type == OPTION_IP)
+                               break;
+                       dest += sprint_nip(dest, "/", option + 4);
                        break;
 //             case OPTION_BOOLEAN:
 //                     dest += sprintf(dest, *option ? "yes" : "no");
@@ -129,10 +240,17 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
                        dest += sprintf(dest, type == OPTION_U32 ? "%lu" : "%ld", (unsigned long) ntohl(val_u32));
                        break;
                }
+               /* Note: options which use 'return' instead of 'break'
+                * (for example, OPTION_STRING) skip the code which handles
+                * the case of list of options.
+                */
                case OPTION_STRING:
+               case OPTION_STRING_HOST:
                        memcpy(dest, option, len);
                        dest[len] = '\0';
-                       return ret;      /* Short circuit this case */
+                       if (type == OPTION_STRING_HOST && !good_hostname(dest))
+                               safe_strncpy(dest, "bad", len);
+                       return ret;
                case OPTION_STATIC_ROUTES: {
                        /* Option binary format:
                         * mask [one byte, 0..32]
@@ -177,6 +295,53 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
 
                        return ret;
                }
+               case OPTION_6RD:
+                       /* Option binary format (see RFC 5969):
+                        *  0                   1                   2                   3
+                        *  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
+                        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                        * |  OPTION_6RD   | option-length |  IPv4MaskLen  |  6rdPrefixLen |
+                        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                        * |                           6rdPrefix                           |
+                        * ...                        (16 octets)                        ...
+                        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                        * ...                   6rdBRIPv4Address(es)                    ...
+                        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                        * We convert it to a string
+                        * "IPv4MaskLen 6rdPrefixLen 6rdPrefix 6rdBRIPv4Address..."
+                        *
+                        * Sanity check: ensure that our length is at least 22 bytes, that
+                        * IPv4MaskLen <= 32,
+                        * 6rdPrefixLen <= 128,
+                        * 6rdPrefixLen + (32 - IPv4MaskLen) <= 128
+                        * (2nd condition need no check - it follows from 1st and 3rd).
+                        * Else, return envvar with empty value ("optname=")
+                        */
+                       if (len >= (1 + 1 + 16 + 4)
+                        && option[0] <= 32
+                        && (option[1] + 32 - option[0]) <= 128
+                       ) {
+                               /* IPv4MaskLen */
+                               dest += sprintf(dest, "%u ", *option++);
+                               /* 6rdPrefixLen */
+                               dest += sprintf(dest, "%u ", *option++);
+                               /* 6rdPrefix */
+                               dest += sprint_nip6(dest, /* "", */ option);
+                               option += 16;
+                               len -= 1 + 1 + 16 + 4;
+                               /* "+ 4" above corresponds to the length of IPv4 addr
+                                * we consume in the loop below */
+                               while (1) {
+                                       /* 6rdBRIPv4Address(es) */
+                                       dest += sprint_nip(dest, " ", option);
+                                       option += 4;
+                                       len -= 4; /* do we have yet another 4+ bytes? */
+                                       if (len < 0)
+                                               break; /* no */
+                               }
+                       }
+
+                       return ret;
 #if ENABLE_FEATURE_UDHCP_RFC3397
                case OPTION_DNS_STRING:
                        /* unpack option into dest; use ret for prefix (i.e., "optname=") */
@@ -216,13 +381,21 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
                        return ret;
 #endif
                } /* switch */
+
+               /* If we are here, try to format any remaining data
+                * in the option as another, similarly-formatted option
+                */
                option += optlen;
                len -= optlen;
-               if (len <= 0)
+// TODO: it can be a list only if (optflag->flags & OPTION_LIST).
+// Should we bail out/warn if we see multi-ip option which is
+// not allowed to be such (for example, DHCP_BROADCAST)? -
+               if (len < optlen /* || !(optflag->flags & OPTION_LIST) */)
                        break;
                *dest++ = ' ';
                *dest = '\0';
-       }
+       } /* while */
+
        return ret;
 }
 
@@ -236,6 +409,14 @@ static char **fill_envp(struct dhcp_packet *packet)
        uint8_t *temp;
        uint8_t overload = 0;
 
+#define BITMAP unsigned
+#define BBITS (sizeof(BITMAP) * 8)
+#define BMASK(i) (1 << (i & (sizeof(BITMAP) * 8 - 1)))
+#define FOUND_OPTS(i) (found_opts[(unsigned)i / BBITS])
+       BITMAP found_opts[256 / BBITS];
+
+       memset(found_opts, 0, sizeof(found_opts));
+
        /* We need 6 elements for:
         * "interface=IFACE"
         * "ip=N.N.N.N" from packet->yiaddr
@@ -247,18 +428,22 @@ static char **fill_envp(struct dhcp_packet *packet)
        envc = 6;
        /* +1 element for each option, +2 for subnet option: */
        if (packet) {
-               for (i = 0; dhcp_optflags[i].code; i++) {
-                       if (udhcp_get_option(packet, dhcp_optflags[i].code)) {
-                               if (dhcp_optflags[i].code == DHCP_SUBNET)
-                                       envc++; /* for mton */
+               /* note: do not search for "pad" (0) and "end" (255) options */
+//TODO: change logic to scan packet _once_
+               for (i = 1; i < 255; i++) {
+                       temp = udhcp_get_option(packet, i);
+                       if (temp) {
+                               if (i == DHCP_OPTION_OVERLOAD)
+                                       overload = *temp;
+                               else if (i == DHCP_SUBNET)
+                                       envc++; /* for $mask */
                                envc++;
+                               /*if (i != DHCP_MESSAGE_TYPE)*/
+                               FOUND_OPTS(i) |= BMASK(i);
                        }
                }
-               temp = udhcp_get_option(packet, DHCP_OPTION_OVERLOAD);
-               if (temp)
-                       overload = *temp;
        }
-       curr = envp = xzalloc(sizeof(char *) * envc);
+       curr = envp = xzalloc(sizeof(envp[0]) * envc);
 
        *curr = xasprintf("interface=%s", client_config.interface);
        putenv(*curr++);
@@ -266,44 +451,88 @@ static char **fill_envp(struct dhcp_packet *packet)
        if (!packet)
                return envp;
 
+       /* Export BOOTP fields. Fields we don't (yet?) export:
+        * uint8_t op;      // always BOOTREPLY
+        * uint8_t htype;   // hardware address type. 1 = 10mb ethernet
+        * uint8_t hlen;    // hardware address length
+        * uint8_t hops;    // used by relay agents only
+        * uint32_t xid;
+        * uint16_t secs;   // elapsed since client began acquisition/renewal
+        * uint16_t flags;  // only one flag so far: bcast. Never set by server
+        * uint32_t ciaddr; // client IP (usually == yiaddr. can it be different
+        *                  // if during renew server wants to give us differn IP?)
+        * uint32_t gateway_nip; // relay agent IP address
+        * uint8_t chaddr[16]; // link-layer client hardware address (MAC)
+        * TODO: export gateway_nip as $giaddr?
+        */
+       /* Most important one: yiaddr as $ip */
        *curr = xmalloc(sizeof("ip=255.255.255.255"));
        sprint_nip(*curr, "ip=", (uint8_t *) &packet->yiaddr);
        putenv(*curr++);
+       if (packet->siaddr_nip) {
+               /* IP address of next server to use in bootstrap */
+               *curr = xmalloc(sizeof("siaddr=255.255.255.255"));
+               sprint_nip(*curr, "siaddr=", (uint8_t *) &packet->siaddr_nip);
+               putenv(*curr++);
+       }
+       if (!(overload & FILE_FIELD) && packet->file[0]) {
+               /* watch out for invalid packets */
+               *curr = xasprintf("boot_file=%."DHCP_PKT_FILE_LEN_STR"s", packet->file);
+               putenv(*curr++);
+       }
+       if (!(overload & SNAME_FIELD) && packet->sname[0]) {
+               /* watch out for invalid packets */
+               *curr = xasprintf("sname=%."DHCP_PKT_SNAME_LEN_STR"s", packet->sname);
+               putenv(*curr++);
+       }
 
+       /* Export known DHCP options */
        opt_name = dhcp_option_strings;
        i = 0;
        while (*opt_name) {
-               temp = udhcp_get_option(packet, dhcp_optflags[i].code);
-               if (!temp)
+               uint8_t code = dhcp_optflags[i].code;
+               BITMAP *found_ptr = &FOUND_OPTS(code);
+               BITMAP found_mask = BMASK(code);
+               if (!(*found_ptr & found_mask))
                        goto next;
+               *found_ptr &= ~found_mask; /* leave only unknown options */
+               temp = udhcp_get_option(packet, code);
                *curr = xmalloc_optname_optval(temp, &dhcp_optflags[i], opt_name);
                putenv(*curr++);
-               if (dhcp_optflags[i].code == DHCP_SUBNET) {
+               if (code == DHCP_SUBNET) {
                        /* Subnet option: make things like "$ip/$mask" possible */
                        uint32_t subnet;
                        move_from_unaligned32(subnet, temp);
-                       *curr = xasprintf("mask=%d", mton(subnet));
+                       *curr = xasprintf("mask=%u", mton(subnet));
                        putenv(*curr++);
                }
  next:
                opt_name += strlen(opt_name) + 1;
                i++;
        }
-       if (packet->siaddr_nip) {
-               *curr = xmalloc(sizeof("siaddr=255.255.255.255"));
-               sprint_nip(*curr, "siaddr=", (uint8_t *) &packet->siaddr_nip);
-               putenv(*curr++);
-       }
-       if (!(overload & FILE_FIELD) && packet->file[0]) {
-               /* watch out for invalid packets */
-               *curr = xasprintf("boot_file=%."DHCP_PKT_FILE_LEN_STR"s", packet->file);
-               putenv(*curr++);
-       }
-       if (!(overload & SNAME_FIELD) && packet->sname[0]) {
-               /* watch out for invalid packets */
-               *curr = xasprintf("sname=%."DHCP_PKT_SNAME_LEN_STR"s", packet->sname);
-               putenv(*curr++);
+       /* Export unknown options */
+       for (i = 0; i < 256;) {
+               BITMAP bitmap = FOUND_OPTS(i);
+               if (!bitmap) {
+                       i += BBITS;
+                       continue;
+               }
+               if (bitmap & BMASK(i)) {
+                       unsigned len, ofs;
+
+                       temp = udhcp_get_option(packet, i);
+                       /* udhcp_get_option returns ptr to data portion,
+                        * need to go back to get len
+                        */
+                       len = temp[-OPT_DATA + OPT_LEN];
+                       *curr = xmalloc(sizeof("optNNN=") + 1 + len*2);
+                       ofs = sprintf(*curr, "opt%u=", i);
+                       *bin2hex(*curr + ofs, (void*) temp, len) = '\0';
+                       putenv(*curr++);
+               }
+               i++;
        }
+
        return envp;
 }
 
@@ -313,9 +542,6 @@ static void udhcp_run_script(struct dhcp_packet *packet, const char *name)
        char **envp, **curr;
        char *argv[3];
 
-       if (client_config.script == NULL)
-               return;
-
        envp = fill_envp(packet);
 
        /* call script */
@@ -343,38 +569,38 @@ static ALWAYS_INLINE uint32_t random_xid(void)
 /* Initialize the packet with the proper defaults */
 static void init_packet(struct dhcp_packet *packet, char type)
 {
+       uint16_t secs;
+
+       /* Fill in: op, htype, hlen, cookie fields; message type option: */
        udhcp_init_header(packet, type);
+
+       packet->xid = random_xid();
+
+       client_config.last_secs = monotonic_sec();
+       if (client_config.first_secs == 0)
+               client_config.first_secs = client_config.last_secs;
+       secs = client_config.last_secs - client_config.first_secs;
+       packet->secs = htons(secs);
+
        memcpy(packet->chaddr, client_config.client_mac, 6);
        if (client_config.clientid)
                udhcp_add_binary_option(packet, client_config.clientid);
-       if (client_config.hostname)
-               udhcp_add_binary_option(packet, client_config.hostname);
-       if (client_config.fqdn)
-               udhcp_add_binary_option(packet, client_config.fqdn);
-       if (type != DHCPDECLINE
-        && type != DHCPRELEASE
-        && client_config.vendorclass
-       ) {
-               udhcp_add_binary_option(packet, client_config.vendorclass);
-       }
 }
 
 static void add_client_options(struct dhcp_packet *packet)
 {
+       int i, end, len;
+
+       udhcp_add_simple_option(packet, DHCP_MAX_SIZE, htons(IP_UDP_DHCP_SIZE));
+
        /* Add a "param req" option with the list of options we'd like to have
         * from stubborn DHCP servers. Pull the data from the struct in common.c.
         * No bounds checking because it goes towards the head of the packet. */
-       uint8_t c;
-       int end = udhcp_end_option(packet->options);
-       int i, len = 0;
-
-       for (i = 0; (c = dhcp_optflags[i].code) != 0; i++) {
-               if ((   (dhcp_optflags[i].flags & OPTION_REQ)
-                    && !client_config.no_default_options
-                   )
-                || (client_config.opt_mask[c >> 3] & (1 << (c & 7)))
-               ) {
-                       packet->options[end + OPT_DATA + len] = c;
+       end = udhcp_end_option(packet->options);
+       len = 0;
+       for (i = 1; i < DHCP_END; i++) {
+               if (client_config.opt_mask[i >> 3] & (1 << (i & 7))) {
+                       packet->options[end + OPT_DATA + len] = i;
                        len++;
                }
        }
@@ -384,6 +610,17 @@ static void add_client_options(struct dhcp_packet *packet)
                packet->options[end + OPT_DATA + len] = DHCP_END;
        }
 
+       if (client_config.vendorclass)
+               udhcp_add_binary_option(packet, client_config.vendorclass);
+       if (client_config.hostname)
+               udhcp_add_binary_option(packet, client_config.hostname);
+       if (client_config.fqdn)
+               udhcp_add_binary_option(packet, client_config.fqdn);
+
+       /* Request broadcast replies if we have no IP addr */
+       if ((option_mask32 & OPT_B) && packet->ciaddr == 0)
+               packet->flags |= htons(BROADCAST_FLAG);
+
        /* Add -x options if any */
        {
                struct option_set *curr = client_config.options;
@@ -396,6 +633,12 @@ static void add_client_options(struct dhcp_packet *packet)
 //             if (client_config.boot_file)
 //                     strncpy((char*)packet->file, client_config.boot_file, sizeof(packet->file) - 1);
        }
+
+       // This will be needed if we remove -V VENDOR_STR in favor of
+       // -x vendor:VENDOR_STR
+       //if (!udhcp_find_option(packet.options, DHCP_VENDOR))
+       //      /* not set, set the default vendor ID */
+       //      ...add (DHCP_VENDOR, "udhcp "BB_VER) opt...
 }
 
 /* RFC 2131
@@ -424,18 +667,35 @@ static int raw_bcast_from_client_config_ifindex(struct dhcp_packet *packet)
                client_config.ifindex);
 }
 
+static int bcast_or_ucast(struct dhcp_packet *packet, uint32_t ciaddr, uint32_t server)
+{
+       if (server)
+               return udhcp_send_kernel_packet(packet,
+                       ciaddr, CLIENT_PORT,
+                       server, SERVER_PORT);
+       return raw_bcast_from_client_config_ifindex(packet);
+}
+
 /* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
-static int send_discover(uint32_t xid, uint32_t requested)
+/* NOINLINE: limit stack usage in caller */
+static NOINLINE int send_discover(uint32_t xid, uint32_t requested)
 {
        struct dhcp_packet packet;
 
+       /* Fill in: op, htype, hlen, cookie, chaddr fields,
+        * random xid field (we override it below),
+        * client-id option (unless -C), message type option:
+        */
        init_packet(&packet, DHCPDISCOVER);
+
        packet.xid = xid;
        if (requested)
                udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
-       /* Explicitly saying that we want RFC-compliant packets helps
-        * some buggy DHCP servers to NOT send bigger packets */
-       udhcp_add_simple_option(&packet, DHCP_MAX_SIZE, htons(576));
+
+       /* Add options: maxsize,
+        * optionally: hostname, fqdn, vendorclass,
+        * "param req" option according to -O, options specified with -x
+        */
        add_client_options(&packet);
 
        bb_info_msg("Sending discover...");
@@ -446,15 +706,39 @@ static int send_discover(uint32_t xid, uint32_t requested)
 /* RFC 2131 3.1 paragraph 3:
  * "The client _broadcasts_ a DHCPREQUEST message..."
  */
-static int send_select(uint32_t xid, uint32_t server, uint32_t requested)
+/* NOINLINE: limit stack usage in caller */
+static NOINLINE int send_select(uint32_t xid, uint32_t server, uint32_t requested)
 {
        struct dhcp_packet packet;
        struct in_addr addr;
 
+/*
+ * RFC 2131 4.3.2 DHCPREQUEST message
+ * ...
+ * If the DHCPREQUEST message contains a 'server identifier'
+ * option, the message is in response to a DHCPOFFER message.
+ * Otherwise, the message is a request to verify or extend an
+ * existing lease. If the client uses a 'client identifier'
+ * in a DHCPREQUEST message, it MUST use that same 'client identifier'
+ * in all subsequent messages. If the client included a list
+ * of requested parameters in a DHCPDISCOVER message, it MUST
+ * include that list in all subsequent messages.
+ */
+       /* Fill in: op, htype, hlen, cookie, chaddr fields,
+        * random xid field (we override it below),
+        * client-id option (unless -C), message type option:
+        */
        init_packet(&packet, DHCPREQUEST);
+
        packet.xid = xid;
        udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
+
        udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
+
+       /* Add options: maxsize,
+        * optionally: hostname, fqdn, vendorclass,
+        * "param req" option according to -O, and options specified with -x
+        */
        add_client_options(&packet);
 
        addr.s_addr = requested;
@@ -463,32 +747,67 @@ static int send_select(uint32_t xid, uint32_t server, uint32_t requested)
 }
 
 /* Unicast or broadcast a DHCP renew message */
-static int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
+/* NOINLINE: limit stack usage in caller */
+static NOINLINE int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
 {
        struct dhcp_packet packet;
 
+/*
+ * RFC 2131 4.3.2 DHCPREQUEST message
+ * ...
+ * DHCPREQUEST generated during RENEWING state:
+ *
+ * 'server identifier' MUST NOT be filled in, 'requested IP address'
+ * option MUST NOT be filled in, 'ciaddr' MUST be filled in with
+ * client's IP address. In this situation, the client is completely
+ * configured, and is trying to extend its lease. This message will
+ * be unicast, so no relay agents will be involved in its
+ * transmission.  Because 'giaddr' is therefore not filled in, the
+ * DHCP server will trust the value in 'ciaddr', and use it when
+ * replying to the client.
+ */
+       /* Fill in: op, htype, hlen, cookie, chaddr fields,
+        * random xid field (we override it below),
+        * client-id option (unless -C), message type option:
+        */
        init_packet(&packet, DHCPREQUEST);
+
        packet.xid = xid;
        packet.ciaddr = ciaddr;
+
+       /* Add options: maxsize,
+        * optionally: hostname, fqdn, vendorclass,
+        * "param req" option according to -O, and options specified with -x
+        */
        add_client_options(&packet);
 
        bb_info_msg("Sending renew...");
-       if (server)
-               return udhcp_send_kernel_packet(&packet,
-                       ciaddr, CLIENT_PORT,
-                       server, SERVER_PORT);
-       return raw_bcast_from_client_config_ifindex(&packet);
+       return bcast_or_ucast(&packet, ciaddr, server);
 }
 
 #if ENABLE_FEATURE_UDHCPC_ARPING
 /* Broadcast a DHCP decline message */
-static int send_decline(uint32_t xid, uint32_t server, uint32_t requested)
+/* NOINLINE: limit stack usage in caller */
+static NOINLINE int send_decline(/*uint32_t xid,*/ uint32_t server, uint32_t requested)
 {
        struct dhcp_packet packet;
 
+       /* Fill in: op, htype, hlen, cookie, chaddr, random xid fields,
+        * client-id option (unless -C), message type option:
+        */
        init_packet(&packet, DHCPDECLINE);
+
+#if 0
+       /* RFC 2131 says DHCPDECLINE's xid is randomly selected by client,
+        * but in case the server is buggy and wants DHCPDECLINE's xid
+        * to match the xid which started entire handshake,
+        * we use the same xid we used in initial DHCPDISCOVER:
+        */
        packet.xid = xid;
+#endif
+       /* DHCPDECLINE uses "requested ip", not ciaddr, to store offered IP */
        udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
+
        udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
 
        bb_info_msg("Sending decline...");
@@ -501,29 +820,56 @@ static int send_release(uint32_t server, uint32_t ciaddr)
 {
        struct dhcp_packet packet;
 
+       /* Fill in: op, htype, hlen, cookie, chaddr, random xid fields,
+        * client-id option (unless -C), message type option:
+        */
        init_packet(&packet, DHCPRELEASE);
-       packet.xid = random_xid();
+
+       /* DHCPRELEASE uses ciaddr, not "requested ip", to store IP being released */
        packet.ciaddr = ciaddr;
 
        udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
 
        bb_info_msg("Sending release...");
-       return udhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
+       /* Note: normally we unicast here since "server" is not zero.
+        * However, there _are_ people who run "address-less" DHCP servers,
+        * and reportedly ISC dhcp client and Windows allow that.
+        */
+       return bcast_or_ucast(&packet, ciaddr, server);
 }
 
 /* Returns -1 on errors that are fatal for the socket, -2 for those that aren't */
+/* NOINLINE: limit stack usage in caller */
 static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
 {
        int bytes;
        struct ip_udp_dhcp_packet packet;
        uint16_t check;
+       unsigned char cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
+       struct iovec iov;
+       struct msghdr msg;
+       struct cmsghdr *cmsg;
 
-       memset(&packet, 0, sizeof(packet));
-       bytes = safe_read(fd, &packet, sizeof(packet));
-       if (bytes < 0) {
-               log1("Packet read error, ignoring");
-               /* NB: possible down interface, etc. Caller should pause. */
-               return bytes; /* returns -1 */
+       /* used to use just safe_read(fd, &packet, sizeof(packet))
+        * but we need to check for TP_STATUS_CSUMNOTREADY :(
+        */
+       iov.iov_base = &packet;
+       iov.iov_len = sizeof(packet);
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+       msg.msg_control = cmsgbuf;
+       msg.msg_controllen = sizeof(cmsgbuf);
+       for (;;) {
+               bytes = recvmsg(fd, &msg, 0);
+               if (bytes < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       log1("Packet read error, ignoring");
+                       /* NB: possible down interface, etc. Caller should pause. */
+                       return bytes; /* returns -1 */
+               }
+               break;
        }
 
        if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp))) {
@@ -541,7 +887,8 @@ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
        bytes = ntohs(packet.ip.tot_len);
 
        /* make sure its the right packet for us, and that it passes sanity checks */
-       if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION
+       if (packet.ip.protocol != IPPROTO_UDP
+        || packet.ip.version != IPVERSION
         || packet.ip.ihl != (sizeof(packet.ip) >> 2)
         || packet.udp.dest != htons(CLIENT_PORT)
        /* || bytes > (int) sizeof(packet) - can't happen */
@@ -554,31 +901,48 @@ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
        /* verify IP checksum */
        check = packet.ip.check;
        packet.ip.check = 0;
-       if (check != udhcp_checksum(&packet.ip, sizeof(packet.ip))) {
+       if (check != inet_cksum((uint16_t *)&packet.ip, sizeof(packet.ip))) {
                log1("Bad IP header checksum, ignoring");
                return -2;
        }
 
+       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+               if (cmsg->cmsg_level == SOL_PACKET
+                && cmsg->cmsg_type == PACKET_AUXDATA
+               ) {
+                       /* some VMs don't checksum UDP and TCP data
+                        * they send to the same physical machine,
+                        * here we detect this case:
+                        */
+                       struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg);
+                       if (aux->tp_status & TP_STATUS_CSUMNOTREADY)
+                               goto skip_udp_sum_check;
+               }
+       }
+
        /* verify UDP checksum. IP header has to be modified for this */
        memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
        /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
        packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
        check = packet.udp.check;
        packet.udp.check = 0;
-       if (check && check != udhcp_checksum(&packet, bytes)) {
+       if (check && check != inet_cksum((uint16_t *)&packet, bytes)) {
                log1("Packet with bad UDP checksum received, ignoring");
                return -2;
        }
+ skip_udp_sum_check:
 
-       memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) + sizeof(packet.udp)));
-
-       if (dhcp_pkt->cookie != htonl(DHCP_MAGIC)) {
+       if (packet.data.cookie != htonl(DHCP_MAGIC)) {
                bb_info_msg("Packet with bad magic, ignoring");
                return -2;
        }
-       log1("Got valid DHCP packet");
-       udhcp_dump_packet(dhcp_pkt);
-       return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
+
+       log1("Received a packet");
+       udhcp_dump_packet(&packet.data);
+
+       bytes -= sizeof(packet.ip) + sizeof(packet.udp);
+       memcpy(dhcp_pkt, &packet.data, bytes);
+       return bytes;
 }
 
 
@@ -633,22 +997,25 @@ static int udhcp_raw_socket(int ifindex)
         *
         * TODO: make conditional?
         */
-#define SERVER_AND_CLIENT_PORTS  ((67 << 16) + 68)
        static const struct sock_filter filter_instr[] = {
-               /* check for udp */
+               /* load 9th byte (protocol) */
                BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
-               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0),     /* L5, L1, is UDP? */
-               /* ugly check for arp on ethernet-like and IPv4 */
-               BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2),                      /* L1: */
-               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4),      /* L3, L4 */
-               /* skip IP header */
-               BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0),                     /* L5: */
-               /* check udp source and destination ports */
-               BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
-               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1), /* L3, L4 */
-               /* returns */
-               BPF_STMT(BPF_RET|BPF_K, 0x0fffffff ),                   /* L3: pass */
-               BPF_STMT(BPF_RET|BPF_K, 0),                             /* L4: reject */
+               /* jump to L1 if it is IPPROTO_UDP, else to L4 */
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6),
+               /* L1: load halfword from offset 6 (flags and frag offset) */
+               BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6),
+               /* jump to L4 if any bits in frag offset field are set, else to L2 */
+               BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0),
+               /* L2: skip IP header (load index reg with header len) */
+               BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0),
+               /* load udp destination port from halfword[header_len + 2] */
+               BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2),
+               /* jump to L3 if udp dport is CLIENT_PORT, else to L4 */
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1),
+               /* L3: accept packet */
+               BPF_STMT(BPF_RET|BPF_K, 0xffffffff),
+               /* L4: discard packet */
+               BPF_STMT(BPF_RET|BPF_K, 0),
        };
        static const struct sock_fprog filter_prog = {
                .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
@@ -659,20 +1026,28 @@ static int udhcp_raw_socket(int ifindex)
        log1("Opening raw socket on ifindex %d", ifindex); //log2?
 
        fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
-       log1("Got raw socket fd %d", fd); //log2?
+       log1("Got raw socket fd"); //log2?
 
-       if (SERVER_PORT == 67 && CLIENT_PORT == 68) {
-               /* Use only if standard ports are in use */
+       sock.sll_family = AF_PACKET;
+       sock.sll_protocol = htons(ETH_P_IP);
+       sock.sll_ifindex = ifindex;
+       xbind(fd, (struct sockaddr *) &sock, sizeof(sock));
+
+       if (CLIENT_PORT == 68) {
+               /* Use only if standard port is in use */
                /* Ignoring error (kernel may lack support for this) */
                if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
                                sizeof(filter_prog)) >= 0)
-                       log1("Attached filter to raw socket fd %d", fd); // log?
+                       log1("Attached filter to raw socket fd"); // log?
+       }
+
+       if (setsockopt(fd, SOL_PACKET, PACKET_AUXDATA,
+                       &const_int_1, sizeof(int)) < 0
+       ) {
+               if (errno != ENOPROTOOPT)
+                       log1("Can't set PACKET_AUXDATA on raw socket");
        }
 
-       sock.sll_family = AF_PACKET;
-       sock.sll_protocol = htons(ETH_P_IP);
-       sock.sll_ifindex = ifindex;
-       xbind(fd, (struct sockaddr *) &sock, sizeof(sock));
        log1("Created raw socket");
 
        return fd;
@@ -698,6 +1073,7 @@ static void change_listen_mode(int new_mode)
        /* else LISTEN_NONE: sockfd stays closed */
 }
 
+/* Called only on SIGUSR1 */
 static void perform_renew(void)
 {
        bb_info_msg("Performing a DHCP renew");
@@ -720,7 +1096,7 @@ static void perform_renew(void)
        }
 }
 
-static void perform_release(uint32_t requested_ip, uint32_t server_addr)
+static void perform_release(uint32_t server_addr, uint32_t requested_ip)
 {
        char buffer[sizeof("255.255.255.255")];
        struct in_addr temp_addr;
@@ -762,12 +1138,102 @@ static void client_background(void)
 }
 #endif
 
+//usage:#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
+//usage:# define IF_UDHCP_VERBOSE(...) __VA_ARGS__
+//usage:#else
+//usage:# define IF_UDHCP_VERBOSE(...)
+//usage:#endif
+//usage:#define udhcpc_trivial_usage
+//usage:       "[-fbq"IF_UDHCP_VERBOSE("v")IF_FEATURE_UDHCPC_ARPING("a")"RB] [-t N] [-T SEC] [-A SEC/-n]\n"
+//usage:       "       [-i IFACE]"IF_FEATURE_UDHCP_PORT(" [-P PORT]")" [-s PROG] [-p PIDFILE]\n"
+//usage:       "       [-oC] [-r IP] [-V VENDOR] [-F NAME] [-x OPT:VAL]... [-O OPT]..."
+//usage:#define udhcpc_full_usage "\n"
+//usage:       IF_LONG_OPTS(
+//usage:     "\n       -i,--interface IFACE    Interface to use (default eth0)"
+//usage:       IF_FEATURE_UDHCP_PORT(
+//usage:     "\n       -P,--client-port PORT   Use PORT (default 68)"
+//usage:       )
+//usage:     "\n       -s,--script PROG        Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")"
+//usage:     "\n       -p,--pidfile FILE       Create pidfile"
+//usage:     "\n       -B,--broadcast          Request broadcast replies"
+//usage:     "\n       -t,--retries N          Send up to N discover packets (default 3)"
+//usage:     "\n       -T,--timeout SEC        Pause between packets (default 3)"
+//usage:     "\n       -A,--tryagain SEC       Wait if lease is not obtained (default 20)"
+//usage:     "\n       -n,--now                Exit if lease is not obtained"
+//usage:     "\n       -q,--quit               Exit after obtaining lease"
+//usage:     "\n       -R,--release            Release IP on exit"
+//usage:     "\n       -f,--foreground         Run in foreground"
+//usage:       USE_FOR_MMU(
+//usage:     "\n       -b,--background         Background if lease is not obtained"
+//usage:       )
+//usage:     "\n       -S,--syslog             Log to syslog too"
+//usage:       IF_FEATURE_UDHCPC_ARPING(
+//usage:     "\n       -a,--arping             Use arping to validate offered address"
+//usage:       )
+//usage:     "\n       -r,--request IP         Request this IP address"
+//usage:     "\n       -o,--no-default-options Don't request any options (unless -O is given)"
+//usage:     "\n       -O,--request-option OPT Request option OPT from server (cumulative)"
+//usage:     "\n       -x OPT:VAL              Include option OPT in sent packets (cumulative)"
+//usage:     "\n                               Examples of string, numeric, and hex byte opts:"
+//usage:     "\n                               -x hostname:bbox - option 12"
+//usage:     "\n                               -x lease:3600 - option 51 (lease time)"
+//usage:     "\n                               -x 0x3d:0100BEEFC0FFEE - option 61 (client id)"
+//usage:     "\n       -F,--fqdn NAME          Ask server to update DNS mapping for NAME"
+//usage:     "\n       -V,--vendorclass VENDOR Vendor identifier (default 'udhcp VERSION')"
+//usage:     "\n       -C,--clientid-none      Don't send MAC as client identifier"
+//usage:       IF_UDHCP_VERBOSE(
+//usage:     "\n       -v                      Verbose"
+//usage:       )
+//usage:       )
+//usage:       IF_NOT_LONG_OPTS(
+//usage:     "\n       -i IFACE        Interface to use (default eth0)"
+//usage:       IF_FEATURE_UDHCP_PORT(
+//usage:     "\n       -P PORT         Use PORT (default 68)"
+//usage:       )
+//usage:     "\n       -s PROG         Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")"
+//usage:     "\n       -p FILE         Create pidfile"
+//usage:     "\n       -B              Request broadcast replies"
+//usage:     "\n       -t N            Send up to N discover packets (default 3)"
+//usage:     "\n       -T SEC          Pause between packets (default 3)"
+//usage:     "\n       -A SEC          Wait if lease is not obtained (default 20)"
+//usage:     "\n       -n              Exit if lease is not obtained"
+//usage:     "\n       -q              Exit after obtaining lease"
+//usage:     "\n       -R              Release IP on exit"
+//usage:     "\n       -f              Run in foreground"
+//usage:       USE_FOR_MMU(
+//usage:     "\n       -b              Background if lease is not obtained"
+//usage:       )
+//usage:     "\n       -S              Log to syslog too"
+//usage:       IF_FEATURE_UDHCPC_ARPING(
+//usage:     "\n       -a              Use arping to validate offered address"
+//usage:       )
+//usage:     "\n       -r IP           Request this IP address"
+//usage:     "\n       -o              Don't request any options (unless -O is given)"
+//usage:     "\n       -O OPT          Request option OPT from server (cumulative)"
+//usage:     "\n       -x OPT:VAL      Include option OPT in sent packets (cumulative)"
+//usage:     "\n                       Examples of string, numeric, and hex byte opts:"
+//usage:     "\n                       -x hostname:bbox - option 12"
+//usage:     "\n                       -x lease:3600 - option 51 (lease time)"
+//usage:     "\n                       -x 0x3d:0100BEEFC0FFEE - option 61 (client id)"
+//usage:     "\n       -F NAME         Ask server to update DNS mapping for NAME"
+//usage:     "\n       -V VENDOR       Vendor identifier (default 'udhcp VERSION')"
+//usage:     "\n       -C              Don't send MAC as client identifier"
+//usage:       IF_UDHCP_VERBOSE(
+//usage:     "\n       -v              Verbose"
+//usage:       )
+//usage:       )
+//usage:     "\nSignals:"
+//usage:     "\n       USR1    Renew lease"
+//usage:     "\n       USR2    Release lease"
+
+
 int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 {
        uint8_t *temp, *message;
-       const char *str_c, *str_V, *str_h, *str_F, *str_r;
+       const char *str_V, *str_h, *str_F, *str_r;
        IF_FEATURE_UDHCP_PORT(char *str_P;)
+       void *clientid_mac_ptr;
        llist_t *list_O = NULL;
        llist_t *list_x = NULL;
        int tryagain_timeout = 20;
@@ -775,78 +1241,16 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
        int discover_retries = 3;
        uint32_t server_addr = server_addr; /* for compiler */
        uint32_t requested_ip = 0;
-       uint32_t xid = 0;
-       uint32_t lease_seconds = 0; /* can be given as 32-bit quantity */
+       uint32_t xid = xid; /* for compiler */
        int packet_num;
        int timeout; /* must be signed */
        unsigned already_waited_sec;
        unsigned opt;
        int max_fd;
        int retval;
-       struct timeval tv;
-       struct dhcp_packet packet;
        fd_set rfds;
 
-#if ENABLE_LONG_OPTS
-       static const char udhcpc_longopts[] ALIGN1 =
-               "clientid\0"       Required_argument "c"
-               "clientid-none\0"  No_argument       "C"
-               "vendorclass\0"    Required_argument "V"
-               "hostname\0"       Required_argument "H"
-               "fqdn\0"           Required_argument "F"
-               "interface\0"      Required_argument "i"
-               "now\0"            No_argument       "n"
-               "pidfile\0"        Required_argument "p"
-               "quit\0"           No_argument       "q"
-               "release\0"        No_argument       "R"
-               "request\0"        Required_argument "r"
-               "script\0"         Required_argument "s"
-               "timeout\0"        Required_argument "T"
-               "version\0"        No_argument       "v"
-               "retries\0"        Required_argument "t"
-               "tryagain\0"       Required_argument "A"
-               "syslog\0"         No_argument       "S"
-               "request-option\0" Required_argument "O"
-               "no-default-options\0" No_argument   "o"
-               "foreground\0"     No_argument       "f"
-               "background\0"     No_argument       "b"
-               IF_FEATURE_UDHCPC_ARPING("arping\0"     No_argument       "a")
-               IF_FEATURE_UDHCP_PORT("client-port\0"   Required_argument "P")
-               ;
-#endif
-       enum {
-               OPT_c = 1 << 0,
-               OPT_C = 1 << 1,
-               OPT_V = 1 << 2,
-               OPT_H = 1 << 3,
-               OPT_h = 1 << 4,
-               OPT_F = 1 << 5,
-               OPT_i = 1 << 6,
-               OPT_n = 1 << 7,
-               OPT_p = 1 << 8,
-               OPT_q = 1 << 9,
-               OPT_R = 1 << 10,
-               OPT_r = 1 << 11,
-               OPT_s = 1 << 12,
-               OPT_T = 1 << 13,
-               OPT_t = 1 << 14,
-               OPT_S = 1 << 15,
-               OPT_A = 1 << 16,
-               OPT_O = 1 << 17,
-               OPT_o = 1 << 18,
-               OPT_x = 1 << 19,
-               OPT_f = 1 << 20,
-/* The rest has variable bit positions, need to be clever */
-               OPTBIT_f = 20,
-               USE_FOR_MMU(             OPTBIT_b,)
-               IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
-               IF_FEATURE_UDHCP_PORT(   OPTBIT_P,)
-               USE_FOR_MMU(             OPT_b = 1 << OPTBIT_b,)
-               IF_FEATURE_UDHCPC_ARPING(OPT_a = 1 << OPTBIT_a,)
-               IF_FEATURE_UDHCP_PORT(   OPT_P = 1 << OPTBIT_P,)
-       };
-
-       /* Default options. */
+       /* Default options */
        IF_FEATURE_UDHCP_PORT(SERVER_PORT = 67;)
        IF_FEATURE_UDHCP_PORT(CLIENT_PORT = 68;)
        client_config.interface = "eth0";
@@ -854,31 +1258,28 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
        str_V = "udhcp "BB_VER;
 
        /* Parse command line */
-       /* Cc: mutually exclusive; O,x: list; -T,-t,-A take numeric param */
-       opt_complementary = "c--C:C--c:O::x::T+:t+:A+"
-#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
-               ":vv"
-#endif
-               ;
+       /* O,x: list; -T,-t,-A take numeric param */
+       opt_complementary = "O::x::T+:t+:A+" IF_UDHCP_VERBOSE(":vv") ;
        IF_LONG_OPTS(applet_long_options = udhcpc_longopts;)
-       opt = getopt32(argv, "c:CV:H:h:F:i:np:qRr:s:T:t:SA:O:ox:f"
+       opt = getopt32(argv, "CV:H:h:F:i:np:qRr:s:T:t:SA:O:ox:fB"
                USE_FOR_MMU("b")
                IF_FEATURE_UDHCPC_ARPING("a")
                IF_FEATURE_UDHCP_PORT("P:")
                "v"
-               , &str_c, &str_V, &str_h, &str_h, &str_F
+               , &str_V, &str_h, &str_h, &str_F
                , &client_config.interface, &client_config.pidfile, &str_r /* i,p */
                , &client_config.script /* s */
                , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */
                , &list_O
                , &list_x
                IF_FEATURE_UDHCP_PORT(, &str_P)
-#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
-               , &dhcp_verbose
-#endif
-               );
-       if (opt & (OPT_h|OPT_H))
+               IF_UDHCP_VERBOSE(, &dhcp_verbose)
+       );
+       if (opt & (OPT_h|OPT_H)) {
+               //msg added 2011-11
+               bb_error_msg("option -h NAME is deprecated, use -x hostname:NAME");
                client_config.hostname = alloc_dhcp_option(DHCP_HOST_NAME, str_h, 0);
+       }
        if (opt & OPT_F) {
                /* FQDN option format: [0x51][len][flags][0][0]<fqdn> */
                client_config.fqdn = alloc_dhcp_option(DHCP_FQDN, str_F, 3);
@@ -902,14 +1303,23 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
                SERVER_PORT = CLIENT_PORT - 1;
        }
 #endif
-       if (opt & OPT_o)
-               client_config.no_default_options = 1;
        while (list_O) {
                char *optstr = llist_pop(&list_O);
-               unsigned n = udhcp_option_idx(optstr);
-               n = dhcp_optflags[n].code;
+               unsigned n = bb_strtou(optstr, NULL, 0);
+               if (errno || n > 254) {
+                       n = udhcp_option_idx(optstr);
+                       n = dhcp_optflags[n].code;
+               }
                client_config.opt_mask[n >> 3] |= 1 << (n & 7);
        }
+       if (!(opt & OPT_o)) {
+               unsigned i, n;
+               for (i = 0; (n = dhcp_optflags[i].code) != 0; i++) {
+                       if (dhcp_optflags[i].flags & OPTION_REQ) {
+                               client_config.opt_mask[n >> 3] |= 1 << (n & 7);
+                       }
+               }
+       }
        while (list_x) {
                char *optstr = llist_pop(&list_x);
                char *colon = strchr(optstr, ':');
@@ -928,16 +1338,24 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
                return 1;
        }
 
-       if (opt & OPT_c) {
-               client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, str_c, 0);
-       } else if (!(opt & OPT_C)) {
-               /* not set and not suppressed, set the default client ID */
+       clientid_mac_ptr = NULL;
+       if (!(opt & OPT_C) && !udhcp_find_option(client_config.options, DHCP_CLIENT_ID)) {
+               /* not suppressed and not set, set the default client ID */
                client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, "", 7);
                client_config.clientid[OPT_DATA] = 1; /* type: ethernet */
-               memcpy(client_config.clientid + OPT_DATA+1, client_config.client_mac, 6);
+               clientid_mac_ptr = client_config.clientid + OPT_DATA+1;
+               memcpy(clientid_mac_ptr, client_config.client_mac, 6);
        }
-       if (str_V[0] != '\0')
+       if (str_V[0] != '\0') {
+               // can drop -V, str_V, client_config.vendorclass,
+               // but need to add "vendor" to the list of recognized
+               // string opts for this to work;
+               // and need to tweak add_client_options() too...
+               // ...so the question is, should we?
+               //bb_error_msg("option -V VENDOR is deprecated, use -x vendor:VENDOR");
                client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, str_V, 0);
+       }
+
 #if !BB_MMU
        /* on NOMMU reexec (i.e., background) early */
        if (!(opt & OPT_f)) {
@@ -975,6 +1393,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
         * "continue" statements in code below jump to the top of the loop.
         */
        for (;;) {
+               struct timeval tv;
+               struct dhcp_packet packet;
                /* silence "uninitialized!" warning */
                unsigned timestamp_before_wait = timestamp_before_wait;
 
@@ -994,8 +1414,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
                retval = 0;
                /* If we already timed out, fall through with retval = 0, else... */
                if ((int)tv.tv_sec > 0) {
+                       log1("Waiting on select %u seconds", (int)tv.tv_sec);
                        timestamp_before_wait = (unsigned)monotonic_sec();
-                       log1("Waiting on select...");
                        retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
                        if (retval < 0) {
                                /* EINTR? A signal was caught, don't panic */
@@ -1012,12 +1432,27 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
                 * resend discover/renew/whatever
                 */
                if (retval == 0) {
+                       /* When running on a bridge, the ifindex may have changed
+                        * (e.g. if member interfaces were added/removed
+                        * or if the status of the bridge changed).
+                        * Refresh ifindex and client_mac:
+                        */
+                       if (udhcp_read_interface(client_config.interface,
+                                       &client_config.ifindex,
+                                       NULL,
+                                       client_config.client_mac)
+                       ) {
+                               goto ret0; /* iface is gone? */
+                       }
+                       if (clientid_mac_ptr)
+                               memcpy(clientid_mac_ptr, client_config.client_mac, 6);
+
                        /* We will restart the wait in any case */
                        already_waited_sec = 0;
 
                        switch (state) {
                        case INIT_SELECTING:
-                               if (packet_num < discover_retries) {
+                               if (!discover_retries || packet_num < discover_retries) {
                                        if (packet_num == 0)
                                                xid = random_xid();
                                        /* broadcast */
@@ -1046,7 +1481,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
                                packet_num = 0;
                                continue;
                        case REQUESTING:
-                               if (packet_num < discover_retries) {
+                               if (!discover_retries || packet_num < discover_retries) {
                                        /* send broadcast select packet */
                                        send_select(xid, server_addr, requested_ip);
                                        timeout = discover_timeout;
@@ -1063,6 +1498,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
                        case BOUND:
                                /* 1/2 lease passed, enter renewing state */
                                state = RENEWING;
+                               client_config.first_secs = 0; /* make secs field count from 0 */
                                change_listen_mode(LISTEN_KERNEL);
                                log1("Entering renew state");
                                /* fall right through */
@@ -1102,6 +1538,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
                                bb_info_msg("Lease lost, entering init state");
                                udhcp_run_script(NULL, "deconfig");
                                state = INIT_SELECTING;
+                               client_config.first_secs = 0; /* make secs field count from 0 */
                                /*timeout = 0; - already is */
                                packet_num = 0;
                                continue;
@@ -1118,22 +1555,35 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
                /* note: udhcp_sp_read checks FD_ISSET before reading */
                switch (udhcp_sp_read(&rfds)) {
                case SIGUSR1:
+                       client_config.first_secs = 0; /* make secs field count from 0 */
+                       already_waited_sec = 0;
                        perform_renew();
-                       if (state == RENEW_REQUESTED)
+                       if (state == RENEW_REQUESTED) {
+                               /* We might be either on the same network
+                                * (in which case renew might work),
+                                * or we might be on a completely different one
+                                * (in which case renew won't ever succeed).
+                                * For the second case, must make sure timeout
+                                * is not too big, or else we can send
+                                * futile renew requests for hours.
+                                * (Ab)use -A TIMEOUT value (usually 20 sec)
+                                * as a cap on the timeout.
+                                */
+                               if (timeout > tryagain_timeout)
+                                       timeout = tryagain_timeout;
                                goto case_RENEW_REQUESTED;
+                       }
                        /* Start things over */
                        packet_num = 0;
                        /* Kill any timeouts, user wants this to hurry along */
                        timeout = 0;
                        continue;
                case SIGUSR2:
-                       perform_release(requested_ip, server_addr);
+                       perform_release(server_addr, requested_ip);
                        timeout = INT_MAX;
                        continue;
                case SIGTERM:
                        bb_info_msg("Received SIGTERM");
-                       if (opt & OPT_R) /* release on quit */
-                               perform_release(requested_ip, server_addr);
                        goto ret0;
                }
 
@@ -1171,7 +1621,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 
                /* Ignore packets that aren't for us */
                if (packet.hlen != 6
-                || memcmp(packet.chaddr, client_config.client_mac, 6)
+                || memcmp(packet.chaddr, client_config.client_mac, 6) != 0
                ) {
 //FIXME: need to also check that last 10 bytes are zero
                        log1("chaddr does not match, ignoring packet"); // log2?
@@ -1186,18 +1636,41 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 
                switch (state) {
                case INIT_SELECTING:
-                       /* Must be a DHCPOFFER to one of our xid's */
+                       /* Must be a DHCPOFFER */
                        if (*message == DHCPOFFER) {
-               /* TODO: why we don't just fetch server's IP from IP header? */
+/* What exactly is server's IP? There are several values.
+ * Example DHCP offer captured with tchdump:
+ *
+ * 10.34.25.254:67 > 10.34.25.202:68 // IP header's src
+ * BOOTP fields:
+ * Your-IP 10.34.25.202
+ * Server-IP 10.34.32.125   // "next server" IP
+ * Gateway-IP 10.34.25.254  // relay's address (if DHCP relays are in use)
+ * DHCP options:
+ * DHCP-Message Option 53, length 1: Offer
+ * Server-ID Option 54, length 4: 10.34.255.7       // "server ID"
+ * Default-Gateway Option 3, length 4: 10.34.25.254 // router
+ *
+ * We think that real server IP (one to use in renew/release)
+ * is one in Server-ID option. But I am not 100% sure.
+ * IP header's src and Gateway-IP (same in this example)
+ * might work too.
+ * "Next server" and router are definitely wrong ones to use, though...
+ */
+/* We used to ignore pcakets without DHCP_SERVER_ID.
+ * I've got user reports from people who run "address-less" servers.
+ * They either supply DHCP_SERVER_ID of 0.0.0.0 or don't supply it at all.
+ * They say ISC DHCP client supports this case.
+ */
+                               server_addr = 0;
                                temp = udhcp_get_option(&packet, DHCP_SERVER_ID);
                                if (!temp) {
-                                       bb_error_msg("no server ID in message");
-                                       continue;
-                                       /* still selecting - this server looks bad */
+                                       bb_error_msg("no server ID, using 0.0.0.0");
+                               } else {
+                                       /* it IS unaligned sometimes, don't "optimize" */
+                                       move_from_unaligned32(server_addr, temp);
                                }
-                               /* it IS unaligned sometimes, don't "optimize" */
-                               move_from_unaligned32(server_addr, temp);
-                               xid = packet.xid;
+                               /*xid = packet.xid; - already is */
                                requested_ip = packet.yiaddr;
 
                                /* enter requesting state */
@@ -1212,6 +1685,9 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
                case RENEW_REQUESTED:
                case REBINDING:
                        if (*message == DHCPACK) {
+                               uint32_t lease_seconds;
+                               struct in_addr temp_addr;
+
                                temp = udhcp_get_option(&packet, DHCP_LEASE_TIME);
                                if (!temp) {
                                        bb_error_msg("no lease time with ACK, using 1 hour lease");
@@ -1220,9 +1696,11 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
                                        /* it IS unaligned sometimes, don't "optimize" */
                                        move_from_unaligned32(lease_seconds, temp);
                                        lease_seconds = ntohl(lease_seconds);
-                                       lease_seconds &= 0x0fffffff; /* paranoia: must not be prone to overflows */
-                                       if (lease_seconds < 10) /* and not too small */
-                                               lease_seconds = 10;
+                                       /* paranoia: must not be too small and not prone to overflows */
+                                       if (lease_seconds < 0x10)
+                                               lease_seconds = 0x10;
+                                       if (lease_seconds >= 0x10000000)
+                                               lease_seconds = 0x0fffffff;
                                }
 #if ENABLE_FEATURE_UDHCPC_ARPING
                                if (opt & OPT_a) {
@@ -1243,12 +1721,13 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
                                        ) {
                                                bb_info_msg("Offered address is in use "
                                                        "(got ARP reply), declining");
-                                               send_decline(xid, server_addr, packet.yiaddr);
+                                               send_decline(/*xid,*/ server_addr, packet.yiaddr);
 
                                                if (state != REQUESTING)
                                                        udhcp_run_script(NULL, "deconfig");
                                                change_listen_mode(LISTEN_RAW);
                                                state = INIT_SELECTING;
+                                               client_config.first_secs = 0; /* make secs field count from 0 */
                                                requested_ip = 0;
                                                timeout = tryagain_timeout;
                                                packet_num = 0;
@@ -1259,20 +1738,15 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
 #endif
                                /* enter bound state */
                                timeout = lease_seconds / 2;
-                               {
-                                       struct in_addr temp_addr;
-                                       temp_addr.s_addr = packet.yiaddr;
-                                       bb_info_msg("Lease of %s obtained, lease time %u",
-                                               inet_ntoa(temp_addr), (unsigned)lease_seconds);
-                               }
+                               temp_addr.s_addr = packet.yiaddr;
+                               bb_info_msg("Lease of %s obtained, lease time %u",
+                                       inet_ntoa(temp_addr), (unsigned)lease_seconds);
                                requested_ip = packet.yiaddr;
                                udhcp_run_script(&packet, state == REQUESTING ? "bound" : "renew");
 
                                state = BOUND;
                                change_listen_mode(LISTEN_NONE);
                                if (opt & OPT_q) { /* quit after lease */
-                                       if (opt & OPT_R) /* release on quit */
-                                               perform_release(requested_ip, server_addr);
                                        goto ret0;
                                }
                                /* future renew failures should not exit (JM) */
@@ -1284,6 +1758,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
                                        opt = ((opt & ~OPT_b) | OPT_f);
                                }
 #endif
+                               /* make future renew packets use different xid */
+                               /* xid = random_xid(); ...but why bother? */
                                already_waited_sec = 0;
                                continue; /* back to main loop */
                        }
@@ -1296,6 +1772,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
                                change_listen_mode(LISTEN_RAW);
                                sleep(3); /* avoid excessive network traffic */
                                state = INIT_SELECTING;
+                               client_config.first_secs = 0; /* make secs field count from 0 */
                                requested_ip = 0;
                                timeout = 0;
                                packet_num = 0;
@@ -1309,6 +1786,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
        } /* for (;;) - main loop ends */
 
  ret0:
+       if (opt & OPT_R) /* release on quit */
+               perform_release(server_addr, requested_ip);
        retval = 0;
  ret:
        /*if (client_config.pidfile) - remove_pidfile has its own check */
index 6bef562..9f423a5 100644 (file)
@@ -1,6 +1,6 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #ifndef UDHCP_DHCPC_H
 #define UDHCP_DHCPC_H 1
@@ -9,7 +9,6 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 
 struct client_config_t {
        uint8_t client_mac[6];          /* Our mac address */
-       char no_default_options;        /* Do not include default options in request */
        IF_FEATURE_UDHCP_PORT(uint16_t port;)
        int ifindex;                    /* Index number of the interface to use */
        uint8_t opt_mask[256 / 8];      /* Bitmask of options to send (-O option) */
@@ -21,15 +20,20 @@ struct client_config_t {
        uint8_t *vendorclass;           /* Optional vendor class-id to use */
        uint8_t *hostname;              /* Optional hostname to use */
        uint8_t *fqdn;                  /* Optional fully qualified domain name to use */
+
+       uint16_t first_secs;
+       uint16_t last_secs;
 } FIX_ALIASING;
 
 /* server_config sits in 1st half of bb_common_bufsiz1 */
 #define client_config (*(struct client_config_t*)(&bb_common_bufsiz1[COMMON_BUFSIZE / 2]))
 
 #if ENABLE_FEATURE_UDHCP_PORT
-#define CLIENT_PORT (client_config.port)
+#define CLIENT_PORT  (client_config.port)
+#define CLIENT_PORT6 (client_config.port)
 #else
-#define CLIENT_PORT 68
+#define CLIENT_PORT  68
+#define CLIENT_PORT6 546
 #endif
 
 POP_SAVED_FUNCTION_VISIBILITY
index 043220d..a1a7f6b 100644 (file)
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
+
+//usage:#define udhcpd_trivial_usage
+//usage:       "[-fS] [-I ADDR]" IF_FEATURE_UDHCP_PORT(" [-P N]") " [CONFFILE]"
+//usage:#define udhcpd_full_usage "\n\n"
+//usage:       "DHCP server\n"
+//usage:     "\n       -f      Run in foreground"
+//usage:     "\n       -S      Log to syslog too"
+//usage:     "\n       -I ADDR Local address"
+//usage:       IF_FEATURE_UDHCP_PORT(
+//usage:     "\n       -P N    Use port N (default 67)"
+//usage:       )
+
 #include <syslog.h>
 #include "common.h"
 #include "dhcpc.h"
@@ -132,7 +144,11 @@ static uint32_t select_lease_time(struct dhcp_packet *packet)
 }
 
 /* We got a DHCP DISCOVER. Send an OFFER. */
-static void send_offer(struct dhcp_packet *oldpacket, uint32_t static_lease_nip, struct dyn_lease *lease)
+/* NOINLINE: limit stack usage in caller */
+static NOINLINE void send_offer(struct dhcp_packet *oldpacket,
+               uint32_t static_lease_nip,
+               struct dyn_lease *lease,
+               uint8_t *requested_ip_opt)
 {
        struct dhcp_packet packet;
        uint32_t lease_time_sec;
@@ -146,7 +162,6 @@ static void send_offer(struct dhcp_packet *oldpacket, uint32_t static_lease_nip,
        if (!static_lease_nip) {
                /* We have no static lease for client's chaddr */
                uint32_t req_nip;
-               uint8_t *req_ip_opt;
                const char *p_host_name;
 
                if (lease) {
@@ -157,9 +172,9 @@ static void send_offer(struct dhcp_packet *oldpacket, uint32_t static_lease_nip,
                        packet.yiaddr = lease->lease_nip;
                }
                /* Or: if client has requested an IP */
-               else if ((req_ip_opt = udhcp_get_option(oldpacket, DHCP_REQUESTED_IP)) != NULL
+               else if (requested_ip_opt != NULL
                 /* (read IP) */
-                && (move_from_unaligned32(req_nip, req_ip_opt), 1)
+                && (move_from_unaligned32(req_nip, requested_ip_opt), 1)
                 /* and the IP is in the lease range */
                 && ntohl(req_nip) >= server_config.start_ip
                 && ntohl(req_nip) <= server_config.end_ip
@@ -202,7 +217,8 @@ static void send_offer(struct dhcp_packet *oldpacket, uint32_t static_lease_nip,
        send_packet(&packet, /*force_bcast:*/ 0);
 }
 
-static void send_NAK(struct dhcp_packet *oldpacket)
+/* NOINLINE: limit stack usage in caller */
+static NOINLINE void send_NAK(struct dhcp_packet *oldpacket)
 {
        struct dhcp_packet packet;
 
@@ -212,7 +228,8 @@ static void send_NAK(struct dhcp_packet *oldpacket)
        send_packet(&packet, /*force_bcast:*/ 1);
 }
 
-static void send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
+/* NOINLINE: limit stack usage in caller */
+static NOINLINE void send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
 {
        struct dhcp_packet packet;
        uint32_t lease_time_sec;
@@ -243,7 +260,8 @@ static void send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
        }
 }
 
-static void send_inform(struct dhcp_packet *oldpacket)
+/* NOINLINE: limit stack usage in caller */
+static NOINLINE void send_inform(struct dhcp_packet *oldpacket)
 {
        struct dhcp_packet packet;
 
@@ -279,16 +297,13 @@ struct dyn_lease *g_leases;
 int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int udhcpd_main(int argc UNUSED_PARAM, char **argv)
 {
-       fd_set rfds;
        int server_socket = -1, retval, max_sock;
-       struct dhcp_packet packet;
        uint8_t *state;
-       uint32_t static_lease_nip;
        unsigned timeout_end;
        unsigned num_ips;
        unsigned opt;
        struct option_set *option;
-       struct dyn_lease *lease, fake_lease;
+       char *str_I = str_I;
        IF_FEATURE_UDHCP_PORT(char *str_P;)
 
 #if ENABLE_FEATURE_UDHCP_PORT
@@ -299,11 +314,11 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
        opt_complementary = "vv";
 #endif
-       opt = getopt32(argv, "fSv"
-               IF_FEATURE_UDHCP_PORT("P:", &str_P)
-#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
-               , &dhcp_verbose
-#endif
+       opt = getopt32(argv, "fSI:v"
+               IF_FEATURE_UDHCP_PORT("P:")
+               , &str_I
+               IF_FEATURE_UDHCP_PORT(, &str_P)
+               IF_UDHCP_VERBOSE(, &dhcp_verbose)
                );
        if (!(opt & 1)) { /* no -f */
                bb_daemonize_or_rexec(0, argv);
@@ -315,8 +330,13 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
                openlog(applet_name, LOG_PID, LOG_DAEMON);
                logmode |= LOGMODE_SYSLOG;
        }
+       if (opt & 4) { /* -I */
+               len_and_sockaddr *lsa = xhost_and_af2sockaddr(str_I, 0, AF_INET);
+               server_config.server_nip = lsa->u.sin.sin_addr.s_addr;
+               free(lsa);
+       }
 #if ENABLE_FEATURE_UDHCP_PORT
-       if (opt & 8) { /* -P */
+       if (opt & 16) { /* -P */
                SERVER_PORT = xatou16(str_P);
                CLIENT_PORT = SERVER_PORT + 1;
        }
@@ -356,7 +376,7 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
 
        if (udhcp_read_interface(server_config.interface,
                        &server_config.ifindex,
-                       &server_config.server_nip,
+                       (server_config.server_nip == 0 ? &server_config.server_nip : NULL),
                        server_config.server_mac)
        ) {
                retval = 1;
@@ -366,13 +386,18 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
        /* Setup the signal pipe */
        udhcp_sp_setup();
 
+ continue_with_autotime:
        timeout_end = monotonic_sec() + server_config.auto_time;
        while (1) { /* loop until universe collapses */
+               fd_set rfds;
+               struct dhcp_packet packet;
                int bytes;
                struct timeval tv;
                uint8_t *server_id_opt;
-               uint8_t *requested_opt;
+               uint8_t *requested_ip_opt;
                uint32_t requested_nip = requested_nip; /* for compiler */
+               uint32_t static_lease_nip;
+               struct dyn_lease *lease, fake_lease;
 
                if (server_socket < 0) {
                        server_socket = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT,
@@ -391,8 +416,7 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
                }
                if (retval == 0) {
                        write_leases();
-                       timeout_end = monotonic_sec() + server_config.auto_time;
-                       continue;
+                       goto continue_with_autotime;
                }
                if (retval < 0 && errno != EINTR) {
                        log1("Error on select");
@@ -404,12 +428,12 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
                        bb_info_msg("Received SIGUSR1");
                        write_leases();
                        /* why not just reset the timeout, eh */
-                       timeout_end = monotonic_sec() + server_config.auto_time;
-                       continue;
+                       goto continue_with_autotime;
                case SIGTERM:
                        bb_info_msg("Received SIGTERM");
+                       write_leases();
                        goto ret0;
-               case 0: /* no signal: read a packet */
+               case 0: /* no signal: read a packet */
                        break;
                default: /* signal or error (probably EINTR): back to select */
                        continue;
@@ -439,6 +463,18 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
                        continue;
                }
 
+               /* Get SERVER_ID if present */
+               server_id_opt = udhcp_get_option(&packet, DHCP_SERVER_ID);
+               if (server_id_opt) {
+                       uint32_t server_id_network_order;
+                       move_from_unaligned32(server_id_network_order, server_id_opt);
+                       if (server_id_network_order != server_config.server_nip) {
+                               /* client talks to somebody else */
+                               log1("server ID doesn't match, ignoring");
+                               continue;
+                       }
+               }
+
                /* Look for a static/dynamic lease */
                static_lease_nip = get_static_nip_by_mac(server_config.static_leases, &packet.chaddr);
                if (static_lease_nip) {
@@ -451,20 +487,10 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
                        lease = find_lease_by_mac(packet.chaddr);
                }
 
-               /* Get REQUESTED_IP and SERVER_ID if present */
-               server_id_opt = udhcp_get_option(&packet, DHCP_SERVER_ID);
-               if (server_id_opt) {
-                       uint32_t server_id_net;
-                       move_from_unaligned32(server_id_net, server_id_opt);
-                       if (server_id_net != server_config.server_nip) {
-                               /* client talks to somebody else */
-                               log1("server ID doesn't match, ignoring");
-                               continue;
-                       }
-               }
-               requested_opt = udhcp_get_option(&packet, DHCP_REQUESTED_IP);
-               if (requested_opt) {
-                       move_from_unaligned32(requested_nip, requested_opt);
+               /* Get REQUESTED_IP if present */
+               requested_ip_opt = udhcp_get_option(&packet, DHCP_REQUESTED_IP);
+               if (requested_ip_opt) {
+                       move_from_unaligned32(requested_nip, requested_ip_opt);
                }
 
                switch (state[0]) {
@@ -472,7 +498,7 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
                case DHCPDISCOVER:
                        log1("Received DISCOVER");
 
-                       send_offer(&packet, static_lease_nip, lease);
+                       send_offer(&packet, static_lease_nip, lease, requested_ip_opt);
                        break;
 
                case DHCPREQUEST:
@@ -563,7 +589,7 @@ o DHCPREQUEST generated during REBINDING state:
    A DHCP server MAY extend a client's lease only if it has local
    administrative authority to do so.
 */
-                       if (!requested_opt) {
+                       if (!requested_ip_opt) {
                                requested_nip = packet.ciaddr;
                                if (requested_nip == 0) {
                                        log1("no requested IP and no ciaddr, ignoring");
@@ -576,11 +602,15 @@ o DHCPREQUEST generated during REBINDING state:
                                send_ACK(&packet, lease->lease_nip);
                                break;
                        }
-                       if (server_id_opt) {
-                               /* client was talking specifically to us.
-                                * "No, we don't have this IP for you". */
+                       /* No lease for this MAC, or lease IP != requested IP */
+
+                       if (server_id_opt    /* client is in SELECTING state */
+                        || requested_ip_opt /* client is in INIT-REBOOT state */
+                       ) {
+                               /* "No, we don't have this IP for you" */
                                send_NAK(&packet);
-                       }
+                       } /* else: client is in RENEWING or REBINDING, do not answer */
+
                        break;
 
                case DHCPDECLINE:
@@ -599,7 +629,7 @@ o DHCPREQUEST generated during REBINDING state:
                         */
                        log1("Received DECLINE");
                        if (server_id_opt
-                        && requested_opt
+                        && requested_ip_opt
                         && lease  /* chaddr matches this lease */
                         && requested_nip == lease->lease_nip
                        ) {
index eea9017..a77724f 100644 (file)
@@ -1,6 +1,6 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #ifndef UDHCP_DHCPD_H
 #define UDHCP_DHCPD_H 1
@@ -61,9 +61,11 @@ struct server_config_t {
 /* client_config sits in 2nd half of bb_common_bufsiz1 */
 
 #if ENABLE_FEATURE_UDHCP_PORT
-#define SERVER_PORT (server_config.port)
+#define SERVER_PORT  (server_config.port)
+#define SERVER_PORT6 (server_config.port)
 #else
-#define SERVER_PORT 67
+#define SERVER_PORT  67
+#define SERVER_PORT6 547
 #endif
 
 
index d194a98..f82ac05 100644 (file)
@@ -1,7 +1,7 @@
 /* vi: set sw=4 ts=4: */
 /* Port to Busybox Copyright (C) 2006 Jesse Dutton <jessedutton@gmail.com>
  *
- * Licensed under GPL v2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  *
  * DHCP Relay for 'DHCPv4 Configuration of IPSec Tunnel Mode' support
  * Copyright (C) 2002 Mario Strasser <mast@gmx.net>,
@@ -9,11 +9,20 @@
  *                   Netbeat AG
  * Upstream has GPL v2 or later
  */
+
+//usage:#define dhcprelay_trivial_usage
+//usage:       "CLIENT_IFACE[,CLIENT_IFACE2]... SERVER_IFACE [SERVER_IP]"
+//usage:#define dhcprelay_full_usage "\n\n"
+//usage:       "Relay DHCP requests between clients and server"
+
 #include "common.h"
 
-#define SERVER_PORT      67
-#define SELECT_TIMEOUT    5 /* select timeout in sec. */
-#define MAX_LIFETIME   2*60 /* lifetime of an xid entry in sec. */
+#define SERVER_PORT    67
+
+/* lifetime of an xid entry in sec. */
+#define MAX_LIFETIME   2*60
+/* select timeout in sec. */
+#define SELECT_TIMEOUT (MAX_LIFETIME / 8)
 
 /* This list holds information about clients. The xid_* functions manipulate this list. */
 struct xid_item {
@@ -22,7 +31,7 @@ struct xid_item {
        uint32_t xid;
        struct sockaddr_in ip;
        struct xid_item *next;
-};
+} FIX_ALIASING;
 
 #define dhcprelay_xid_list (*(struct xid_item*)&bb_common_bufsiz1)
 
@@ -67,11 +76,11 @@ static struct xid_item *xid_find(uint32_t xid)
        struct xid_item *item = dhcprelay_xid_list.next;
        while (item != NULL) {
                if (item->xid == xid) {
-                       return item;
+                       break;
                }
                item = item->next;
        }
-       return NULL;
+       return item;
 }
 
 static void xid_del(uint32_t xid)
@@ -110,62 +119,72 @@ static int get_dhcp_packet_type(struct dhcp_packet *p)
 }
 
 /**
- * get_client_devices - parses the devices list
- * dev_list - comma separated list of devices
+ * make_iface_list - parses client/server interface names
  * returns array
  */
-static char **get_client_devices(char *dev_list, int *client_number)
+static char **make_iface_list(char **client_and_server_ifaces, int *client_number)
 {
-       char *s, **client_dev;
+       char *s, **iface_list;
        int i, cn;
 
-       /* copy list */
-       dev_list = xstrdup(dev_list);
-
-       /* get number of items, replace ',' with NULs */
-       s = dev_list;
-       cn = 1;
+       /* get number of items */
+       cn = 2; /* 1 server iface + at least 1 client one */
+       s = client_and_server_ifaces[0]; /* list of client ifaces */
        while (*s) {
-               if (*s == ',') {
-                       *s = '\0';
+               if (*s == ',')
                        cn++;
-               }
                s++;
        }
        *client_number = cn;
 
        /* create vector of pointers */
-       client_dev = xzalloc(cn * sizeof(*client_dev));
-       client_dev[0] = dev_list;
+       iface_list = xzalloc(cn * sizeof(iface_list[0]));
+
+       iface_list[0] = client_and_server_ifaces[1]; /* server iface */
+
        i = 1;
-       while (i != cn) {
-               client_dev[i] = client_dev[i - 1] + strlen(client_dev[i - 1]) + 1;
-               i++;
+       s = xstrdup(client_and_server_ifaces[0]); /* list of client ifaces */
+       goto store_client_iface_name;
+
+       while (i < cn) {
+               if (*s++ == ',') {
+                       s[-1] = '\0';
+ store_client_iface_name:
+                       iface_list[i++] = s;
+               }
        }
-       return client_dev;
+
+       return iface_list;
 }
 
 /* Creates listen sockets (in fds) bound to client and server ifaces,
  * and returns numerically max fd.
  */
-static int init_sockets(char **client_ifaces, int num_clients,
-                       char *server_iface, int *fds)
+static int init_sockets(char **iface_list, int num_clients, int *fds)
 {
        int i, n;
 
-       /* talk to real server on bootps */
-       fds[0] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, server_iface);
-       n = fds[0];
-
-       for (i = 1; i < num_clients; i++) {
-               /* listen for clients on bootps */
-               fds[i] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, client_ifaces[i-1]);
-               if (fds[i] > n)
+       n = 0;
+       for (i = 0; i < num_clients; i++) {
+               fds[i] = udhcp_listen_socket(/*INADDR_ANY,*/ SERVER_PORT, iface_list[i]);
+               if (n < fds[i])
                        n = fds[i];
        }
        return n;
 }
 
+static int sendto_ip4(int sock, const void *msg, int msg_len, struct sockaddr_in *to)
+{
+       int err;
+
+       errno = 0;
+       err = sendto(sock, msg, msg_len, 0, (struct sockaddr*) to, sizeof(*to));
+       err -= msg_len;
+       if (err)
+               bb_perror_msg("sendto");
+       return err;
+}
+
 /**
  * pass_to_server() - forwards dhcp packets from client to server
  * p - packet to send
@@ -174,7 +193,7 @@ static int init_sockets(char **client_ifaces, int num_clients,
 static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, int *fds,
                        struct sockaddr_in *client_addr, struct sockaddr_in *server_addr)
 {
-       int res, type;
+       int type;
 
        /* check packet_type */
        type = get_dhcp_packet_type(p);
@@ -188,13 +207,12 @@ static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, in
        /* create new xid entry */
        xid_add(p->xid, client_addr, client);
 
-       /* forward request to LAN (server) */
-       errno = 0;
-       res = sendto(fds[0], p, packet_len, 0, (struct sockaddr*)server_addr,
-                       sizeof(struct sockaddr_in));
-       if (res != packet_len) {
-               bb_perror_msg("sendto");
-       }
+       /* forward request to server */
+       /* note that we send from fds[0] which is bound to SERVER_PORT (67).
+        * IOW: we send _from_ SERVER_PORT! Although this may look strange,
+        * RFC 1542 not only allows, but prescribes this for BOOTP relays.
+        */
+       sendto_ip4(fds[0], p, packet_len, server_addr);
 }
 
 /**
@@ -203,7 +221,7 @@ static void pass_to_server(struct dhcp_packet *p, int packet_len, int client, in
  */
 static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds)
 {
-       int res, type;
+       int type;
        struct xid_item *item;
 
        /* check xid */
@@ -218,14 +236,12 @@ static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds)
                return;
        }
 
+//TODO: also do it if (p->flags & htons(BROADCAST_FLAG)) is set!
        if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY))
                item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST);
-       errno = 0;
-       res = sendto(fds[item->client], p, packet_len, 0, (struct sockaddr*) &(item->ip),
-                       sizeof(item->ip));
-       if (res != packet_len) {
-               bb_perror_msg("sendto");
-               return;
+
+       if (sendto_ip4(fds[item->client], p, packet_len, &item->ip) != 0) {
+               return; /* send error occurred */
        }
 
        /* remove xid entry */
@@ -235,36 +251,30 @@ static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds)
 int dhcprelay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int dhcprelay_main(int argc, char **argv)
 {
-       struct dhcp_packet dhcp_msg;
        struct sockaddr_in server_addr;
-       struct sockaddr_in client_addr;
-       fd_set rfds;
-       char **client_ifaces;
+       char **iface_list;
        int *fds;
        int num_sockets, max_socket;
        uint32_t our_nip;
 
        server_addr.sin_family = AF_INET;
+       server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
        server_addr.sin_port = htons(SERVER_PORT);
 
-       /* dhcprelay client_iface1,client_iface2,... server_iface [server_IP] */
+       /* dhcprelay CLIENT_IFACE1[,CLIENT_IFACE2...] SERVER_IFACE [SERVER_IP] */
        if (argc == 4) {
                if (!inet_aton(argv[3], &server_addr.sin_addr))
                        bb_perror_msg_and_die("bad server IP");
-       } else if (argc == 3) {
-               server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
-       } else {
+       } else if (argc != 3) {
                bb_show_usage();
        }
 
-       /* Produce list of client ifaces */
-       client_ifaces = get_client_devices(argv[1], &num_sockets);
+       iface_list = make_iface_list(argv + 1, &num_sockets);
 
-       num_sockets++; /* for server socket at fds[0] */
        fds = xmalloc(num_sockets * sizeof(fds[0]));
 
        /* Create sockets and bind one to every iface */
-       max_socket = init_sockets(client_ifaces, num_sockets, argv[2], fds);
+       max_socket = init_sockets(iface_list, num_sockets, fds);
 
        /* Get our IP on server_iface */
        if (udhcp_read_interface(argv[2], NULL, &our_nip, NULL))
@@ -272,11 +282,10 @@ int dhcprelay_main(int argc, char **argv)
 
        /* Main loop */
        while (1) {
-//reinit stuff from time to time? go back to get_client_devices
-//every N minutes?
+// reinit stuff from time to time? go back to make_iface_list
+// every N minutes?
+               fd_set rfds;
                struct timeval tv;
-               size_t packlen;
-               socklen_t addr_size;
                int i;
 
                FD_ZERO(&rfds);
@@ -285,6 +294,9 @@ int dhcprelay_main(int argc, char **argv)
                tv.tv_sec = SELECT_TIMEOUT;
                tv.tv_usec = 0;
                if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) {
+                       int packlen;
+                       struct dhcp_packet dhcp_msg;
+
                        /* server */
                        if (FD_ISSET(fds[0], &rfds)) {
                                packlen = udhcp_recv_kernel_packet(&dhcp_msg, fds[0]);
@@ -292,24 +304,65 @@ int dhcprelay_main(int argc, char **argv)
                                        pass_to_client(&dhcp_msg, packlen, fds);
                                }
                        }
+
                        /* clients */
                        for (i = 1; i < num_sockets; i++) {
+                               struct sockaddr_in client_addr;
+                               socklen_t addr_size;
+
                                if (!FD_ISSET(fds[i], &rfds))
                                        continue;
-                               addr_size = sizeof(struct sockaddr_in);
+
+                               addr_size = sizeof(client_addr);
                                packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0,
                                                (struct sockaddr *)(&client_addr), &addr_size);
                                if (packlen <= 0)
                                        continue;
 
                                /* Get our IP on corresponding client_iface */
-//why? what if server can't route such IP?
-                               if (udhcp_read_interface(client_ifaces[i-1], NULL, &dhcp_msg.gateway_nip, NULL)) {
-                                       /* Fall back to our server_iface's IP */
-//this makes more sense!
+// RFC 1542
+// 4.1 General BOOTP Processing for Relay Agents
+// 4.1.1 BOOTREQUEST Messages
+//   If the relay agent does decide to relay the request, it MUST examine
+//   the 'giaddr' ("gateway" IP address) field.  If this field is zero,
+//   the relay agent MUST fill this field with the IP address of the
+//   interface on which the request was received.  If the interface has
+//   more than one IP address logically associated with it, the relay
+//   agent SHOULD choose one IP address associated with that interface and
+//   use it consistently for all BOOTP messages it relays.  If the
+//   'giaddr' field contains some non-zero value, the 'giaddr' field MUST
+//   NOT be modified.  The relay agent MUST NOT, under any circumstances,
+//   fill the 'giaddr' field with a broadcast address as is suggested in
+//   [1] (Section 8, sixth paragraph).
+
+// but why? what if server can't route such IP? Client ifaces may be, say, NATed!
+
+// 4.1.2 BOOTREPLY Messages
+//   BOOTP relay agents relay BOOTREPLY messages only to BOOTP clients.
+//   It is the responsibility of BOOTP servers to send BOOTREPLY messages
+//   directly to the relay agent identified in the 'giaddr' field.
+// (yeah right, unless it is impossible... see comment above)
+//   Therefore, a relay agent may assume that all BOOTREPLY messages it
+//   receives are intended for BOOTP clients on its directly-connected
+//   networks.
+//
+//   When a relay agent receives a BOOTREPLY message, it should examine
+//   the BOOTP 'giaddr', 'yiaddr', 'chaddr', 'htype', and 'hlen' fields.
+//   These fields should provide adequate information for the relay agent
+//   to deliver the BOOTREPLY message to the client.
+//
+//   The 'giaddr' field can be used to identify the logical interface from
+//   which the reply must be sent (i.e., the host or router interface
+//   connected to the same network as the BOOTP client).  If the content
+//   of the 'giaddr' field does not match one of the relay agent's
+//   directly-connected logical interfaces, the BOOTREPLY messsage MUST be
+//   silently discarded.
+                               if (udhcp_read_interface(iface_list[i], NULL, &dhcp_msg.gateway_nip, NULL)) {
+                                       /* Fall back to our IP on server iface */
+// this makes more sense!
                                        dhcp_msg.gateway_nip = our_nip;
                                }
-//maybe set dhcp_msg.flags |= BROADCAST_FLAG too?
+// maybe dhcp_msg.hops++? drop packets with too many hops (RFC 1542 says 4 or 16)?
                                pass_to_server(&dhcp_msg, packlen, i, fds, &client_addr, &server_addr);
                        }
                }
index 2dd5347..c1325d8 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Loosely based on the isc-dhcpd implementation by dhankins@isc.org
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #ifdef DNS_COMPR_TESTING
 # define FAST_FUNC /* nothing */
index fb6219f..64cd73e 100644 (file)
@@ -1,22 +1,27 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define dumpleases_trivial_usage
+//usage:       "[-r|-a] [-f LEASEFILE]"
+//usage:#define dumpleases_full_usage "\n\n"
+//usage:       "Display DHCP leases granted by udhcpd\n"
+//usage:       IF_LONG_OPTS(
+//usage:     "\n       -f,--file=FILE  Lease file"
+//usage:     "\n       -r,--remaining  Show remaining time"
+//usage:     "\n       -a,--absolute   Show expiration time"
+//usage:       )
+//usage:       IF_NOT_LONG_OPTS(
+//usage:     "\n       -f FILE Lease file"
+//usage:     "\n       -r      Show remaining time"
+//usage:     "\n       -a      Show expiration time"
+//usage:       )
+
 #include "common.h"
 #include "dhcpd.h"
 #include "unicode.h"
 
-#if BB_LITTLE_ENDIAN
-static inline uint64_t hton64(uint64_t v)
-{
-        return (((uint64_t)htonl(v)) << 32) | htonl(v >> 32);
-}
-#else
-#define hton64(v) (v)
-#endif
-#define ntoh64(v) hton64(v)
-
-
 int dumpleases_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int dumpleases_main(int argc UNUSED_PARAM, char **argv)
 {
@@ -29,9 +34,9 @@ int dumpleases_main(int argc UNUSED_PARAM, char **argv)
        struct in_addr addr;
 
        enum {
-               OPT_a   = 0x1,  // -a
-               OPT_r   = 0x2,  // -r
-               OPT_f   = 0x4,  // -f
+               OPT_a = 0x1, // -a
+               OPT_r = 0x2, // -r
+               OPT_f = 0x4, // -f
        };
 #if ENABLE_LONG_OPTS
        static const char dumpleases_longopts[] ALIGN1 =
@@ -54,7 +59,7 @@ int dumpleases_main(int argc UNUSED_PARAM, char **argv)
        /*     "123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 */
 
        xread(fd, &written_at, sizeof(written_at));
-       written_at = ntoh64(written_at);
+       written_at = SWAP_BE64(written_at);
        curr = time(NULL);
        if (curr < written_at)
                written_at = curr; /* lease file from future! :) */
@@ -68,7 +73,7 @@ int dumpleases_main(int argc UNUSED_PARAM, char **argv)
                addr.s_addr = lease.lease_nip;
 #if ENABLE_UNICODE_SUPPORT
                {
-                       char *uni_name = unicode_conv_to_printable_fixedwidth(NULL, lease.hostname, 19);
+                       char *uni_name = unicode_conv_to_printable_fixedwidth(/*NULL,*/ lease.hostname, 19);
                        printf(" %-16s%s ", inet_ntoa(addr), uni_name);
                        free(uni_name);
                }
index ff63111..6840f3c 100644 (file)
@@ -4,23 +4,13 @@
  *
  * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include <netinet/ether.h>
 
 #include "common.h"
 #include "dhcpd.h"
 
-#if BB_LITTLE_ENDIAN
-static inline uint64_t hton64(uint64_t v)
-{
-        return (((uint64_t)htonl(v)) << 32) | htonl(v >> 32);
-}
-#else
-#define hton64(v) (v)
-#endif
-#define ntoh64(v) hton64(v)
-
 /* on these functions, make sure your datatype matches */
 static int FAST_FUNC read_str(const char *line, void *arg)
 {
@@ -90,9 +80,9 @@ static const struct config_keyword keywords[] = {
        /* keywords with no defaults must be last! */
        {"option"       , udhcp_str2optset, &server_config.options      , ""},
        {"opt"          , udhcp_str2optset, &server_config.options      , ""},
-       {"notify_file"  , read_str        , &server_config.notify_file  , ""},
-       {"sname"        , read_str        , &server_config.sname        , ""},
-       {"boot_file"    , read_str        , &server_config.boot_file    , ""},
+       {"notify_file"  , read_str        , &server_config.notify_file  , NULL},
+       {"sname"        , read_str        , &server_config.sname        , NULL},
+       {"boot_file"    , read_str        , &server_config.boot_file    , NULL},
        {"static_lease" , read_staticlease, &server_config.static_leases, ""},
 };
 enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 };
@@ -140,7 +130,7 @@ void FAST_FUNC write_leases(void)
 
        curr = written_at = time(NULL);
 
-       written_at = hton64(written_at);
+       written_at = SWAP_BE64(written_at);
        full_write(fd, &written_at, sizeof(written_at));
 
        for (i = 0; i < server_config.max_leases; i++) {
@@ -190,7 +180,7 @@ void FAST_FUNC read_leases(const char *file)
 
        if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at))
                goto ret;
-       written_at = ntoh64(written_at);
+       written_at = SWAP_BE64(written_at);
 
        time_passed = time(NULL) - written_at;
        /* Strange written_at, or lease file from old version of udhcpd
index fad71ec..c5b60b1 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Russ Dill <Russ.Dill@asu.edu> July 2001
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "common.h"
 #include "dhcpd.h"
@@ -137,21 +137,42 @@ uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac)
        uint32_t addr;
        struct dyn_lease *oldest_lease = NULL;
 
-       addr = server_config.start_ip; /* addr is in host order here */
-       for (; addr <= server_config.end_ip; addr++) {
+#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
+       uint32_t stop;
+       unsigned i, hash;
+
+       /* hash hwaddr: use the SDBM hashing algorithm.  Seems to give good
+        * dispersal even with similarly-valued "strings".
+        */
+       hash = 0;
+       for (i = 0; i < 6; i++)
+               hash += safe_mac[i] + (hash << 6) + (hash << 16) - hash;
+
+       /* pick a seed based on hwaddr then iterate until we find a free address. */
+       addr = server_config.start_ip
+               + (hash % (1 + server_config.end_ip - server_config.start_ip));
+       stop = addr;
+#else
+       addr = server_config.start_ip;
+#define stop (server_config.end_ip + 1)
+#endif
+       do {
                uint32_t nip;
                struct dyn_lease *lease;
 
                /* ie, 192.168.55.0 */
                if ((addr & 0xff) == 0)
-                       continue;
+                       goto next_addr;
                /* ie, 192.168.55.255 */
                if ((addr & 0xff) == 0xff)
-                       continue;
+                       goto next_addr;
                nip = htonl(addr);
+               /* skip our own address */
+               if (nip == server_config.server_nip)
+                       goto next_addr;
                /* is this a static lease addr? */
                if (is_nip_reserved(server_config.static_leases, nip))
-                       continue;
+                       goto next_addr;
 
                lease = find_lease_by_nip(nip);
                if (!lease) {
@@ -162,7 +183,14 @@ uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac)
                        if (!oldest_lease || lease->expires < oldest_lease->expires)
                                oldest_lease = lease;
                }
-       }
+
+ next_addr:
+               addr++;
+#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
+               if (addr > server_config.end_ip)
+                       addr = server_config.start_ip;
+#endif
+       } while (addr != stop);
 
        if (oldest_lease
         && is_expired_lease(oldest_lease)
index 4badc9c..148f525 100644 (file)
@@ -4,20 +4,13 @@
  *
  * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
-#include <netinet/in.h>
-#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
-# include <netpacket/packet.h>
-# include <net/ethernet.h>
-#else
-# include <asm/types.h>
-# include <linux/if_packet.h>
-# include <linux/if_ether.h>
-#endif
-
 #include "common.h"
 #include "dhcpd.h"
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netpacket/packet.h>
 
 void FAST_FUNC udhcp_init_header(struct dhcp_packet *packet, char type)
 {
@@ -88,7 +81,6 @@ void FAST_FUNC udhcp_dump_packet(struct dhcp_packet *packet)
 int FAST_FUNC udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd)
 {
        int bytes;
-       unsigned char *vendor;
 
        memset(packet, 0, sizeof(*packet));
        bytes = safe_read(fd, packet, sizeof(*packet));
@@ -97,74 +89,18 @@ int FAST_FUNC udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd)
                return bytes; /* returns -1 */
        }
 
-       if (packet->cookie != htonl(DHCP_MAGIC)) {
+       if (bytes < offsetof(struct dhcp_packet, options)
+        || packet->cookie != htonl(DHCP_MAGIC)
+       ) {
                bb_info_msg("Packet with bad magic, ignoring");
                return -2;
        }
        log1("Received a packet");
        udhcp_dump_packet(packet);
 
-       if (packet->op == BOOTREQUEST) {
-               vendor = udhcp_get_option(packet, DHCP_VENDOR);
-               if (vendor) {
-#if 0
-                       static const char broken_vendors[][8] = {
-                               "MSFT 98",
-                               ""
-                       };
-                       int i;
-                       for (i = 0; broken_vendors[i][0]; i++) {
-                               if (vendor[OPT_LEN - OPT_DATA] == (uint8_t)strlen(broken_vendors[i])
-                                && strncmp((char*)vendor, broken_vendors[i], vendor[OPT_LEN - OPT_DATA]) == 0
-                               ) {
-                                       log1("Broken client (%s), forcing broadcast replies",
-                                               broken_vendors[i]);
-                                       packet->flags |= htons(BROADCAST_FLAG);
-                               }
-                       }
-#else
-                       if (vendor[OPT_LEN - OPT_DATA] == (uint8_t)(sizeof("MSFT 98")-1)
-                        && memcmp(vendor, "MSFT 98", sizeof("MSFT 98")-1) == 0
-                       ) {
-                               log1("Broken client (%s), forcing broadcast replies", "MSFT 98");
-                               packet->flags |= htons(BROADCAST_FLAG);
-                       }
-#endif
-               }
-       }
-
        return bytes;
 }
 
-uint16_t FAST_FUNC udhcp_checksum(void *addr, int count)
-{
-       /* Compute Internet Checksum for "count" bytes
-        * beginning at location "addr".
-        */
-       int32_t sum = 0;
-       uint16_t *source = (uint16_t *) addr;
-
-       while (count > 1)  {
-               /*  This is the inner loop */
-               sum += *source++;
-               count -= 2;
-       }
-
-       /*  Add left-over byte, if any */
-       if (count > 0) {
-               /* Make sure that the left-over byte is added correctly both
-                * with little and big endian hosts */
-               uint16_t tmp = 0;
-               *(uint8_t*)&tmp = *(uint8_t*)source;
-               sum += tmp;
-       }
-       /*  Fold 32-bit sum to 16 bits */
-       while (sum >> 16)
-               sum = (sum & 0xffff) + (sum >> 16);
-
-       return ~sum;
-}
-
 /* Construct a ip/udp header for a packet, send packet */
 int FAST_FUNC udhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
                uint32_t source_nip, int source_port,
@@ -207,8 +143,15 @@ int FAST_FUNC udhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
         *
         * In order to work with those buggy servers,
         * we truncate packets after end option byte.
+        *
+        * However, RFC 1542 says "The IP Total Length and UDP Length
+        * must be large enough to contain the minimal BOOTP header of 300 octets".
+        * Thus, we retain enough padding to not go below 300 BOOTP bytes.
+        * Some devices have filters which drop DHCP packets shorter than that.
         */
        padding = DHCP_OPTIONS_BUFSIZE - 1 - udhcp_end_option(packet.data.options);
+       if (padding > DHCP_SIZE - 300)
+               padding = DHCP_SIZE - 300;
 
        packet.ip.protocol = IPPROTO_UDP;
        packet.ip.saddr = source_nip;
@@ -216,19 +159,20 @@ int FAST_FUNC udhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
        packet.udp.source = htons(source_port);
        packet.udp.dest = htons(dest_port);
        /* size, excluding IP header: */
-       packet.udp.len = htons(UPD_DHCP_SIZE - padding);
+       packet.udp.len = htons(UDP_DHCP_SIZE - padding);
        /* for UDP checksumming, ip.len is set to UDP packet len */
        packet.ip.tot_len = packet.udp.len;
-       packet.udp.check = udhcp_checksum(&packet, IP_UPD_DHCP_SIZE - padding);
+       packet.udp.check = inet_cksum((uint16_t *)&packet,
+                       IP_UDP_DHCP_SIZE - padding);
        /* but for sending, it is set to IP packet len */
-       packet.ip.tot_len = htons(IP_UPD_DHCP_SIZE - padding);
+       packet.ip.tot_len = htons(IP_UDP_DHCP_SIZE - padding);
        packet.ip.ihl = sizeof(packet.ip) >> 2;
        packet.ip.version = IPVERSION;
        packet.ip.ttl = IPDEFTTL;
-       packet.ip.check = udhcp_checksum(&packet.ip, sizeof(packet.ip));
+       packet.ip.check = inet_cksum((uint16_t *)&packet.ip, sizeof(packet.ip));
 
        udhcp_dump_packet(dhcp_pkt);
-       result = sendto(fd, &packet, IP_UPD_DHCP_SIZE - padding, /*flags:*/ 0,
+       result = sendto(fd, &packet, IP_UDP_DHCP_SIZE - padding, /*flags:*/ 0,
                        (struct sockaddr *) &dest_sll, sizeof(dest_sll));
        msg = "sendto";
  ret_close:
@@ -245,7 +189,7 @@ int FAST_FUNC udhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
                uint32_t source_nip, int source_port,
                uint32_t dest_nip, int dest_port)
 {
-       struct sockaddr_in client;
+       struct sockaddr_in sa;
        unsigned padding;
        int fd;
        int result = -1;
@@ -258,27 +202,28 @@ int FAST_FUNC udhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
        }
        setsockopt_reuseaddr(fd);
 
-       memset(&client, 0, sizeof(client));
-       client.sin_family = AF_INET;
-       client.sin_port = htons(source_port);
-       client.sin_addr.s_addr = source_nip;
-       if (bind(fd, (struct sockaddr *)&client, sizeof(client)) == -1) {
+       memset(&sa, 0, sizeof(sa));
+       sa.sin_family = AF_INET;
+       sa.sin_port = htons(source_port);
+       sa.sin_addr.s_addr = source_nip;
+       if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
                msg = "bind(%s)";
                goto ret_close;
        }
 
-       memset(&client, 0, sizeof(client));
-       client.sin_family = AF_INET;
-       client.sin_port = htons(dest_port);
-       client.sin_addr.s_addr = dest_nip;
-       if (connect(fd, (struct sockaddr *)&client, sizeof(client)) == -1) {
+       memset(&sa, 0, sizeof(sa));
+       sa.sin_family = AF_INET;
+       sa.sin_port = htons(dest_port);
+       sa.sin_addr.s_addr = dest_nip;
+       if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
                msg = "connect";
                goto ret_close;
        }
 
        udhcp_dump_packet(dhcp_pkt);
-
        padding = DHCP_OPTIONS_BUFSIZE - 1 - udhcp_end_option(dhcp_pkt->options);
+       if (padding > DHCP_SIZE - 300)
+               padding = DHCP_SIZE - 300;
        result = safe_write(fd, dhcp_pkt, DHCP_SIZE - padding);
        msg = "write";
  ret_close:
index 0ed7ad1..a421069 100644 (file)
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-#include <net/if.h>
-#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
-# include <netpacket/packet.h>
-# include <net/ethernet.h>
-#else
-# include <asm/types.h>
-# include <linux/if_packet.h>
-# include <linux/if_ether.h>
-#endif
-
 #include "common.h"
+#include <net/if.h>
 
 int FAST_FUNC udhcp_read_interface(const char *interface, int *ifindex, uint32_t *nip, uint8_t *mac)
 {
+       /* char buffer instead of bona-fide struct avoids aliasing warning */
+       char ifr_buf[sizeof(struct ifreq)];
+       struct ifreq *const ifr = (void *)ifr_buf;
+
        int fd;
-       struct ifreq ifr;
        struct sockaddr_in *our_ip;
 
-       memset(&ifr, 0, sizeof(ifr));
+       memset(ifr, 0, sizeof(*ifr));
        fd = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW);
 
-       ifr.ifr_addr.sa_family = AF_INET;
-       strncpy_IFNAMSIZ(ifr.ifr_name, interface);
+       ifr->ifr_addr.sa_family = AF_INET;
+       strncpy_IFNAMSIZ(ifr->ifr_name, interface);
        if (nip) {
-               if (ioctl_or_perror(fd, SIOCGIFADDR, &ifr,
+               if (ioctl_or_perror(fd, SIOCGIFADDR, ifr,
                        "is interface %s up and configured?", interface)
                ) {
                        close(fd);
                        return -1;
                }
-               our_ip = (struct sockaddr_in *) &ifr.ifr_addr;
+               our_ip = (struct sockaddr_in *) &ifr->ifr_addr;
                *nip = our_ip->sin_addr.s_addr;
                log1("IP %s", inet_ntoa(our_ip->sin_addr));
        }
 
        if (ifindex) {
-               if (ioctl_or_warn(fd, SIOCGIFINDEX, &ifr) != 0) {
+               if (ioctl_or_warn(fd, SIOCGIFINDEX, ifr) != 0) {
                        close(fd);
                        return -1;
                }
-               log1("Adapter index %d", ifr.ifr_ifindex);
-               *ifindex = ifr.ifr_ifindex;
+               log1("Adapter index %d", ifr->ifr_ifindex);
+               *ifindex = ifr->ifr_ifindex;
        }
 
        if (mac) {
-               if (ioctl_or_warn(fd, SIOCGIFHWADDR, &ifr) != 0) {
+               if (ioctl_or_warn(fd, SIOCGIFHWADDR, ifr) != 0) {
                        close(fd);
                        return -1;
                }
-               memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);
+               memcpy(mac, ifr->ifr_hwaddr.sa_data, 6);
                log1("MAC %02x:%02x:%02x:%02x:%02x:%02x",
                        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
        }
@@ -86,6 +80,7 @@ int FAST_FUNC udhcp_listen_socket(/*uint32_t ip,*/ int port, const char *inf)
 {
        int fd;
        struct sockaddr_in addr;
+       char *colon;
 
        log1("Opening listen socket on *:%d %s", port, inf);
        fd = xsocket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
@@ -94,10 +89,17 @@ int FAST_FUNC udhcp_listen_socket(/*uint32_t ip,*/ int port, const char *inf)
        if (setsockopt_broadcast(fd) == -1)
                bb_perror_msg_and_die("SO_BROADCAST");
 
-       /* NB: bug 1032 says this doesn't work on ethernet aliases (ethN:M) */
+       /* SO_BINDTODEVICE doesn't work on ethernet aliases (ethN:M) */
+       colon = strrchr(inf, ':');
+       if (colon)
+               *colon = '\0';
+
        if (setsockopt_bindtodevice(fd, inf))
                xfunc_die(); /* warning is already printed */
 
+       if (colon)
+               *colon = ':';
+
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
index b334a58..f4a24ab 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Wade Berrier <wberrier@myrealbox.com> September 2004
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "common.h"
 #include "dhcpd.h"
index 00379fc..924b2f0 100644 (file)
@@ -4,11 +4,22 @@
  *
  * Copyright (C) 2001  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 N/A */
 
+//usage:#define vconfig_trivial_usage
+//usage:       "COMMAND [OPTIONS]"
+//usage:#define vconfig_full_usage "\n\n"
+//usage:       "Create and remove virtual ethernet devices\n"
+//usage:     "\n       add             IFACE VLAN_ID"
+//usage:     "\n       rem             VLAN_NAME"
+//usage:     "\n       set_flag        IFACE 0|1 VLAN_QOS"
+//usage:     "\n       set_egress_map  VLAN_NAME SKB_PRIO VLAN_QOS"
+//usage:     "\n       set_ingress_map VLAN_NAME SKB_PRIO VLAN_QOS"
+//usage:     "\n       set_name_type   NAME_TYPE"
+
 #include "libbb.h"
 #include <net/if.h>
 
@@ -47,66 +58,48 @@ struct vlan_ioctl_args {
        short vlan_qos;
 };
 
-#define VLAN_GROUP_ARRAY_LEN 4096
-#define SIOCSIFVLAN    0x8983          /* Set 802.1Q VLAN options */
+#define VLAN_GROUP_ARRAY_LEN  4096
+#define SIOCSIFVLAN           0x8983  /* Set 802.1Q VLAN options */
 
 /* On entry, table points to the length of the current string
  * plus NUL terminator plus data length for the subsequent entry.
  * The return value is the last data entry for the matching string. */
 static const char *xfind_str(const char *table, const char *str)
 {
-       while (strcasecmp(str, table+1) != 0) {
-               table += table[0];
-               if (!*table) {
+       while (strcasecmp(str, table + 1) != 0) {
+               if (!table[0])
                        bb_show_usage();
-               }
+               table += table[0];
        }
        return table - 1;
 }
 
 static const char cmds[] ALIGN1 = {
        4, ADD_VLAN_CMD, 7,
-       'a', 'd', 'd', 0,
+       'a','d','d',0,
        3, DEL_VLAN_CMD, 7,
-       'r', 'e', 'm', 0,
+       'r','e','m',0,
        3, SET_VLAN_NAME_TYPE_CMD, 17,
-       's', 'e', 't', '_',
-       'n', 'a', 'm', 'e', '_',
-       't', 'y', 'p', 'e', 0,
+       's','e','t','_','n','a','m','e','_','t','y','p','e',0,
        5, SET_VLAN_FLAG_CMD, 12,
-       's', 'e', 't', '_',
-       'f', 'l', 'a', 'g', 0,
+       's','e','t','_','f','l','a','g',0,
        5, SET_VLAN_EGRESS_PRIORITY_CMD, 18,
-       's', 'e', 't', '_',
-       'e', 'g', 'r', 'e', 's', 's', '_',
-       'm', 'a', 'p', 0,
-       5, SET_VLAN_INGRESS_PRIORITY_CMD, 16,
-       's', 'e', 't', '_',
-       'i', 'n', 'g', 'r', 'e', 's', 's', '_',
-       'm', 'a', 'p', 0,
+       's','e','t','_','e','g','r','e','s','s','_','m','a','p',0,
+       5, SET_VLAN_INGRESS_PRIORITY_CMD, 0,
+       's','e','t','_','i','n','g','r','e','s','s','_','m','a','p',0,
 };
 
 static const char name_types[] ALIGN1 = {
        VLAN_NAME_TYPE_PLUS_VID, 16,
-       'V', 'L', 'A', 'N',
-       '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
-       0,
+       'V','L','A','N','_','P','L','U','S','_','V','I','D',0,
        VLAN_NAME_TYPE_PLUS_VID_NO_PAD, 22,
-       'V', 'L', 'A', 'N',
-       '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
-       '_', 'N', 'O', '_', 'P', 'A', 'D', 0,
+       'V','L','A','N','_','P','L','U','S','_','V','I','D','_','N','O','_','P','A','D',0,
        VLAN_NAME_TYPE_RAW_PLUS_VID, 15,
-       'D', 'E', 'V',
-       '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
-       0,
-       VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, 20,
-       'D', 'E', 'V',
-       '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
-       '_', 'N', 'O', '_', 'P', 'A', 'D', 0,
+       'D','E','V','_','P','L','U','S','_','V','I','D',0,
+       VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, 0,
+       'D','E','V','_','P','L','U','S','_','V','I','D','_','N','O','_','P','A','D',0,
 };
 
-static const char conf_file_name[] ALIGN1 = "/proc/net/vlan/config";
-
 int vconfig_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int vconfig_main(int argc, char **argv)
 {
@@ -114,25 +107,19 @@ int vconfig_main(int argc, char **argv)
        const char *p;
        int fd;
 
-       if (argc < 3) {
-               bb_show_usage();
-       }
-
-       /* Don't bother closing the filedes.  It will be closed on cleanup. */
-       /* Will die if 802.1q is not present */
-       xopen(conf_file_name, O_RDONLY);
-
        memset(&ifr, 0, sizeof(ifr));
 
        ++argv;
-       p = xfind_str(cmds+2, *argv);
+       if (!argv[0])
+               bb_show_usage();
+       p = xfind_str(cmds + 2, argv[0]);
        ifr.cmd = *p;
-       if (argc != p[-1]) {
+       if (argc != p[-1])
                bb_show_usage();
-       }
 
-       if (ifr.cmd == SET_VLAN_NAME_TYPE_CMD) { /* set_name_type */
-               ifr.u.name_type = *xfind_str(name_types+1, argv[1]);
+       if (ifr.cmd == SET_VLAN_NAME_TYPE_CMD) {
+               /* set_name_type */
+               ifr.u.name_type = *xfind_str(name_types + 1, argv[1]);
        } else {
                strncpy_IFNAMSIZ(ifr.device1, argv[1]);
                p = argv[2];
@@ -141,22 +128,26 @@ int vconfig_main(int argc, char **argv)
                 * since ifr.u.flag, ifr.u.VID, and ifr.u.skb_priority are all same-sized
                 * (unsigned) int members of a unions.  But because of the range checking,
                 * doing so wouldn't save that much space and would also make maintainence
-                * more of a pain. */
-               if (ifr.cmd == SET_VLAN_FLAG_CMD) { /* set_flag */
-                       ifr.u.flag = xatoul_range(p, 0, 1);
+                * more of a pain.
+                */
+               if (ifr.cmd == SET_VLAN_FLAG_CMD) {
+                       /* set_flag */
+                       ifr.u.flag = xatou_range(p, 0, 1);
                        /* DM: in order to set reorder header, qos must be set */
-                       ifr.vlan_qos = xatoul_range(argv[3], 0, 7);
-               } else if (ifr.cmd == ADD_VLAN_CMD) { /* add */
-                       ifr.u.VID = xatoul_range(p, 0, VLAN_GROUP_ARRAY_LEN-1);
-               } else if (ifr.cmd != DEL_VLAN_CMD) { /* set_{egress|ingress}_map */
+                       ifr.vlan_qos = xatou_range(argv[3], 0, 7);
+               } else if (ifr.cmd == ADD_VLAN_CMD) {
+                       /* add */
+                       ifr.u.VID = xatou_range(p, 0, VLAN_GROUP_ARRAY_LEN - 1);
+               } else if (ifr.cmd != DEL_VLAN_CMD) {
+                       /* set_{egress|ingress}_map */
                        ifr.u.skb_priority = xatou(p);
-                       ifr.vlan_qos = xatoul_range(argv[3], 0, 7);
+                       ifr.vlan_qos = xatou_range(argv[3], 0, 7);
                }
        }
 
        fd = xsocket(AF_INET, SOCK_STREAM, 0);
        ioctl_or_perror_and_die(fd, SIOCSIFVLAN, &ifr,
-                                               "ioctl error for %s", *argv);
+                                               "ioctl error for %s", argv[0]);
 
        return 0;
 }
index 1f35f8b..d6c509e 100644 (file)
@@ -3,14 +3,48 @@
  * wget - retrieve a file using HTTP or FTP
  *
  * Chip Rosenthal Covad Communications <chip@laserlink.net>
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Copyright (C) 2010 Bradley M. Kuhn <bkuhn@ebb.org>
+ * Kuhn's copyrights are licensed GPLv2-or-later.  File as a whole remains GPLv2.
  */
+
+//usage:#define wget_trivial_usage
+//usage:       IF_FEATURE_WGET_LONG_OPTIONS(
+//usage:       "[-c|--continue] [-s|--spider] [-q|--quiet] [-O|--output-document FILE]\n"
+//usage:       "       [--header 'header: value'] [-Y|--proxy on/off] [-P DIR]\n"
+/* Since we ignore these opts, we don't show them in --help */
+/* //usage:    "       [--no-check-certificate] [--no-cache]" */
+//usage:       "       [-U|--user-agent AGENT]" IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..."
+//usage:       )
+//usage:       IF_NOT_FEATURE_WGET_LONG_OPTIONS(
+//usage:       "[-csq] [-O FILE] [-Y on/off] [-P DIR] [-U AGENT]"
+//usage:                       IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..."
+//usage:       )
+//usage:#define wget_full_usage "\n\n"
+//usage:       "Retrieve files via HTTP or FTP\n"
+//usage:     "\n       -s      Spider mode - only check file existence"
+//usage:     "\n       -c      Continue retrieval of aborted transfer"
+//usage:     "\n       -q      Quiet"
+//usage:     "\n       -P DIR  Save to DIR (default .)"
+//usage:       IF_FEATURE_WGET_TIMEOUT(
+//usage:     "\n       -T SEC  Network read timeout is SEC seconds"
+//usage:       )
+//usage:     "\n       -O FILE Save to FILE ('-' for stdout)"
+//usage:     "\n       -U STR  Use STR for User-Agent header"
+//usage:     "\n       -Y      Use proxy ('on' or 'off')"
+
 #include "libbb.h"
 
+#if 0
+# define log_io(...) bb_error_msg(__VA_ARGS__)
+#else
+# define log_io(...) ((void)0)
+#endif
+
+
 struct host_info {
-       // May be used if we ever will want to free() all xstrdup()s...
-       /* char *allocated; */
+       char *allocated;
        const char *path;
        const char *user;
        char       *host;
@@ -19,7 +53,7 @@ struct host_info {
 };
 
 
-/* Globals (can be accessed from signal handlers) */
+/* Globals */
 struct globals {
        off_t content_len;        /* Content-length of the file */
        off_t beg_range;          /* Range at which continue begins */
@@ -28,49 +62,82 @@ struct globals {
        const char *curfile;      /* Name of current file being transferred */
        bb_progress_t pmt;
 #endif
+       char *dir_prefix;
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+       char *post_data;
+       char *extra_headers;
+#endif
+       char *fname_out;        /* where to direct output (-O) */
+       const char *proxy_flag; /* Use proxies if env vars are set */
+       const char *user_agent; /* "User-Agent" header field */
+#if ENABLE_FEATURE_WGET_TIMEOUT
+       unsigned timeout_seconds;
+       bool connecting;
+#endif
+       int output_fd;
+       int o_flags;
        smallint chunked;         /* chunked transfer encoding */
        smallint got_clen;        /* got content-length: from server  */
+       /* Local downloads do benefit from big buffer.
+        * With 512 byte buffer, it was measured to be
+        * an order of magnitude slower than with big one.
+        */
+       uint64_t just_to_align_next_member;
+       char wget_buf[CONFIG_FEATURE_COPYBUF_KB*1024];
 } FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-struct BUG_G_too_big {
-       char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
-};
-#define INIT_G() do { } while (0)
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+       SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+#define FINI_G() do { \
+       FREE_PTR_TO_GLOBALS(); \
+} while (0)
 
 
-#if ENABLE_FEATURE_WGET_STATUSBAR
+/* Must match option string! */
+enum {
+       WGET_OPT_CONTINUE   = (1 << 0),
+       WGET_OPT_SPIDER     = (1 << 1),
+       WGET_OPT_QUIET      = (1 << 2),
+       WGET_OPT_OUTNAME    = (1 << 3),
+       WGET_OPT_PREFIX     = (1 << 4),
+       WGET_OPT_PROXY      = (1 << 5),
+       WGET_OPT_USER_AGENT = (1 << 6),
+       WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 7),
+       WGET_OPT_RETRIES    = (1 << 8),
+       WGET_OPT_PASSIVE    = (1 << 9),
+       WGET_OPT_HEADER     = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
+       WGET_OPT_POST_DATA  = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
+};
 
+enum {
+       PROGRESS_START = -1,
+       PROGRESS_END   = 0,
+       PROGRESS_BUMP  = 1,
+};
+#if ENABLE_FEATURE_WGET_STATUSBAR
 static void progress_meter(int flag)
 {
-       /* We can be called from signal handler */
-       int save_errno = errno;
+       if (option_mask32 & WGET_OPT_QUIET)
+               return;
 
-       if (flag == -1) { /* first call to progress_meter */
-               bb_progress_init(&G.pmt);
-       }
+       if (flag == PROGRESS_START)
+               bb_progress_init(&G.pmt, G.curfile);
 
-       bb_progress_update(&G.pmt, G.curfile, G.beg_range, G.transferred,
-                          G.chunked ? 0 : G.beg_range + G.transferred + G.content_len);
+       bb_progress_update(&G.pmt,
+                       G.beg_range,
+                       G.transferred,
+                       (G.chunked || !G.got_clen) ? 0 : G.beg_range + G.transferred + G.content_len
+       );
 
-       if (flag == 0) {
-               /* last call to progress_meter */
-               alarm(0);
+       if (flag == PROGRESS_END) {
+               bb_progress_free(&G.pmt);
                bb_putchar_stderr('\n');
                G.transferred = 0;
-       } else {
-               if (flag == -1) { /* first call to progress_meter */
-                       signal_SA_RESTART_empty_mask(SIGALRM, progress_meter);
-               }
-               alarm(1);
        }
-
-       errno = save_errno;
 }
-
-#else /* FEATURE_WGET_STATUSBAR */
-
+#else
 static ALWAYS_INLINE void progress_meter(int flag UNUSED_PARAM) { }
-
 #endif
 
 
@@ -110,48 +177,15 @@ static void strip_ipv6_scope_id(char *host)
        overlapping_strcpy(scope, cp);
 }
 
-/* Read NMEMB bytes into PTR from STREAM.  Returns the number of bytes read,
- * and a short count if an eof or non-interrupt error is encountered.  */
-static size_t safe_fread(void *ptr, size_t nmemb, FILE *stream)
-{
-       size_t ret;
-       char *p = (char*)ptr;
-
-       do {
-               clearerr(stream);
-               errno = 0;
-               ret = fread(p, 1, nmemb, stream);
-               p += ret;
-               nmemb -= ret;
-       } while (nmemb && ferror(stream) && errno == EINTR);
-
-       return p - (char*)ptr;
-}
-
-/* Read a line or SIZE-1 bytes into S, whichever is less, from STREAM.
- * Returns S, or NULL if an eof or non-interrupt error is encountered.  */
-static char *safe_fgets(char *s, int size, FILE *stream)
-{
-       char *ret;
-
-       do {
-               clearerr(stream);
-               errno = 0;
-               ret = fgets(s, size, stream);
-       } while (ret == NULL && ferror(stream) && errno == EINTR);
-
-       return ret;
-}
-
 #if ENABLE_FEATURE_WGET_AUTHENTICATION
-/* Base64-encode character string. buf is assumed to be char buf[512]. */
-static char *base64enc_512(char buf[512], const char *str)
+/* Base64-encode character string. */
+static char *base64enc(const char *str)
 {
        unsigned len = strlen(str);
-       if (len > 512/4*3 - 10) /* paranoia */
-               len = 512/4*3 - 10;
-       bb_uuencode(buf, str, len, bb_uuenc_tbl_base64);
-       return buf;
+       if (len > sizeof(G.wget_buf)/4*3 - 10) /* paranoia */
+               len = sizeof(G.wget_buf)/4*3 - 10;
+       bb_uuencode(G.wget_buf, str, len, bb_uuenc_tbl_base64);
+       return G.wget_buf;
 }
 #endif
 
@@ -164,61 +198,98 @@ static char* sanitize_string(char *s)
        return s;
 }
 
+#if ENABLE_FEATURE_WGET_TIMEOUT
+static void alarm_handler(int sig UNUSED_PARAM)
+{
+       /* This is theoretically unsafe (uses stdio and malloc in signal handler) */
+       if (G.connecting)
+               bb_error_msg_and_die("download timed out");
+}
+#endif
+
 static FILE *open_socket(len_and_sockaddr *lsa)
 {
+       int fd;
        FILE *fp;
 
+       IF_FEATURE_WGET_TIMEOUT(alarm(G.timeout_seconds); G.connecting = 1;)
+       fd = xconnect_stream(lsa);
+       IF_FEATURE_WGET_TIMEOUT(G.connecting = 0;)
+
        /* glibc 2.4 seems to try seeking on it - ??! */
        /* hopefully it understands what ESPIPE means... */
-       fp = fdopen(xconnect_stream(lsa), "r+");
+       fp = fdopen(fd, "r+");
        if (fp == NULL)
-               bb_perror_msg_and_die("fdopen");
+               bb_perror_msg_and_die(bb_msg_memory_exhausted);
 
        return fp;
 }
 
-static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf)
+/* Returns '\n' if it was seen, else '\0'. Trims at first '\r' or '\n' */
+/* FIXME: does not respect FEATURE_WGET_TIMEOUT and -T N: */
+static char fgets_and_trim(FILE *fp)
+{
+       char c;
+       char *buf_ptr;
+
+       if (fgets(G.wget_buf, sizeof(G.wget_buf) - 1, fp) == NULL)
+               bb_perror_msg_and_die("error getting response");
+
+       buf_ptr = strchrnul(G.wget_buf, '\n');
+       c = *buf_ptr;
+       *buf_ptr = '\0';
+       buf_ptr = strchrnul(G.wget_buf, '\r');
+       *buf_ptr = '\0';
+
+       log_io("< %s", G.wget_buf);
+
+       return c;
+}
+
+static int ftpcmd(const char *s1, const char *s2, FILE *fp)
 {
        int result;
        if (s1) {
-               if (!s2) s2 = "";
+               if (!s2)
+                       s2 = "";
                fprintf(fp, "%s%s\r\n", s1, s2);
                fflush(fp);
+               log_io("> %s%s", s1, s2);
        }
 
        do {
-               char *buf_ptr;
-
-               if (fgets(buf, 510, fp) == NULL) {
-                       bb_perror_msg_and_die("error getting response");
-               }
-               buf_ptr = strstr(buf, "\r\n");
-               if (buf_ptr) {
-                       *buf_ptr = '\0';
-               }
-       } while (!isdigit(buf[0]) || buf[3] != ' ');
+               fgets_and_trim(fp);
+       } while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' ');
 
-       buf[3] = '\0';
-       result = xatoi_u(buf);
-       buf[3] = ' ';
+       G.wget_buf[3] = '\0';
+       result = xatoi_positive(G.wget_buf);
+       G.wget_buf[3] = ' ';
        return result;
 }
 
-static void parse_url(char *src_url, struct host_info *h)
+static void parse_url(const char *src_url, struct host_info *h)
 {
        char *url, *p, *sp;
 
-       /* h->allocated = */ url = xstrdup(src_url);
+       free(h->allocated);
+       h->allocated = url = xstrdup(src_url);
 
-       if (strncmp(url, "http://", 7) == 0) {
-               h->port = bb_lookup_port("http", "tcp", 80);
-               h->host = url + 7;
-               h->is_ftp = 0;
-       } else if (strncmp(url, "ftp://", 6) == 0) {
+       if (strncmp(url, "ftp://", 6) == 0) {
                h->port = bb_lookup_port("ftp", "tcp", 21);
                h->host = url + 6;
                h->is_ftp = 1;
        } else
+       if (strncmp(url, "http://", 7) == 0) {
+               h->host = url + 7;
+ http:
+               h->port = bb_lookup_port("http", "tcp", 80);
+               h->is_ftp = 0;
+       } else
+       if (!strstr(url, "//")) {
+               // GNU wget is user-friendly and falls back to http://
+               h->host = url;
+               goto http;
+       } else
                bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url));
 
        // FYI:
@@ -256,103 +327,73 @@ static void parse_url(char *src_url, struct host_info *h)
 
        sp = strrchr(h->host, '@');
        if (sp != NULL) {
-               h->user = h->host;
+               // URL-decode "user:password" string before base64-encoding:
+               // wget http://test:my%20pass@example.com should send
+               // Authorization: Basic dGVzdDpteSBwYXNz
+               // which decodes to "test:my pass".
+               // Standard wget and curl do this too.
                *sp = '\0';
+               h->user = percent_decode_in_place(h->host, /*strict:*/ 0);
                h->host = sp + 1;
        }
 
        sp = h->host;
 }
 
-static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
+static char *gethdr(FILE *fp)
 {
        char *s, *hdrval;
        int c;
 
-       /* *istrunc = 0; */
-
        /* retrieve header line */
-       if (fgets(buf, bufsiz, fp) == NULL)
-               return NULL;
+       c = fgets_and_trim(fp);
 
-       /* see if we are at the end of the headers */
-       for (s = buf; *s == '\r'; ++s)
-               continue;
-       if (*s == '\n')
+       /* end of the headers? */
+       if (G.wget_buf[0] == '\0')
                return NULL;
 
        /* convert the header name to lower case */
-       for (s = buf; isalnum(*s) || *s == '-' || *s == '.'; ++s) {
-               /* tolower for "A-Z", no-op for "0-9a-z-." */
-               *s = (*s | 0x20);
+       for (s = G.wget_buf; isalnum(*s) || *s == '-' || *s == '.' || *s == '_'; ++s) {
+               /*
+                * No-op for 20-3f and 60-7f. "0-9a-z-." are in these ranges.
+                * 40-5f range ("@A-Z[\]^_") maps to 60-7f.
+                * "A-Z" maps to "a-z".
+                * "@[\]" can't occur in header names.
+                * "^_" maps to "~,DEL" (which is wrong).
+                * "^" was never seen yet, "_" was seen from web.archive.org
+                * (x-archive-orig-x_commoncrawl_Signature: HEXSTRING).
+                */
+               *s |= 0x20;
        }
 
        /* verify we are at the end of the header name */
        if (*s != ':')
-               bb_error_msg_and_die("bad header line: %s", sanitize_string(buf));
+               bb_error_msg_and_die("bad header line: %s", sanitize_string(G.wget_buf));
 
        /* locate the start of the header value */
        *s++ = '\0';
        hdrval = skip_whitespace(s);
 
-       /* locate the end of header */
-       while (*s && *s != '\r' && *s != '\n')
-               ++s;
-
-       /* end of header found */
-       if (*s) {
-               *s = '\0';
-               return hdrval;
+       if (c != '\n') {
+               /* Rats! The buffer isn't big enough to hold the entire header value */
+               while (c = getc(fp), c != EOF && c != '\n')
+                       continue;
        }
 
-       /* Rats! The buffer isn't big enough to hold the entire header value */
-       while (c = getc(fp), c != EOF && c != '\n')
-               continue;
-       /* *istrunc = 1; */
        return hdrval;
 }
 
-#if ENABLE_FEATURE_WGET_LONG_OPTIONS
-static char *URL_escape(const char *str)
+static void reset_beg_range_to_zero(void)
 {
-       /* URL encode, see RFC 2396 */
-       char *dst;
-       char *res = dst = xmalloc(strlen(str) * 3 + 1);
-       unsigned char c;
-
-       while (1) {
-               c = *str++;
-               if (c == '\0'
-               /* || strchr("!&'()*-.=_~", c) - more code */
-                || c == '!'
-                || c == '&'
-                || c == '\''
-                || c == '('
-                || c == ')'
-                || c == '*'
-                || c == '-'
-                || c == '.'
-                || c == '='
-                || c == '_'
-                || c == '~'
-                || (c >= '0' && c <= '9')
-                || ((c|0x20) >= 'a' && (c|0x20) <= 'z')
-               ) {
-                       *dst++ = c;
-                       if (c == '\0')
-                               return res;
-               } else {
-                       *dst++ = '%';
-                       *dst++ = bb_hexdigits_upcase[c >> 4];
-                       *dst++ = bb_hexdigits_upcase[c & 0xf];
-               }
-       }
+       bb_error_msg("restart failed");
+       G.beg_range = 0;
+       xlseek(G.output_fd, 0, SEEK_SET);
+       /* Done at the end instead: */
+       /* ftruncate(G.output_fd, 0); */
 }
-#endif
 
 static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa)
 {
-       char buf[512];
        FILE *sfp;
        char *str;
        int port;
@@ -361,8 +402,8 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
                target->user = xstrdup("anonymous:busybox@");
 
        sfp = open_socket(lsa);
-       if (ftpcmd(NULL, NULL, sfp, buf) != 220)
-               bb_error_msg_and_die("%s", sanitize_string(buf+4));
+       if (ftpcmd(NULL, NULL, sfp) != 220)
+               bb_error_msg_and_die("%s", sanitize_string(G.wget_buf + 4));
 
        /*
         * Splitting username:password pair,
@@ -371,24 +412,24 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
        str = strchr(target->user, ':');
        if (str)
                *str++ = '\0';
-       switch (ftpcmd("USER ", target->user, sfp, buf)) {
+       switch (ftpcmd("USER ", target->user, sfp)) {
        case 230:
                break;
        case 331:
-               if (ftpcmd("PASS ", str, sfp, buf) == 230)
+               if (ftpcmd("PASS ", str, sfp) == 230)
                        break;
                /* fall through (failed login) */
        default:
-               bb_error_msg_and_die("ftp login: %s", sanitize_string(buf+4));
+               bb_error_msg_and_die("ftp login: %s", sanitize_string(G.wget_buf + 4));
        }
 
-       ftpcmd("TYPE I", NULL, sfp, buf);
+       ftpcmd("TYPE I", NULL, sfp);
 
        /*
         * Querying file size
         */
-       if (ftpcmd("SIZE ", target->path, sfp, buf) == 213) {
-               G.content_len = BB_STRTOOFF(buf+4, NULL, 10);
+       if (ftpcmd("SIZE ", target->path, sfp) == 213) {
+               G.content_len = BB_STRTOOFF(G.wget_buf + 4, NULL, 10);
                if (G.content_len < 0 || errno) {
                        bb_error_msg_and_die("SIZE value is garbage");
                }
@@ -398,212 +439,222 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
        /*
         * Entering passive mode
         */
-       if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
+       if (ftpcmd("PASV", NULL, sfp) != 227) {
  pasv_error:
-               bb_error_msg_and_die("bad response to %s: %s", "PASV", sanitize_string(buf));
+               bb_error_msg_and_die("bad response to %s: %s", "PASV", sanitize_string(G.wget_buf));
        }
        // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
        // Server's IP is N1.N2.N3.N4 (we ignore it)
        // Server's port for data connection is P1*256+P2
-       str = strrchr(buf, ')');
+       str = strrchr(G.wget_buf, ')');
        if (str) str[0] = '\0';
-       str = strrchr(buf, ',');
+       str = strrchr(G.wget_buf, ',');
        if (!str) goto pasv_error;
        port = xatou_range(str+1, 0, 255);
        *str = '\0';
-       str = strrchr(buf, ',');
+       str = strrchr(G.wget_buf, ',');
        if (!str) goto pasv_error;
        port += xatou_range(str+1, 0, 255) * 256;
-       set_nport(lsa, htons(port));
+       set_nport(&lsa->u.sa, htons(port));
 
        *dfpp = open_socket(lsa);
 
-       if (G.beg_range) {
-               sprintf(buf, "REST %"OFF_FMT"u", G.beg_range);
-               if (ftpcmd(buf, NULL, sfp, buf) == 350)
+       if (G.beg_range != 0) {
+               sprintf(G.wget_buf, "REST %"OFF_FMT"u", G.beg_range);
+               if (ftpcmd(G.wget_buf, NULL, sfp) == 350)
                        G.content_len -= G.beg_range;
+               else
+                       reset_beg_range_to_zero();
        }
 
-       if (ftpcmd("RETR ", target->path, sfp, buf) > 150)
-               bb_error_msg_and_die("bad response to %s: %s", "RETR", sanitize_string(buf));
+       if (ftpcmd("RETR ", target->path, sfp) > 150)
+               bb_error_msg_and_die("bad response to %s: %s", "RETR", sanitize_string(G.wget_buf));
 
        return sfp;
 }
 
-/* Must match option string! */
-enum {
-       WGET_OPT_CONTINUE   = (1 << 0),
-       WGET_OPT_SPIDER     = (1 << 1),
-       WGET_OPT_QUIET      = (1 << 2),
-       WGET_OPT_OUTNAME    = (1 << 3),
-       WGET_OPT_PREFIX     = (1 << 4),
-       WGET_OPT_PROXY      = (1 << 5),
-       WGET_OPT_USER_AGENT = (1 << 6),
-       WGET_OPT_RETRIES    = (1 << 7),
-       WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 8),
-       WGET_OPT_PASSIVE    = (1 << 9),
-       WGET_OPT_HEADER     = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
-       WGET_OPT_POST_DATA  = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
-};
-
-static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
+static void NOINLINE retrieve_file_data(FILE *dfp)
 {
-       char buf[512];
-
-       if (!(option_mask32 & WGET_OPT_QUIET))
-               progress_meter(-1);
+#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
+# if ENABLE_FEATURE_WGET_TIMEOUT
+       unsigned second_cnt = G.timeout_seconds;
+# endif
+       struct pollfd polldata;
+
+       polldata.fd = fileno(dfp);
+       polldata.events = POLLIN | POLLPRI;
+#endif
+       progress_meter(PROGRESS_START);
 
        if (G.chunked)
                goto get_clen;
 
        /* Loops only if chunked */
        while (1) {
+
+#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
+               /* Must use nonblocking I/O, otherwise fread will loop
+                * and *block* until it reads full buffer,
+                * which messes up progress bar and/or timeout logic.
+                * Because of nonblocking I/O, we need to dance
+                * very carefully around EAGAIN. See explanation at
+                * clearerr() calls.
+                */
+               ndelay_on(polldata.fd);
+#endif
                while (1) {
                        int n;
                        unsigned rdsz;
 
-                       rdsz = sizeof(buf);
+#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
+                       /* fread internally uses read loop, which in our case
+                        * is usually exited when we get EAGAIN.
+                        * In this case, libc sets error marker on the stream.
+                        * Need to clear it before next fread to avoid possible
+                        * rare false positive ferror below. Rare because usually
+                        * fread gets more than zero bytes, and we don't fall
+                        * into if (n <= 0) ...
+                        */
+                       clearerr(dfp);
+#endif
+                       errno = 0;
+                       rdsz = sizeof(G.wget_buf);
                        if (G.got_clen) {
-                               if (G.content_len < (off_t)sizeof(buf)) {
+                               if (G.content_len < (off_t)sizeof(G.wget_buf)) {
                                        if ((int)G.content_len <= 0)
                                                break;
                                        rdsz = (unsigned)G.content_len;
                                }
                        }
-                       n = safe_fread(buf, rdsz, dfp);
-                       if (n <= 0) {
+                       n = fread(G.wget_buf, 1, rdsz, dfp);
+
+                       if (n > 0) {
+                               xwrite(G.output_fd, G.wget_buf, n);
+#if ENABLE_FEATURE_WGET_STATUSBAR
+                               G.transferred += n;
+#endif
+                               if (G.got_clen) {
+                                       G.content_len -= n;
+                                       if (G.content_len == 0)
+                                               break;
+                               }
+#if ENABLE_FEATURE_WGET_TIMEOUT
+                               second_cnt = G.timeout_seconds;
+#endif
+                               continue;
+                       }
+
+                       /* n <= 0.
+                        * man fread:
+                        * If error occurs, or EOF is reached, the return value
+                        * is a short item count (or zero).
+                        * fread does not distinguish between EOF and error.
+                        */
+                       if (errno != EAGAIN) {
                                if (ferror(dfp)) {
-                                       /* perror will not work: ferror doesn't set errno */
-                                       bb_error_msg_and_die(bb_msg_read_error);
+                                       progress_meter(PROGRESS_END);
+                                       bb_perror_msg_and_die(bb_msg_read_error);
                                }
-                               break;
+                               break; /* EOF, not error */
                        }
-                       xwrite(output_fd, buf, n);
-#if ENABLE_FEATURE_WGET_STATUSBAR
-                       G.transferred += n;
+
+#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
+                       /* It was EAGAIN. There is no data. Wait up to one second
+                        * then abort if timed out, or update the bar and try reading again.
+                        */
+                       if (safe_poll(&polldata, 1, 1000) == 0) {
+# if ENABLE_FEATURE_WGET_TIMEOUT
+                               if (second_cnt != 0 && --second_cnt == 0) {
+                                       progress_meter(PROGRESS_END);
+                                       bb_error_msg_and_die("download timed out");
+                               }
+# endif
+                               /* We used to loop back to poll here,
+                                * but there is no great harm in letting fread
+                                * to try reading anyway.
+                                */
+                       }
+                       /* Need to do it _every_ second for "stalled" indicator
+                        * to be shown properly.
+                        */
+                       progress_meter(PROGRESS_BUMP);
 #endif
-                       if (G.got_clen)
-                               G.content_len -= n;
-               }
+               } /* while (reading data) */
 
+#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
+               clearerr(dfp);
+               ndelay_off(polldata.fd); /* else fgets can get very unhappy */
+#endif
                if (!G.chunked)
                        break;
 
-               safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
+               fgets_and_trim(dfp); /* Eat empty line */
  get_clen:
-               safe_fgets(buf, sizeof(buf), dfp);
-               G.content_len = STRTOOFF(buf, NULL, 16);
+               fgets_and_trim(dfp);
+               G.content_len = STRTOOFF(G.wget_buf, NULL, 16);
                /* FIXME: error check? */
                if (G.content_len == 0)
                        break; /* all done! */
                G.got_clen = 1;
+               /*
+                * Note that fgets may result in some data being buffered in dfp.
+                * We loop back to fread, which will retrieve this data.
+                * Also note that code has to be arranged so that fread
+                * is done _before_ one-second poll wait - poll doesn't know
+                * about stdio buffering and can result in spurious one second waits!
+                */
+       }
+
+       /* If -c failed, we restart from the beginning,
+        * but we do not truncate file then, we do it only now, at the end.
+        * This lets user to ^C if his 99% complete 10 GB file download
+        * failed to restart *without* losing the almost complete file.
+        */
+       {
+               off_t pos = lseek(G.output_fd, 0, SEEK_CUR);
+               if (pos != (off_t)-1)
+                       ftruncate(G.output_fd, pos);
        }
 
-       if (!(option_mask32 & WGET_OPT_QUIET))
-               progress_meter(0);
+       /* Draw full bar and free its resources */
+       G.chunked = 0;  /* makes it show 100% even for chunked download */
+       G.got_clen = 1; /* makes it show 100% even for download of (formerly) unknown size */
+       progress_meter(PROGRESS_END);
 }
 
-int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int wget_main(int argc UNUSED_PARAM, char **argv)
+static void download_one_url(const char *url)
 {
-       char buf[512];
-       struct host_info server, target;
-       len_and_sockaddr *lsa;
-       unsigned opt;
+       bool use_proxy;                 /* Use proxies if env vars are set  */
        int redir_limit;
-       char *proxy = NULL;
-       char *dir_prefix = NULL;
-#if ENABLE_FEATURE_WGET_LONG_OPTIONS
-       char *post_data;
-       char *extra_headers = NULL;
-       llist_t *headers_llist = NULL;
-#endif
+       len_and_sockaddr *lsa;
        FILE *sfp;                      /* socket to web/ftp server         */
        FILE *dfp;                      /* socket to ftp server (data)      */
-       char *fname_out;                /* where to direct output (-O)      */
-       int output_fd = -1;
-       bool use_proxy;                 /* Use proxies if env vars are set  */
-       const char *proxy_flag = "on";  /* Use proxies if env vars are set  */
-       const char *user_agent = "Wget";/* "User-Agent" header field        */
-
-       static const char keywords[] ALIGN1 =
-               "content-length\0""transfer-encoding\0""chunked\0""location\0";
-       enum {
-               KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location
-       };
-#if ENABLE_FEATURE_WGET_LONG_OPTIONS
-       static const char wget_longopts[] ALIGN1 =
-               /* name, has_arg, val */
-               "continue\0"         No_argument       "c"
-               "spider\0"           No_argument       "s"
-               "quiet\0"            No_argument       "q"
-               "output-document\0"  Required_argument "O"
-               "directory-prefix\0" Required_argument "P"
-               "proxy\0"            Required_argument "Y"
-               "user-agent\0"       Required_argument "U"
-               /* Ignored: */
-               // "tries\0"            Required_argument "t"
-               // "timeout\0"          Required_argument "T"
-               /* Ignored (we always use PASV): */
-               "passive-ftp\0"      No_argument       "\xff"
-               "header\0"           Required_argument "\xfe"
-               "post-data\0"        Required_argument "\xfd"
-               /* Ignored (we don't do ssl) */
-               "no-check-certificate\0" No_argument   "\xfc"
-               ;
-#endif
-
-       INIT_G();
-
-#if ENABLE_FEATURE_WGET_LONG_OPTIONS
-       applet_long_options = wget_longopts;
-#endif
-       /* server.allocated = target.allocated = NULL; */
-       opt_complementary = "-1" IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
-       opt = getopt32(argv, "csqO:P:Y:U:" /*ignored:*/ "t:T:",
-                               &fname_out, &dir_prefix,
-                               &proxy_flag, &user_agent,
-                               NULL, /* -t RETRIES */
-                               NULL /* -T NETWORK_READ_TIMEOUT */
-                               IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
-                               IF_FEATURE_WGET_LONG_OPTIONS(, &post_data)
-                               );
-#if ENABLE_FEATURE_WGET_LONG_OPTIONS
-       if (headers_llist) {
-               int size = 1;
-               char *cp;
-               llist_t *ll = headers_llist;
-               while (ll) {
-                       size += strlen(ll->data) + 2;
-                       ll = ll->link;
-               }
-               extra_headers = cp = xmalloc(size);
-               while (headers_llist) {
-                       cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
-               }
-       }
-#endif
-
-       /* TODO: compat issue: should handle "wget URL1 URL2..." */
-
+       char *proxy = NULL;
+       char *fname_out_alloc;
+       char *redirected_path = NULL;
+       struct host_info server;
+       struct host_info target;
+
+       server.allocated = NULL;
+       target.allocated = NULL;
+       server.user = NULL;
        target.user = NULL;
-       parse_url(argv[optind], &target);
+
+       parse_url(url, &target);
 
        /* Use the proxy if necessary */
-       use_proxy = (strcmp(proxy_flag, "off") != 0);
+       use_proxy = (strcmp(G.proxy_flag, "off") != 0);
        if (use_proxy) {
                proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
-               if (proxy && proxy[0]) {
-                       server.user = NULL;
+               use_proxy = (proxy && proxy[0]);
+               if (use_proxy)
                        parse_url(proxy, &server);
-               } else {
-                       use_proxy = 0;
-               }
        }
        if (!use_proxy) {
                server.port = target.port;
                if (ENABLE_FEATURE_IPV6) {
-                       server.host = xstrdup(target.host);
+                       //free(server.allocated); - can't be non-NULL
+                       server.host = server.allocated = xstrdup(target.host);
                } else {
                        server.host = target.host;
                }
@@ -612,50 +663,48 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
        if (ENABLE_FEATURE_IPV6)
                strip_ipv6_scope_id(target.host);
 
-       /* Guess an output filename, if there was no -O FILE */
-       if (!(opt & WGET_OPT_OUTNAME)) {
-               fname_out = bb_get_last_path_component_nostrip(target.path);
+       /* If there was no -O FILE, guess output filename */
+       fname_out_alloc = NULL;
+       if (!(option_mask32 & WGET_OPT_OUTNAME)) {
+               G.fname_out = bb_get_last_path_component_nostrip(target.path);
                /* handle "wget http://kernel.org//" */
-               if (fname_out[0] == '/' || !fname_out[0])
-                       fname_out = (char*)"index.html";
+               if (G.fname_out[0] == '/' || !G.fname_out[0])
+                       G.fname_out = (char*)"index.html";
                /* -P DIR is considered only if there was no -O FILE */
-               if (dir_prefix)
-                       fname_out = concat_path_file(dir_prefix, fname_out);
-       } else {
-               if (LONE_DASH(fname_out)) {
-                       /* -O - */
-                       output_fd = 1;
-                       opt &= ~WGET_OPT_CONTINUE;
+               if (G.dir_prefix)
+                       G.fname_out = fname_out_alloc = concat_path_file(G.dir_prefix, G.fname_out);
+               else {
+                       /* redirects may free target.path later, need to make a copy */
+                       G.fname_out = fname_out_alloc = xstrdup(G.fname_out);
                }
        }
 #if ENABLE_FEATURE_WGET_STATUSBAR
-       G.curfile = bb_get_last_path_component_nostrip(fname_out);
+       G.curfile = bb_get_last_path_component_nostrip(G.fname_out);
 #endif
 
-       /* Impossible?
-       if ((opt & WGET_OPT_CONTINUE) && !fname_out)
-               bb_error_msg_and_die("can't specify continue (-c) without a filename (-O)");
-       */
-
        /* Determine where to start transfer */
-       if (opt & WGET_OPT_CONTINUE) {
-               output_fd = open(fname_out, O_WRONLY);
-               if (output_fd >= 0) {
-                       G.beg_range = xlseek(output_fd, 0, SEEK_END);
+       G.beg_range = 0;
+       if (option_mask32 & WGET_OPT_CONTINUE) {
+               G.output_fd = open(G.fname_out, O_WRONLY);
+               if (G.output_fd >= 0) {
+                       G.beg_range = xlseek(G.output_fd, 0, SEEK_END);
                }
                /* File doesn't exist. We do not create file here yet.
-                * We are not sure it exists on remove side */
+                * We are not sure it exists on remote side */
        }
 
        redir_limit = 5;
  resolve_lsa:
        lsa = xhost2sockaddr(server.host, server.port);
-       if (!(opt & WGET_OPT_QUIET)) {
+       if (!(option_mask32 & WGET_OPT_QUIET)) {
                char *s = xmalloc_sockaddr2dotted(&lsa->u.sa);
                fprintf(stderr, "Connecting to %s (%s)\n", server.host, s);
                free(s);
        }
  establish_session:
+       /*G.content_len = 0; - redundant, got_clen = 0 is enough */
+       G.got_clen = 0;
+       G.chunked = 0;
        if (use_proxy || !target.is_ftp) {
                /*
                 *  HTTP session
@@ -663,6 +712,7 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
                char *str;
                int status;
 
+
                /* Open socket to http server */
                sfp = open_socket(lsa);
 
@@ -672,54 +722,61 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
                                target.is_ftp ? "f" : "ht", target.host,
                                target.path);
                } else {
-                       if (opt & WGET_OPT_POST_DATA)
+                       if (option_mask32 & WGET_OPT_POST_DATA)
                                fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path);
                        else
                                fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
                }
 
                fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
-                       target.host, user_agent);
+                       target.host, G.user_agent);
+
+               /* Ask server to close the connection as soon as we are done
+                * (IOW: we do not intend to send more requests)
+                */
+               fprintf(sfp, "Connection: close\r\n");
 
 #if ENABLE_FEATURE_WGET_AUTHENTICATION
                if (target.user) {
                        fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
-                               base64enc_512(buf, target.user));
+                               base64enc(target.user));
                }
                if (use_proxy && server.user) {
                        fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
-                               base64enc_512(buf, server.user));
+                               base64enc(server.user));
                }
 #endif
 
-               if (G.beg_range)
+               if (G.beg_range != 0)
                        fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range);
+
 #if ENABLE_FEATURE_WGET_LONG_OPTIONS
-               if (extra_headers)
-                       fputs(extra_headers, sfp);
-
-               if (opt & WGET_OPT_POST_DATA) {
-                       char *estr = URL_escape(post_data);
-                       fprintf(sfp, "Content-Type: application/x-www-form-urlencoded\r\n");
-                       fprintf(sfp, "Content-Length: %u\r\n" "\r\n" "%s",
-                                       (int) strlen(estr), estr);
-                       /*fprintf(sfp, "Connection: Keep-Alive\r\n\r\n");*/
-                       /*fprintf(sfp, "%s\r\n", estr);*/
-                       free(estr);
+               if (G.extra_headers)
+                       fputs(G.extra_headers, sfp);
+
+               if (option_mask32 & WGET_OPT_POST_DATA) {
+                       fprintf(sfp,
+                               "Content-Type: application/x-www-form-urlencoded\r\n"
+                               "Content-Length: %u\r\n"
+                               "\r\n"
+                               "%s",
+                               (int) strlen(G.post_data), G.post_data
+                       );
                } else
 #endif
-               { /* If "Connection:" is needed, document why */
-                       fprintf(sfp, /* "Connection: close\r\n" */ "\r\n");
+               {
+                       fprintf(sfp, "\r\n");
                }
 
+               fflush(sfp);
+
                /*
                 * Retrieve HTTP response line and check for "200" status code.
                 */
  read_response:
-               if (fgets(buf, sizeof(buf), sfp) == NULL)
-                       bb_error_msg_and_die("no response from server");
+               fgets_and_trim(sfp);
 
-               str = buf;
+               str = G.wget_buf;
                str = skip_non_whitespace(str);
                str = skip_whitespace(str);
                // FIXME: no error check
@@ -728,7 +785,7 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
                switch (status) {
                case 0:
                case 100:
-                       while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL)
+                       while (gethdr(sfp) != NULL)
                                /* eat all remaining headers */;
                        goto read_response;
                case 200:
@@ -757,33 +814,48 @@ However, in real world it was observed that some web servers
 (e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
 */
                case 204:
+                       if (G.beg_range != 0) {
+                               /* "Range:..." was not honored by the server.
+                                * Restart download from the beginning.
+                                */
+                               reset_beg_range_to_zero();
+                       }
                        break;
-               case 300:       /* redirection */
+               case 300:  /* redirection */
                case 301:
                case 302:
                case 303:
                        break;
-               case 206:
-                       if (G.beg_range)
+               case 206: /* Partial Content */
+                       if (G.beg_range != 0)
+                               /* "Range:..." worked. Good. */
                                break;
+                       /* Partial Content even though we did not ask for it??? */
                        /* fall through */
                default:
-                       bb_error_msg_and_die("server returned error: %s", sanitize_string(buf));
+                       bb_error_msg_and_die("server returned error: %s", sanitize_string(G.wget_buf));
                }
 
                /*
                 * Retrieve HTTP headers.
                 */
-               while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
-                       /* gethdr converted "FOO:" string to lowercase */
+               while ((str = gethdr(sfp)) != NULL) {
+                       static const char keywords[] ALIGN1 =
+                               "content-length\0""transfer-encoding\0""location\0";
+                       enum {
+                               KEY_content_length = 1, KEY_transfer_encoding, KEY_location
+                       };
                        smalluint key;
+
+                       /* gethdr converted "FOO:" string to lowercase */
+
                        /* strip trailing whitespace */
                        char *s = strchrnul(str, '\0') - 1;
                        while (s >= str && (*s == ' ' || *s == '\t')) {
                                *s = '\0';
                                s--;
                        }
-                       key = index_in_strings(keywords, buf) + 1;
+                       key = index_in_strings(keywords, G.wget_buf) + 1;
                        if (key == KEY_content_length) {
                                G.content_len = BB_STRTOOFF(str, NULL, 10);
                                if (G.content_len < 0 || errno) {
@@ -793,23 +865,23 @@ However, in real world it was observed that some web servers
                                continue;
                        }
                        if (key == KEY_transfer_encoding) {
-                               if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
+                               if (strcmp(str_tolower(str), "chunked") != 0)
                                        bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str));
-                               G.chunked = G.got_clen = 1;
+                               G.chunked = 1;
                        }
                        if (key == KEY_location && status >= 300) {
                                if (--redir_limit == 0)
                                        bb_error_msg_and_die("too many redirections");
                                fclose(sfp);
-                               G.got_clen = 0;
-                               G.chunked = 0;
-                               if (str[0] == '/')
-                                       /* free(target.allocated); */
-                                       target.path = /* target.allocated = */ xstrdup(str+1);
+                               if (str[0] == '/') {
+                                       free(redirected_path);
+                                       target.path = redirected_path = xstrdup(str+1);
                                        /* lsa stays the same: it's on the same server */
-                               else {
+                               else {
                                        parse_url(str, &target);
                                        if (!use_proxy) {
+                                               free(server.allocated);
+                                               server.allocated = NULL;
                                                server.host = target.host;
                                                /* strip_ipv6_scope_id(target.host); - no! */
                                                /* we assume remote never gives us IPv6 addr with scope id */
@@ -834,30 +906,127 @@ However, in real world it was observed that some web servers
                sfp = prepare_ftp_session(&dfp, &target, lsa);
        }
 
-       if (opt & WGET_OPT_SPIDER) {
-               if (ENABLE_FEATURE_CLEAN_UP)
-                       fclose(sfp);
-               return EXIT_SUCCESS;
-       }
+       free(lsa);
 
-       if (output_fd < 0) {
-               int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
-               /* compat with wget: -O FILE can overwrite */
-               if (opt & WGET_OPT_OUTNAME)
-                       o_flags = O_WRONLY | O_CREAT | O_TRUNC;
-               output_fd = xopen(fname_out, o_flags);
+       if (!(option_mask32 & WGET_OPT_SPIDER)) {
+               if (G.output_fd < 0)
+                       G.output_fd = xopen(G.fname_out, G.o_flags);
+               retrieve_file_data(dfp);
+               if (!(option_mask32 & WGET_OPT_OUTNAME)) {
+                       xclose(G.output_fd);
+                       G.output_fd = -1;
+               }
        }
 
-       retrieve_file_data(dfp, output_fd);
-       xclose(output_fd);
-
        if (dfp != sfp) {
-               /* It's ftp. Close it properly */
+               /* It's ftp. Close data connection properly */
                fclose(dfp);
-               if (ftpcmd(NULL, NULL, sfp, buf) != 226)
-                       bb_error_msg_and_die("ftp error: %s", sanitize_string(buf+4));
-               /* ftpcmd("QUIT", NULL, sfp, buf); - why bother? */
+               if (ftpcmd(NULL, NULL, sfp) != 226)
+                       bb_error_msg_and_die("ftp error: %s", sanitize_string(G.wget_buf + 4));
+               /* ftpcmd("QUIT", NULL, sfp); - why bother? */
+       }
+       fclose(sfp);
+
+       free(server.allocated);
+       free(target.allocated);
+       free(fname_out_alloc);
+       free(redirected_path);
+}
+
+int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int wget_main(int argc UNUSED_PARAM, char **argv)
+{
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+       static const char wget_longopts[] ALIGN1 =
+               /* name, has_arg, val */
+               "continue\0"         No_argument       "c"
+//FIXME: -s isn't --spider, it's --save-headers!
+               "spider\0"           No_argument       "s"
+               "quiet\0"            No_argument       "q"
+               "output-document\0"  Required_argument "O"
+               "directory-prefix\0" Required_argument "P"
+               "proxy\0"            Required_argument "Y"
+               "user-agent\0"       Required_argument "U"
+#if ENABLE_FEATURE_WGET_TIMEOUT
+               "timeout\0"          Required_argument "T"
+#endif
+               /* Ignored: */
+               // "tries\0"            Required_argument "t"
+               /* Ignored (we always use PASV): */
+               "passive-ftp\0"      No_argument       "\xff"
+               "header\0"           Required_argument "\xfe"
+               "post-data\0"        Required_argument "\xfd"
+               /* Ignored (we don't do ssl) */
+               "no-check-certificate\0" No_argument   "\xfc"
+               /* Ignored (we don't support caching) */
+               "no-cache\0"         No_argument       "\xfb"
+               ;
+#endif
+
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+       llist_t *headers_llist = NULL;
+#endif
+
+       INIT_G();
+
+#if ENABLE_FEATURE_WGET_TIMEOUT
+       G.timeout_seconds = 900;
+       signal(SIGALRM, alarm_handler);
+#endif
+       G.proxy_flag = "on";   /* use proxies if env vars are set */
+       G.user_agent = "Wget"; /* "User-Agent" header field */
+
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+       applet_long_options = wget_longopts;
+#endif
+       opt_complementary = "-1" IF_FEATURE_WGET_TIMEOUT(":T+") IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
+       getopt32(argv, "csqO:P:Y:U:T:" /*ignored:*/ "t:",
+               &G.fname_out, &G.dir_prefix,
+               &G.proxy_flag, &G.user_agent,
+               IF_FEATURE_WGET_TIMEOUT(&G.timeout_seconds) IF_NOT_FEATURE_WGET_TIMEOUT(NULL),
+               NULL /* -t RETRIES */
+               IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
+               IF_FEATURE_WGET_LONG_OPTIONS(, &G.post_data)
+       );
+       argv += optind;
+
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+       if (headers_llist) {
+               int size = 1;
+               char *cp;
+               llist_t *ll = headers_llist;
+               while (ll) {
+                       size += strlen(ll->data) + 2;
+                       ll = ll->link;
+               }
+               G.extra_headers = cp = xmalloc(size);
+               while (headers_llist) {
+                       cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
+               }
+       }
+#endif
+
+       G.output_fd = -1;
+       G.o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
+       if (G.fname_out) { /* -O FILE ? */
+               if (LONE_DASH(G.fname_out)) { /* -O - ? */
+                       G.output_fd = 1;
+                       option_mask32 &= ~WGET_OPT_CONTINUE;
+               }
+               /* compat with wget: -O FILE can overwrite */
+               G.o_flags = O_WRONLY | O_CREAT | O_TRUNC;
        }
 
+       while (*argv)
+               download_one_url(*argv++);
+
+       if (G.output_fd >= 0)
+               xclose(G.output_fd);
+
+#if ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_WGET_LONG_OPTIONS
+       free(G.extra_headers);
+#endif
+       FINI_G();
+
        return EXIT_SUCCESS;
 }
diff --git a/networking/whois.c b/networking/whois.c
new file mode 100644 (file)
index 0000000..bf33033
--- /dev/null
@@ -0,0 +1,65 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * whois - tiny client for the whois directory service
+ *
+ * Copyright (c) 2011 Pere Orga <gotrunks@gmail.com>
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+/* TODO
+ * Add ipv6 support
+ * Add proxy support
+ */
+
+//config:config WHOIS
+//config:      bool "whois"
+//config:      default y
+//config:      help
+//config:        whois is a client for the whois directory service
+
+//applet:IF_WHOIS(APPLET(whois, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_WHOIS) += whois.o
+
+//usage:#define whois_trivial_usage
+//usage:       "[-h SERVER] [-p PORT] NAME..."
+//usage:#define whois_full_usage "\n\n"
+//usage:       "Query WHOIS info about NAME\n"
+//usage:     "\n       -h,-p   Server to query"
+
+#include "libbb.h"
+
+static void pipe_out(int fd)
+{
+       FILE *fp;
+       char buf[1024];
+
+       fp = xfdopen_for_read(fd);
+       while (fgets(buf, sizeof(buf), fp)) {
+               char *p = strpbrk(buf, "\r\n");
+               if (p)
+                       *p = '\0';
+               puts(buf);
+       }
+
+       fclose(fp); /* closes fd too */
+}
+
+int whois_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int whois_main(int argc UNUSED_PARAM, char **argv)
+{
+       int port = 43;
+       const char *host = "whois-servers.net";
+
+       opt_complementary = "-1:p+";
+       getopt32(argv, "h:p:", &host, &port);
+
+       argv += optind;
+       do {
+               int fd = create_and_connect_stream_or_die(host, port);
+               fdprintf(fd, "%s\r\n", *argv);
+               pipe_out(fd);
+       }
+       while (*++argv);
+
+       return EXIT_SUCCESS;
+}
index 6b0b1c4..7314ff8 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
  * Copyright (C) 2004 by David Brownell
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*
 // - avoid silent script failures, especially under load...
 // - link status monitoring (restart on link-up; stop on link-down)
 
+//usage:#define zcip_trivial_usage
+//usage:       "[OPTIONS] IFACE SCRIPT"
+//usage:#define zcip_full_usage "\n\n"
+//usage:       "Manage a ZeroConf IPv4 link-local address\n"
+//usage:     "\n       -f              Run in foreground"
+//usage:     "\n       -q              Quit after obtaining address"
+//usage:     "\n       -r 169.254.x.x  Request this address first"
+//usage:     "\n       -v              Verbose"
+//usage:     "\n"
+//usage:     "\nWith no -q, runs continuously monitoring for ARP conflicts,"
+//usage:     "\nexits only on I/O errors (link down etc)"
+
+#include "libbb.h"
 #include <netinet/ether.h>
-#include <net/ethernet.h>
 #include <net/if.h>
 #include <net/if_arp.h>
-#include <linux/if_packet.h>
 #include <linux/sockios.h>
 
-#include "libbb.h"
 #include <syslog.h>
 
 /* We don't need more than 32 bits of the counter */
@@ -81,6 +91,7 @@ struct globals {
 #define G (*(struct globals*)&bb_common_bufsiz1)
 #define saddr    (G.saddr   )
 #define eth_addr (G.eth_addr)
+#define INIT_G() do { } while (0)
 
 
 /**
@@ -213,6 +224,7 @@ int zcip_main(int argc UNUSED_PARAM, char **argv)
 #define verbose    (L.verbose   )
 
        memset(&L, 0, sizeof(L));
+       INIT_G();
 
 #define FOREGROUND (opts & 1)
 #define QUIT       (opts & 2)
diff --git a/packaging/06ls.patch b/packaging/06ls.patch
deleted file mode 100644 (file)
index b953d77..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#! /bin/sh /usr/share/dpatch/dpatch-run
-## 99-unnamed.dpatch by Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
-##
-## All lines beginning with `## DP:' are a description of the patch.
-## DP: No description.
-
-@DPATCH@
-diff --git a/coreutils/ls.c b/coreutils/ls.c
-index 067e463..39aa63e 100644
---- a/coreutils/ls.c
-+++ b/coreutils/ls.c
-@@ -431,6 +431,7 @@ static void showfiles(struct dnode **dn, int nfiles)
-                       }
-               }
-               putchar('\n');
-+              fflush(NULL);
-               column = 0;
-       }
- }
diff --git a/packaging/applets-fallback.patch b/packaging/applets-fallback.patch
deleted file mode 100644 (file)
index 880cbe5..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
---- a/shell/ash.c
-+++ b/shell/ash.c
-@@ -7241,25 +7241,10 @@
- static void
--tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
-+tryexec(IF_FEATURE_SH_STANDALONE(int applet_no UNUSED_PARAM,) char *cmd, char **argv, char **envp)
- {
-       int repeated = 0;
--#if ENABLE_FEATURE_SH_STANDALONE
--      if (applet_no >= 0) {
--              if (APPLET_IS_NOEXEC(applet_no)) {
--                      clearenv();
--                      while (*envp)
--                              putenv(*envp++);
--                      run_applet_no_and_exit(applet_no, argv);
--              }
--              /* re-exec ourselves with the new arguments */
--              execve(bb_busybox_exec_path, argv, envp);
--              /* If they called chroot or otherwise made the binary no longer
--               * executable, fall through */
--      }
--#endif
--
-  repeat:
- #ifdef SYSV
-       do {
-@@ -7309,14 +7294,14 @@
-       clearredir(/*drop:*/ 1);
-       envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
--      if (strchr(argv[0], '/') != NULL
--#if ENABLE_FEATURE_SH_STANDALONE
--       || (applet_no = find_applet_by_name(argv[0])) >= 0
--#endif
--      ) {
-+      if (strchr(argv[0], '/') != NULL) {
-               tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
-               e = errno;
-       } else {
-+#if ENABLE_FEATURE_SH_STANDALONE
-+              bb_execv_applet(argv[0], argv, envp);
-+#endif
-+
-               e = ENOENT;
-               while ((cmdname = path_advance(&path, argv[0])) != NULL) {
-                       if (--idx < 0 && pathopt == NULL) {
---- a/libbb/execable.c
-+++ b/libbb/execable.c
-@@ -9,6 +9,9 @@
- #include "libbb.h"
-+#include <alloca.h>
-+#include <stdarg.h>
-+
- /* check if path points to an executable file;
-  * return 1 if found;
-  * return 0 otherwise;
-@@ -68,12 +71,60 @@
- }
- #if ENABLE_FEATURE_PREFER_APPLETS
-+int FAST_FUNC bb_execv_applet(const char *name, char *const argv[], char *const envp[])
-+{
-+      const char **path = bb_busybox_exec_paths;
-+
-+      errno = ENOENT;
-+
-+      if (find_applet_by_name(name) < 0)
-+              return -1;
-+
-+      for (; *path; ++path)
-+              execve(*path, argv, envp);
-+
-+      return -1;
-+}
-+
- /* just like the real execvp, but try to launch an applet named 'file' first
-  */
- int FAST_FUNC bb_execvp(const char *file, char *const argv[])
- {
--      return execvp(find_applet_by_name(file) >= 0 ? bb_busybox_exec_path : file,
--                                      argv);
-+      int ret = bb_execv_applet(file, argv, environ);
-+      if (errno != ENOENT)
-+              return ret;
-+
-+      return execvp(file, argv);
-+}
-+
-+int FAST_FUNC bb_execlp(const char *file, const char *arg, ...)
-+{
-+#define INITIAL_ARGV_MAX 16
-+      size_t argv_max = INITIAL_ARGV_MAX;
-+      const char **argv = malloc(argv_max * sizeof (const char *));
-+      va_list args;
-+      unsigned int i = 0;
-+      int ret;
-+
-+      va_start (args, arg);
-+      while (argv[i++] != NULL) {
-+              if (i == argv_max) {
-+                      const char **nptr;
-+                      argv_max *= 2;
-+                      nptr = realloc (argv, argv_max * sizeof (const char *));
-+                      if (nptr == NULL)
-+                              return -1;
-+                      argv = nptr;
-+              }
-+
-+              argv[i] = va_arg (args, const char *);
-+      }
-+      va_end (args);
-+
-+      ret = bb_execvp(file, (char *const *)argv);
-+      free(argv);
-+
-+      return ret;
- }
- #endif
---- a/libbb/messages.c
-+++ b/libbb/messages.c
-@@ -45,6 +45,15 @@
- const char bb_path_motd_file[] ALIGN1 = "/etc/motd";
- const char bb_dev_null[] ALIGN1 = "/dev/null";
- const char bb_busybox_exec_path[] ALIGN1 = CONFIG_BUSYBOX_EXEC_PATH;
-+const char *bb_busybox_exec_paths[] ALIGN1 = {
-+#ifdef __linux__
-+      "/proc/self/exe",
-+#endif
-+#ifdef CONFIG_BUSYBOX_EXEC_PATH
-+      CONFIG_BUSYBOX_EXEC_PATH,
-+#endif
-+      NULL
-+};
- const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL;
- /* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin,
-  * but I want to save a few bytes here. Check libbb.h before changing! */
---- a/include/libbb.h
-+++ b/include/libbb.h
-@@ -830,11 +830,11 @@
-  * but it may exec busybox and call applet instead of searching PATH.
-  */
- #if ENABLE_FEATURE_PREFER_APPLETS
-+int bb_execv_applet(const char *name, char *const argv[], char *const envp[]) FAST_FUNC;
- int bb_execvp(const char *file, char *const argv[]) FAST_FUNC;
--#define BB_EXECVP(prog,cmd) bb_execvp(prog,cmd)
--#define BB_EXECLP(prog,cmd,...) \
--      execlp((find_applet_by_name(prog) >= 0) ? CONFIG_BUSYBOX_EXEC_PATH : prog, \
--              cmd, __VA_ARGS__)
-+int bb_execlp(const char *file, const char *arg, ...) FAST_FUNC;
-+#define BB_EXECVP(prog,cmd)     bb_execvp(prog,cmd)
-+#define BB_EXECLP(prog,cmd,...) bb_execlp(prog,cmd, __VA_ARGS__)
- #else
- #define BB_EXECVP(prog,cmd)     execvp(prog,cmd)
- #define BB_EXECLP(prog,cmd,...) execlp(prog,cmd, __VA_ARGS__)
-@@ -1575,6 +1575,7 @@
- extern const char bb_path_wtmp_file[];
- extern const char bb_dev_null[];
- extern const char bb_busybox_exec_path[];
-+extern const char *bb_busybox_exec_paths[];
- /* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin,
-  * but I want to save a few bytes here */
- extern const char bb_PATH_root_path[]; /* "PATH=/sbin:/usr/sbin:/bin:/usr/bin" */
---- a/Config.in
-+++ b/Config.in
-@@ -386,13 +386,10 @@
- config BUSYBOX_EXEC_PATH
-       string "Path to BusyBox executable"
--      default "/proc/self/exe"
-+      default "/bin/busybox"
-       help
-         When Busybox applets need to run other busybox 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.
-+        sometimes needs to exec() itself.
- # These are auto-selected by other options
---- a/coreutils/chroot.c
-+++ b/coreutils/chroot.c
-@@ -30,5 +30,7 @@
-               argv[1] = (char *) "-i";
-       }
--      BB_EXECVP_or_die(argv);
-+      execvp(argv[0], argv);
-+      xfunc_error_retval = (errno == ENOENT) ? 127 : 126;
-+      bb_perror_msg_and_die("can't execute '%s'", argv[0]);
- }
diff --git a/packaging/bin.links b/packaging/bin.links
deleted file mode 100644 (file)
index acf3d14..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-ash
-mktemp
-bunzip2
-bzcat
-bzip2
-cat
-chgrp
-chmod
-chown
-cp
-cpio
-date
-dd
-df
-dmesg
-dnsdomainname
-dumpkmap
-echo
-ed
-egrep
-false
-fbset
-fdflush
-fgconsole
-fgrep
-fsync
-fuser
-grep
-gunzip
-gzip
-hostname
-ip
-ipaddr
-iplink
-iproute
-iprule
-kill
-ln
-ls
-mkdir
-mknod
-more
-mount
-mountpoint
-mv
-netstat
-ping
-ping6
-ps
-pwd
-readlink
-rm
-rmdir
-sed
-sleep
-stty
-sync
-tar
-touch
-true
-umount
-uname
-uncompress
-usleep
-zcat
diff --git a/packaging/blockdev.patch b/packaging/blockdev.patch
deleted file mode 100644 (file)
index 46f57b7..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-Description: Backport blockdev applet from upstream
- This allows os-prober to avoid replaying journals when mounting block
- devices read-only.
-Origin: http://git.busybox.net/busybox/tree/util-linux/blockdev.c
-Bug-Debian: http://bugs.debian.org/418163
-Author: Sergey Naumov <sknaumov@gmail.com>
-Author: Denys Vlasenko <dvlasenk@redhat.com>
-Forwarded: not-needed
-Last-Update: 2010-11-09
-
-Index: b/util-linux/blockdev.c
-===================================================================
---- /dev/null
-+++ b/util-linux/blockdev.c
-@@ -0,0 +1,195 @@
-+/*
-+ * blockdev implementation for busybox
-+ *
-+ * Copyright (C) 2010 Sergey Naumov <sknaumov@gmail.com>
-+ *
-+ * Licensed under GPLv2, see file LICENSE in this source tree.
-+ */
-+
-+//applet:IF_BLOCKDEV(APPLET(blockdev, _BB_DIR_SBIN, _BB_SUID_DROP))
-+
-+//kbuild:lib-$(CONFIG_BLOCKDEV) += blockdev.o
-+
-+//config:config BLOCKDEV
-+//config:     bool "blockdev"
-+//config:     default y
-+//config:     help
-+//config:       Performs some ioctls with block devices.
-+
-+//usage:#define blockdev_trivial_usage
-+//usage:      "OPTION BLOCKDEV"
-+//usage:#define blockdev_full_usage "\n\n"
-+//usage:       "Options:"
-+//usage:     "\n      --setro         Set ro"
-+//usage:     "\n      --setrw         Set rw"
-+//usage:     "\n      --getro         Get ro"
-+//usage:     "\n      --getss         Get sector size"
-+//usage:     "\n      --getbsz        Get block size"
-+//usage:     "\n      --setbsz BYTES  Set block size"
-+//usage:     "\n      --getsize       Get device size in 512-byte sectors"
-+//usage:     "\n      --getsize64     Get device size in bytes"
-+//usage:     "\n      --flushbufs     Flush buffers"
-+//usage:     "\n      --rereadpt      Reread partition table"
-+
-+
-+#include "libbb.h"
-+#include <linux/fs.h>
-+
-+enum {
-+      ARG_NONE   = 0,
-+      ARG_INT    = 1,
-+      ARG_ULONG  = 2,
-+      /* Yes, BLKGETSIZE64 takes pointer to uint64_t, not ullong! */
-+      ARG_U64    = 3,
-+      ARG_MASK   = 3,
-+
-+      FL_USRARG   = 4, /* argument is provided by user */
-+      FL_NORESULT = 8,
-+};
-+
-+struct bdc {
-+      uint32_t   ioc;                       /* ioctl code */
-+      const char name[sizeof("flushbufs")]; /* "--setfoo" wothout "--" */
-+      uint8_t    flags;
-+      int8_t     argval;                    /* default argument value */
-+};
-+
-+static const struct bdc bdcommands[] = {
-+      {
-+              .ioc = BLKROSET,
-+              .name = "setro",
-+              .flags = ARG_INT + FL_NORESULT,
-+              .argval = 1,
-+      },{
-+              .ioc = BLKROSET,
-+              .name = "setrw",
-+              .flags = ARG_INT + FL_NORESULT,
-+              .argval = 0,
-+      },{
-+              .ioc = BLKROGET,
-+              .name = "getro",
-+              .flags = ARG_INT,
-+              .argval = -1,
-+      },{
-+              .ioc = BLKSSZGET,
-+              .name = "getss",
-+              .flags = ARG_INT,
-+              .argval = -1,
-+      },{
-+              .ioc = BLKBSZGET,
-+              .name = "getbsz",
-+              .flags = ARG_INT,
-+              .argval = -1,
-+      },{
-+              .ioc = BLKBSZSET,
-+              .name = "setbsz",
-+              .flags = ARG_INT + FL_NORESULT + FL_USRARG,
-+              .argval = 0,
-+      },{
-+              .ioc = BLKGETSIZE,
-+              .name = "getsize",
-+              .flags = ARG_ULONG,
-+              .argval = -1,
-+      },{
-+              .ioc = BLKGETSIZE64,
-+              .name = "getsize64",
-+              .flags = ARG_U64,
-+              .argval = -1,
-+      },{
-+              .ioc = BLKFLSBUF,
-+              .name = "flushbufs",
-+              .flags = ARG_NONE + FL_NORESULT,
-+              .argval = 0,
-+      },{
-+              .ioc = BLKRRPART,
-+              .name = "rereadpt",
-+              .flags = ARG_NONE + FL_NORESULT,
-+              .argval = 0,
-+      }
-+};
-+
-+static const struct bdc *find_cmd(const char *s)
-+{
-+      const struct bdc *bdcmd = bdcommands;
-+      if (s[0] == '-' && s[1] == '-') {
-+              s += 2;
-+              do {
-+                      if (strcmp(s, bdcmd->name) == 0)
-+                              return bdcmd;
-+                      bdcmd++;
-+              } while (bdcmd != bdcommands + ARRAY_SIZE(bdcommands));
-+      }
-+      bb_show_usage();
-+}
-+
-+int blockdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-+int blockdev_main(int argc, char **argv)
-+{
-+      const struct bdc *bdcmd;
-+      int fd;
-+      uint64_t u64;
-+      union {
-+              int i;
-+              unsigned long lu;
-+              uint64_t u64;
-+      } ioctl_val_on_stack;
-+
-+      if ((unsigned)(argc - 3) > 1) /* must have 2 or 3 args */
-+              bb_show_usage();
-+
-+      bdcmd = find_cmd(*++argv);
-+
-+      u64 = (int)bdcmd->argval;
-+      if (bdcmd->flags & FL_USRARG)
-+              u64 = xatoi_u(*++argv);
-+
-+      if (!*++argv || argv[1])
-+              bb_show_usage();
-+      fd = xopen(*argv, O_RDONLY);
-+
-+      ioctl_val_on_stack.u64 = u64;
-+#if BB_BIG_ENDIAN
-+      /* Store data properly wrt data size.
-+       * (1) It's no-op for little-endian.
-+       * (2) it's no-op for 0 and -1. Only --setro uses arg != 0 and != -1,
-+       * and it is ARG_INT. --setbsz USER_VAL is also ARG_INT.
-+       * Thus, we don't need to handle ARG_ULONG.
-+       */
-+      switch (bdcmd->flags & ARG_MASK) {
-+      case ARG_INT:
-+              ioctl_val_on_stack.i = (int)u64;
-+              break;
-+# if 0 /* unused */
-+      case ARG_ULONG:
-+              ioctl_val_on_stack.lu = (unsigned long)u64;
-+              break;
-+# endif
-+      }
-+#endif
-+
-+      if (ioctl(fd, bdcmd->ioc, &ioctl_val_on_stack.u64) == -1)
-+              bb_simple_perror_msg_and_die(*argv);
-+
-+      /* Fetch it into register(s) */
-+      u64 = ioctl_val_on_stack.u64;
-+
-+      /* Zero- or one-extend the value if needed, then print */
-+      switch (bdcmd->flags & (ARG_MASK+FL_NORESULT)) {
-+      case ARG_INT:
-+              /* Smaller code when we use long long
-+               * (gcc tail-merges printf call)
-+               */
-+              printf("%lld\n", (long long)(int)u64);
-+              break;
-+      case ARG_ULONG:
-+              u64 = (unsigned long)u64;
-+              /* FALLTHROUGH */
-+      case ARG_U64:
-+              printf("%llu\n", (unsigned long long)u64);
-+              break;
-+      }
-+
-+      if (ENABLE_FEATURE_CLEAN_UP)
-+              close(fd);
-+      return EXIT_SUCCESS;
-+}
diff --git a/packaging/bootchartd-mounting-tmpfs-is-Linux-specific.patch b/packaging/bootchartd-mounting-tmpfs-is-Linux-specific.patch
deleted file mode 100644 (file)
index aafbed2..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-From e7a0632b7b38f635853a08c276dad2fbd395ba92 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 29 Jul 2010 04:29:53 +0200
-Subject: [PATCH 11/12] bootchartd: mounting tmpfs is Linux-specific
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- init/bootchartd.c |   20 +++++++++++++-------
- 1 files changed, 13 insertions(+), 7 deletions(-)
-
-diff --git a/init/bootchartd.c b/init/bootchartd.c
-index a1c0164..465a349 100644
---- a/init/bootchartd.c
-+++ b/init/bootchartd.c
-@@ -6,7 +6,6 @@
- //config:config BOOTCHARTD
- //config:     bool "bootchartd"
- //config:     default y
--//config:     depends on PLATFORM_LINUX
- //config:     help
- //config:       bootchartd is commonly used to profile the boot process
- //config:       for the purpose of speeding it up. In this case, it is started
-@@ -46,12 +45,15 @@
- #include "libbb.h"
- /* After libbb.h, since it needs sys/types.h on some systems */
- #include <sys/utsname.h>
--#include <sys/mount.h>
--#ifndef MS_SILENT
--# define MS_SILENT      (1 << 15)
--#endif
--#ifndef MNT_DETACH
--# define MNT_DETACH 0x00000002
-+
-+#ifdef __linux__
-+# include <sys/mount.h>
-+# ifndef MS_SILENT
-+#  define MS_SILENT      (1 << 15)
-+# endif
-+# ifndef MNT_DETACH
-+#  define MNT_DETACH 0x00000002
-+# endif
- #endif
- #define BC_VERSION_STR "0.8"
-@@ -175,6 +177,7 @@ static char *make_tempdir(void)
-       char template[] = "/tmp/bootchart.XXXXXX";
-       char *tempdir = xstrdup(mkdtemp(template));
-       if (!tempdir) {
-+#ifdef __linux__
-               /* /tmp is not writable (happens when we are used as init).
-                * Try to mount a tmpfs, them cd and lazily unmount it.
-                * Since we unmount it at once, we can mount it anywhere.
-@@ -192,6 +195,9 @@ static char *make_tempdir(void)
-               if (umount2(try_dir, MNT_DETACH) != 0) {
-                       bb_perror_msg_and_die("can't %smount tmpfs", "un");
-               }
-+#else
-+              bb_perror_msg_and_die("can't create temporary directory");
-+#endif
-       } else {
-               xchdir(tempdir);
-       }
--- 
-1.7.1
-
diff --git a/packaging/busybox-1.17.1-make.patch b/packaging/busybox-1.17.1-make.patch
deleted file mode 100644 (file)
index 96da2e4..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
---- Makefile.orig      2012-08-03 13:24:05.328363736 +0900
-+++ busybox-1.17.1/Makefile    2012-08-03 13:28:35.568351822 +0900
-@@ -433,7 +433,12 @@
- -include $(srctree)/arch/$(ARCH)/Makefile
- export KBUILD_DEFCONFIG
--config %config: scripts_basic outputmakefile gen_build_files FORCE
-+%config: scripts_basic outputmakefile gen_build_files FORCE
-+      $(Q)mkdir -p include
-+      $(Q)$(MAKE) $(build)=scripts/kconfig $@
-+      $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= .kernelrelease
-+
-+config: scripts_basic outputmakefile gen_build_files FORCE
-       $(Q)mkdir -p include
-       $(Q)$(MAKE) $(build)=scripts/kconfig $@
-       $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= .kernelrelease
-@@ -1285,9 +1290,14 @@
-       $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
- # Modules
--/ %/: prepare scripts FORCE
-+%/: prepare scripts FORCE
-       $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \
-       $(build)=$(build-dir)
-+
-+/: prepare scripts FORCE
-+      $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \
-+      $(build)=$(build-dir)
-+
- %.ko: prepare scripts FORCE
-       $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1)   \
-       $(build)=$(build-dir) $(@:.ko=.o)
diff --git a/packaging/busybox-1.20.2-fix-resource-h-failure.patch b/packaging/busybox-1.20.2-fix-resource-h-failure.patch
deleted file mode 100755 (executable)
index a081e25..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
---- busybox-1.17.1/include/libbb.h.org 2012-11-07 16:17:30.000000000 +0900
-+++ busybox-1.17.1/include/libbb.h     2012-11-21 14:58:48.000000000 +0900
-@@ -29,6 +29,7 @@
- #include <sys/poll.h>
- #include <sys/ioctl.h>
- #include <sys/mman.h>
-+#include <sys/resource.h>
- #include <sys/socket.h>
- #include <sys/stat.h>
- #include <sys/time.h>
diff --git a/packaging/busybox-zero-ifr.ifr_hwaddr.sa_data.patch b/packaging/busybox-zero-ifr.ifr_hwaddr.sa_data.patch
deleted file mode 100644 (file)
index 9fc6f7d..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-diff --git a/networking/interface.c b/networking/interface.c
-index ef187be..6cb1afa 100644
---- a/networking/interface.c
-+++ b/networking/interface.c
-@@ -623,6 +623,7 @@ static int if_fetch(struct interface *ife)
-       strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
-       memset(ife->hwaddr, 0, 32);
-+      memset(ifr.ifr_hwaddr.sa_data, 0, 8);
-       if (ioctl(skfd, SIOCGIFHWADDR, &ifr) >= 0)
-               memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8);
similarity index 68%
rename from debian/config/pkg/slp
rename to packaging/busybox.config
index a003f21..91440b3 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Busybox version: 1.17.1
-# Tue Jan 17 11:38:38 2012
+# Busybox version: 1.17.2
+# Wed Oct  6 16:34:52 2010
 #
 CONFIG_HAVE_DOT_CONFIG=y
 
@@ -12,11 +12,10 @@ CONFIG_HAVE_DOT_CONFIG=y
 #
 # General Configuration
 #
-# CONFIG_DESKTOP is not set
-CONFIG_EXTRA_COMPAT=y
+CONFIG_DESKTOP=y
+# CONFIG_EXTRA_COMPAT is not set
 CONFIG_INCLUDE_SUSv2=y
 # CONFIG_USE_PORTABLE_CODE is not set
-CONFIG_PLATFORM_LINUX=y
 CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
 # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
 # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
@@ -27,7 +26,7 @@ CONFIG_FEATURE_COMPRESS_USAGE=y
 # CONFIG_LOCALE_SUPPORT is not set
 CONFIG_UNICODE_SUPPORT=y
 # CONFIG_UNICODE_USING_LOCALE is not set
-CONFIG_FEATURE_CHECK_UNICODE_IN_ENV=y
+# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
 CONFIG_SUBST_WCHAR=63
 CONFIG_LAST_SUPPORTED_WCHAR=767
 # CONFIG_UNICODE_COMBINING_WCHARS is not set
@@ -45,7 +44,6 @@ CONFIG_FEATURE_SUID=y
 # CONFIG_FEATURE_SUID_CONFIG is not set
 # CONFIG_FEATURE_SUID_CONFIG_QUIET is not set
 # CONFIG_SELINUX is not set
-CONFIG_SMACK=y
 # CONFIG_FEATURE_PREFER_APPLETS is not set
 CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
 CONFIG_FEATURE_SYSLOG=y
@@ -85,26 +83,26 @@ CONFIG_INSTALL_APPLET_SYMLINKS=y
 # CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
 # CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
 # CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
-CONFIG_PREFIX="./_install"
+CONFIG_PREFIX="/usr"
 
 #
 # Busybox Library Tuning
 #
 CONFIG_PASSWORD_MINLEN=6
-CONFIG_MD5_SIZE_VS_SPEED=0
+CONFIG_MD5_SIZE_VS_SPEED=2
 CONFIG_FEATURE_FAST_TOP=y
 # CONFIG_FEATURE_ETC_NETWORKS is not set
 CONFIG_FEATURE_EDITING=y
 CONFIG_FEATURE_EDITING_MAX_LEN=1024
 # CONFIG_FEATURE_EDITING_VI is not set
-CONFIG_FEATURE_EDITING_HISTORY=15
+CONFIG_FEATURE_EDITING_HISTORY=666
 CONFIG_FEATURE_EDITING_SAVEHISTORY=y
 CONFIG_FEATURE_TAB_COMPLETION=y
 # CONFIG_FEATURE_USERNAME_COMPLETION is not set
-CONFIG_FEATURE_EDITING_FANCY_PROMPT=y
-CONFIG_FEATURE_EDITING_ASK_TERMINAL=y
-# CONFIG_FEATURE_NON_POSIX_CP is not set
-CONFIG_FEATURE_VERBOSE_CP_MESSAGE=y
+# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set
+# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
+CONFIG_FEATURE_NON_POSIX_CP=y
+# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
 CONFIG_FEATURE_COPYBUF_KB=4
 CONFIG_MONOTONIC_SYSCALL=y
 CONFIG_IOCTL_HEX2STR_ERROR=y
@@ -118,18 +116,18 @@ CONFIG_FEATURE_HWIB=y
 # Archival Utilities
 #
 CONFIG_FEATURE_SEAMLESS_XZ=y
-CONFIG_FEATURE_SEAMLESS_LZMA=y
-CONFIG_FEATURE_SEAMLESS_BZ2=y
-CONFIG_FEATURE_SEAMLESS_GZ=y
-CONFIG_FEATURE_SEAMLESS_Z=y
+# CONFIG_FEATURE_SEAMLESS_LZMA is not set
+# CONFIG_FEATURE_SEAMLESS_BZ2 is not set
+# CONFIG_FEATURE_SEAMLESS_GZ is not set
+# CONFIG_FEATURE_SEAMLESS_Z is not set
 CONFIG_AR=y
 CONFIG_FEATURE_AR_LONG_FILENAMES=y
 CONFIG_FEATURE_AR_CREATE=y
 CONFIG_BUNZIP2=y
 CONFIG_BZIP2=y
 CONFIG_CPIO=y
-CONFIG_FEATURE_CPIO_O=y
-CONFIG_FEATURE_CPIO_P=y
+# CONFIG_FEATURE_CPIO_O is not set
+# CONFIG_FEATURE_CPIO_P is not set
 # CONFIG_DPKG is not set
 # CONFIG_DPKG_DEB is not set
 # CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
@@ -137,27 +135,27 @@ CONFIG_GUNZIP=y
 CONFIG_GZIP=y
 CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
 CONFIG_LZOP=y
-CONFIG_LZOP_COMPR_HIGH=y
+# CONFIG_LZOP_COMPR_HIGH is not set
 CONFIG_RPM2CPIO=y
 CONFIG_RPM=y
 CONFIG_TAR=y
 CONFIG_FEATURE_TAR_CREATE=y
-CONFIG_FEATURE_TAR_AUTODETECT=y
+# CONFIG_FEATURE_TAR_AUTODETECT is not set
 CONFIG_FEATURE_TAR_FROM=y
-CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y
-CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y
+# CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY is not set
+# CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set
 CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
 CONFIG_FEATURE_TAR_LONG_OPTIONS=y
 CONFIG_FEATURE_TAR_TO_COMMAND=y
-CONFIG_FEATURE_TAR_UNAME_GNAME=y
+# CONFIG_FEATURE_TAR_UNAME_GNAME is not set
 CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
 # CONFIG_FEATURE_TAR_SELINUX is not set
 CONFIG_UNCOMPRESS=y
 CONFIG_UNLZMA=y
-CONFIG_FEATURE_LZMA_FAST=y
+# CONFIG_FEATURE_LZMA_FAST is not set
 CONFIG_LZMA=y
-# CONFIG_UNXZ is not set
-# CONFIG_XZ is not set
+CONFIG_UNXZ=y
+CONFIG_XZ=y
 CONFIG_UNZIP=y
 
 #
@@ -167,7 +165,7 @@ CONFIG_BASENAME=y
 CONFIG_CAT=y
 CONFIG_DATE=y
 CONFIG_FEATURE_DATE_ISOFMT=y
-CONFIG_FEATURE_DATE_NANO=y
+# CONFIG_FEATURE_DATE_NANO is not set
 CONFIG_FEATURE_DATE_COMPAT=y
 CONFIG_TEST=y
 CONFIG_FEATURE_TEST_64=y
@@ -201,8 +199,8 @@ CONFIG_ECHO=y
 CONFIG_FEATURE_FANCY_ECHO=y
 CONFIG_ENV=y
 CONFIG_FEATURE_ENV_LONG_OPTIONS=y
-CONFIG_EXPAND=y
-CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y
+# CONFIG_EXPAND is not set
+# CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set
 CONFIG_EXPR=y
 CONFIG_EXPR_MATH_SUPPORT_64=y
 CONFIG_FALSE=y
@@ -224,7 +222,7 @@ CONFIG_FEATURE_LS_RECURSIVE=y
 CONFIG_FEATURE_LS_SORTFILES=y
 CONFIG_FEATURE_LS_TIMESTAMPS=y
 CONFIG_FEATURE_LS_USERNAME=y
-CONFIG_FEATURE_LS_COLOR=y
+# CONFIG_FEATURE_LS_COLOR is not set
 # CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set
 CONFIG_MD5SUM=y
 CONFIG_MKDIR=y
@@ -240,18 +238,18 @@ CONFIG_PRINTENV=y
 CONFIG_PRINTF=y
 CONFIG_PWD=y
 CONFIG_READLINK=y
-CONFIG_FEATURE_READLINK_FOLLOW=y
+# CONFIG_FEATURE_READLINK_FOLLOW is not set
 CONFIG_REALPATH=y
 CONFIG_RM=y
 CONFIG_RMDIR=y
-CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y
+# CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set
 CONFIG_SEQ=y
 CONFIG_SHA1SUM=y
 CONFIG_SHA256SUM=y
 CONFIG_SHA512SUM=y
 CONFIG_SLEEP=y
-CONFIG_FEATURE_FANCY_SLEEP=y
-CONFIG_FEATURE_FLOAT_SLEEP=y
+# CONFIG_FEATURE_FANCY_SLEEP is not set
+# CONFIG_FEATURE_FLOAT_SLEEP is not set
 CONFIG_SORT=y
 CONFIG_FEATURE_SORT_BIG=y
 CONFIG_SPLIT=y
@@ -261,17 +259,17 @@ CONFIG_FEATURE_STAT_FORMAT=y
 CONFIG_STTY=y
 CONFIG_SUM=y
 CONFIG_SYNC=y
-CONFIG_TAC=y
+# CONFIG_TAC is not set
 CONFIG_TAIL=y
-CONFIG_FEATURE_FANCY_TAIL=y
+# CONFIG_FEATURE_FANCY_TAIL is not set
 CONFIG_TEE=y
-CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
+# CONFIG_FEATURE_TEE_USE_BLOCK_IO is not set
 CONFIG_TOUCH=y
 CONFIG_TRUE=y
 CONFIG_TTY=y
 CONFIG_UNAME=y
-CONFIG_UNEXPAND=y
-CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y
+# CONFIG_UNEXPAND is not set
+# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set
 CONFIG_UNIQ=y
 CONFIG_USLEEP=y
 CONFIG_UUDECODE=y
@@ -285,7 +283,7 @@ CONFIG_YES=y
 #
 # Common options for cp and mv
 #
-CONFIG_FEATURE_PRESERVE_HARDLINKS=y
+# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set
 
 #
 # Common options for ls, more and telnet
@@ -307,20 +305,20 @@ CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
 #
 CONFIG_CHVT=y
 CONFIG_FGCONSOLE=y
-# CONFIG_CLEAR is not set
+CONFIG_CLEAR=y
 CONFIG_DEALLOCVT=y
 CONFIG_DUMPKMAP=y
-CONFIG_KBD_MODE=y
+# CONFIG_KBD_MODE is not set
 CONFIG_LOADFONT=y
 CONFIG_LOADKMAP=y
 CONFIG_OPENVT=y
-# CONFIG_RESET is not set
+CONFIG_RESET=y
 CONFIG_RESIZE=y
 CONFIG_FEATURE_RESIZE_PRINT=y
 CONFIG_SETCONSOLE=y
-CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS=y
-# CONFIG_SETFONT is not set
-# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
+# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
+CONFIG_SETFONT=y
+CONFIG_FEATURE_SETFONT_TEXTUAL_MAP=y
 CONFIG_DEFAULT_SETFONT_DIR=""
 CONFIG_SETKEYCODES=y
 CONFIG_SETLOGCONS=y
@@ -337,19 +335,19 @@ CONFIG_FEATURE_LOADFONT_RAW=y
 #
 CONFIG_MKTEMP=y
 # CONFIG_PIPE_PROGRESS is not set
-# CONFIG_RUN_PARTS is not set
-# CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set
-# CONFIG_FEATURE_RUN_PARTS_FANCY is not set
+CONFIG_RUN_PARTS=y
+CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y
+CONFIG_FEATURE_RUN_PARTS_FANCY=y
 # CONFIG_START_STOP_DAEMON is not set
 # CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set
 # CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set
-# CONFIG_WHICH is not set
+CONFIG_WHICH=y
 
 #
 # Editors
 #
 CONFIG_AWK=y
-CONFIG_FEATURE_AWK_LIBM=y
+# CONFIG_FEATURE_AWK_LIBM is not set
 CONFIG_CMP=y
 CONFIG_DIFF=y
 CONFIG_FEATURE_DIFF_LONG_OPTIONS=y
@@ -358,7 +356,7 @@ CONFIG_ED=y
 CONFIG_PATCH=y
 CONFIG_SED=y
 CONFIG_VI=y
-CONFIG_FEATURE_VI_MAX_LEN=4096
+CONFIG_FEATURE_VI_MAX_LEN=1024
 CONFIG_FEATURE_VI_8BIT=y
 CONFIG_FEATURE_VI_COLON=y
 CONFIG_FEATURE_VI_YANKMARK=y
@@ -394,11 +392,10 @@ CONFIG_FEATURE_FIND_DEPTH=y
 CONFIG_FEATURE_FIND_PAREN=y
 CONFIG_FEATURE_FIND_SIZE=y
 CONFIG_FEATURE_FIND_PRUNE=y
-CONFIG_FEATURE_FIND_DELETE=y
+# CONFIG_FEATURE_FIND_DELETE is not set
 CONFIG_FEATURE_FIND_PATH=y
 CONFIG_FEATURE_FIND_REGEX=y
 # CONFIG_FEATURE_FIND_CONTEXT is not set
-CONFIG_FEATURE_FIND_SMACK=y
 CONFIG_FEATURE_FIND_LINKS=y
 CONFIG_GREP=y
 CONFIG_FEATURE_GREP_EGREP_ALIAS=y
@@ -413,65 +410,64 @@ CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
 #
 # Init Utilities
 #
-# CONFIG_BOOTCHARTD is not set
-# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set
-# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set
-# CONFIG_INIT is not set
-# CONFIG_FEATURE_USE_INITTAB is not set
-# CONFIG_FEATURE_KILL_REMOVED is not set
+CONFIG_BOOTCHARTD=y
+CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER=y
+CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE=y
+CONFIG_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+CONFIG_FEATURE_KILL_REMOVED=y
 CONFIG_FEATURE_KILL_DELAY=0
-# CONFIG_FEATURE_INIT_SCTTY is not set
-# CONFIG_FEATURE_INIT_SYSLOG is not set
-# CONFIG_FEATURE_EXTRA_QUIET is not set
-# CONFIG_FEATURE_INIT_COREDUMPS is not set
-# CONFIG_FEATURE_INITRD is not set
-CONFIG_INIT_TERMINAL_TYPE=""
-# CONFIG_HALT is not set
+CONFIG_FEATURE_INIT_SCTTY=y
+CONFIG_FEATURE_INIT_SYSLOG=y
+CONFIG_FEATURE_EXTRA_QUIET=y
+CONFIG_FEATURE_INIT_COREDUMPS=y
+CONFIG_FEATURE_INITRD=y
+CONFIG_HALT=y
 # CONFIG_FEATURE_CALL_TELINIT is not set
 CONFIG_TELINIT_PATH=""
-# CONFIG_MESG is not set
+CONFIG_MESG=y
 
 #
 # Login/Password Management Utilities
 #
 CONFIG_FEATURE_SHADOWPASSWDS=y
-# CONFIG_USE_BB_PWD_GRP is not set
-# CONFIG_USE_BB_SHADOW is not set
-# CONFIG_USE_BB_CRYPT is not set
-# CONFIG_USE_BB_CRYPT_SHA is not set
+CONFIG_USE_BB_PWD_GRP=y
+CONFIG_USE_BB_SHADOW=y
+CONFIG_USE_BB_CRYPT=y
+CONFIG_USE_BB_CRYPT_SHA=y
 CONFIG_ADDGROUP=y
 CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y
-CONFIG_FEATURE_ADDUSER_TO_GROUP=y
+# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
 CONFIG_DELGROUP=y
-CONFIG_FEATURE_DEL_USER_FROM_GROUP=y
-CONFIG_FEATURE_CHECK_NAMES=y
+# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
+# CONFIG_FEATURE_CHECK_NAMES is not set
 CONFIG_ADDUSER=y
-CONFIG_FEATURE_ADDUSER_LONG_OPTIONS=y
-CONFIG_FIRST_SYSTEM_ID=0
-CONFIG_LAST_SYSTEM_ID=64900
+# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set
+CONFIG_FIRST_SYSTEM_ID=100
+CONFIG_LAST_SYSTEM_ID=999
 CONFIG_DELUSER=y
 CONFIG_GETTY=y
-# CONFIG_LOGIN is not set
+CONFIG_LOGIN=y
 # CONFIG_PAM is not set
 # CONFIG_LOGIN_SCRIPTS is not set
 # CONFIG_FEATURE_NOLOGIN is not set
-# CONFIG_FEATURE_SECURETTY is not set
+CONFIG_FEATURE_SECURETTY=y
 CONFIG_PASSWD=y
-CONFIG_FEATURE_PASSWD_WEAK_CHECK=y
-# CONFIG_CRYPTPW is not set
-CONFIG_CHPASSWD=y
-# CONFIG_SU is not set
-# CONFIG_FEATURE_SU_SYSLOG is not set
-# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set
-# CONFIG_SULOGIN is not set
+# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set
+CONFIG_CRYPTPW=y
+# CONFIG_CHPASSWD is not set
+CONFIG_SU=y
+CONFIG_FEATURE_SU_SYSLOG=y
+CONFIG_FEATURE_SU_CHECKS_SHELLS=y
+CONFIG_SULOGIN=y
 CONFIG_VLOCK=y
 
 #
 # Linux Ext2 FS Progs
 #
-# CONFIG_CHATTR is not set
-# CONFIG_FSCK is not set
-# CONFIG_LSATTR is not set
+CONFIG_CHATTR=y
+CONFIG_FSCK=y
+CONFIG_LSATTR=y
 # CONFIG_TUNE2FS is not set
 
 #
@@ -486,8 +482,8 @@ CONFIG_RMMOD=y
 CONFIG_LSMOD=y
 CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT=y
 CONFIG_MODPROBE=y
-CONFIG_FEATURE_MODPROBE_BLACKLIST=y
-CONFIG_DEPMOD=y
+# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set
+# CONFIG_DEPMOD is not set
 
 #
 # Options common to multiple modutils
@@ -508,18 +504,17 @@ CONFIG_DEFAULT_DEPMOD_FILE="modules.dep"
 #
 # Linux System Utilities
 #
-CONFIG_BLOCKDEV=y
 CONFIG_REV=y
-# CONFIG_ACPID is not set
-# CONFIG_FEATURE_ACPID_COMPAT is not set
+CONFIG_ACPID=y
+CONFIG_FEATURE_ACPID_COMPAT=y
 CONFIG_BLKID=y
 CONFIG_DMESG=y
 CONFIG_FEATURE_DMESG_PRETTY=y
 CONFIG_FBSET=y
-CONFIG_FEATURE_FBSET_FANCY=y
-CONFIG_FEATURE_FBSET_READMODE=y
-CONFIG_FDFLUSH=y
-CONFIG_FDFORMAT=y
+# CONFIG_FEATURE_FBSET_FANCY is not set
+# CONFIG_FEATURE_FBSET_READMODE is not set
+# CONFIG_FDFLUSH is not set
+# CONFIG_FDFORMAT is not set
 CONFIG_FDISK=y
 CONFIG_FDISK_SUPPORT_LARGE_DISKS=y
 CONFIG_FEATURE_FDISK_WRITABLE=y
@@ -527,33 +522,33 @@ CONFIG_FEATURE_FDISK_WRITABLE=y
 # CONFIG_FEATURE_SGI_LABEL is not set
 # CONFIG_FEATURE_SUN_LABEL is not set
 # CONFIG_FEATURE_OSF_LABEL is not set
-CONFIG_FEATURE_FDISK_ADVANCED=y
+# CONFIG_FEATURE_FDISK_ADVANCED is not set
 # CONFIG_FINDFS is not set
 CONFIG_FLOCK=y
-CONFIG_FREERAMDISK=y
-CONFIG_FSCK_MINIX=y
-# CONFIG_MKFS_EXT2 is not set
-CONFIG_MKFS_MINIX=y
-CONFIG_FEATURE_MINIX2=y
+# CONFIG_FREERAMDISK is not set
+# CONFIG_FSCK_MINIX is not set
+CONFIG_MKFS_EXT2=y
+# CONFIG_MKFS_MINIX is not set
+# CONFIG_FEATURE_MINIX2 is not set
 # CONFIG_MKFS_REISER is not set
 CONFIG_MKFS_VFAT=y
 CONFIG_GETOPT=y
 CONFIG_FEATURE_GETOPT_LONG=y
 CONFIG_HEXDUMP=y
-CONFIG_FEATURE_HEXDUMP_REVERSE=y
-CONFIG_HD=y
+# CONFIG_FEATURE_HEXDUMP_REVERSE is not set
+# CONFIG_HD is not set
 CONFIG_HWCLOCK=y
 CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS=y
 CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS=y
 CONFIG_IPCRM=y
 CONFIG_IPCS=y
 CONFIG_LOSETUP=y
-# CONFIG_LSPCI is not set
-# CONFIG_LSUSB is not set
+CONFIG_LSPCI=y
+CONFIG_LSUSB=y
 CONFIG_MDEV=y
 CONFIG_FEATURE_MDEV_CONF=y
-CONFIG_FEATURE_MDEV_RENAME=y
-CONFIG_FEATURE_MDEV_RENAME_REGEXP=y
+# CONFIG_FEATURE_MDEV_RENAME is not set
+# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set
 CONFIG_FEATURE_MDEV_EXEC=y
 CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y
 CONFIG_MKSWAP=y
@@ -562,23 +557,23 @@ CONFIG_MORE=y
 CONFIG_FEATURE_USE_TERMIOS=y
 CONFIG_MOUNT=y
 CONFIG_FEATURE_MOUNT_FAKE=y
-CONFIG_FEATURE_MOUNT_VERBOSE=y
+# CONFIG_FEATURE_MOUNT_VERBOSE is not set
 CONFIG_FEATURE_MOUNT_HELPERS=y
-CONFIG_FEATURE_MOUNT_LABEL=y
+# CONFIG_FEATURE_MOUNT_LABEL is not set
 CONFIG_FEATURE_MOUNT_NFS=y
 CONFIG_FEATURE_MOUNT_CIFS=y
 CONFIG_FEATURE_MOUNT_FLAGS=y
 CONFIG_FEATURE_MOUNT_FSTAB=y
-CONFIG_PIVOT_ROOT=y
+# CONFIG_PIVOT_ROOT is not set
 CONFIG_RDATE=y
-CONFIG_RDEV=y
+# CONFIG_RDEV is not set
 CONFIG_READPROFILE=y
-CONFIG_RTCWAKE=y
-CONFIG_SCRIPT=y
+# CONFIG_RTCWAKE is not set
+# CONFIG_SCRIPT is not set
 CONFIG_SCRIPTREPLAY=y
-CONFIG_SETARCH=y
+# CONFIG_SETARCH is not set
 CONFIG_SWAPONOFF=y
-CONFIG_FEATURE_SWAPON_PRI=y
+# CONFIG_FEATURE_SWAPON_PRI is not set
 CONFIG_SWITCH_ROOT=y
 CONFIG_UMOUNT=y
 CONFIG_FEATURE_UMOUNT_ALL=y
@@ -588,41 +583,41 @@ CONFIG_FEATURE_UMOUNT_ALL=y
 #
 CONFIG_FEATURE_MOUNT_LOOP=y
 CONFIG_FEATURE_MOUNT_LOOP_CREATE=y
-# CONFIG_FEATURE_MTAB_SUPPORT is not set
+CONFIG_FEATURE_MTAB_SUPPORT=y
 CONFIG_VOLUMEID=y
 
 #
 # Filesystem/Volume identification
 #
-CONFIG_FEATURE_VOLUMEID_EXT=y
+# CONFIG_FEATURE_VOLUMEID_EXT is not set
 CONFIG_FEATURE_VOLUMEID_BTRFS=y
-CONFIG_FEATURE_VOLUMEID_REISERFS=y
-CONFIG_FEATURE_VOLUMEID_FAT=y
-CONFIG_FEATURE_VOLUMEID_HFS=y
-CONFIG_FEATURE_VOLUMEID_JFS=y
-CONFIG_FEATURE_VOLUMEID_XFS=y
-CONFIG_FEATURE_VOLUMEID_NTFS=y
-CONFIG_FEATURE_VOLUMEID_ISO9660=y
-CONFIG_FEATURE_VOLUMEID_UDF=y
-CONFIG_FEATURE_VOLUMEID_LUKS=y
-CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y
-CONFIG_FEATURE_VOLUMEID_CRAMFS=y
-CONFIG_FEATURE_VOLUMEID_ROMFS=y
-CONFIG_FEATURE_VOLUMEID_SYSV=y
-CONFIG_FEATURE_VOLUMEID_OCFS2=y
-CONFIG_FEATURE_VOLUMEID_LINUXRAID=y
+# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
+# CONFIG_FEATURE_VOLUMEID_FAT is not set
+# CONFIG_FEATURE_VOLUMEID_HFS is not set
+# CONFIG_FEATURE_VOLUMEID_JFS is not set
+# CONFIG_FEATURE_VOLUMEID_XFS is not set
+# CONFIG_FEATURE_VOLUMEID_NTFS is not set
+# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set
+# CONFIG_FEATURE_VOLUMEID_UDF is not set
+# CONFIG_FEATURE_VOLUMEID_LUKS is not set
+# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set
+# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set
+# CONFIG_FEATURE_VOLUMEID_ROMFS is not set
+# CONFIG_FEATURE_VOLUMEID_SYSV is not set
+# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set
+# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set
 
 #
 # Miscellaneous Utilities
 #
 # CONFIG_CONSPY is not set
-CONFIG_UBIATTACH=y
-CONFIG_UBIDETACH=y
+# CONFIG_UBIATTACH is not set
+# CONFIG_UBIDETACH is not set
 CONFIG_ADJTIMEX=y
 # CONFIG_BBCONFIG is not set
-# CONFIG_BEEP is not set
-CONFIG_FEATURE_BEEP_FREQ=0
-CONFIG_FEATURE_BEEP_LENGTH_MS=0
+CONFIG_BEEP=y
+CONFIG_FEATURE_BEEP_FREQ=4000
+CONFIG_FEATURE_BEEP_LENGTH_MS=30
 # CONFIG_CHAT is not set
 # CONFIG_FEATURE_CHAT_NOFAIL is not set
 # CONFIG_FEATURE_CHAT_TTY_HIFI is not set
@@ -637,25 +632,25 @@ CONFIG_FEATURE_CROND_D=y
 CONFIG_FEATURE_CROND_CALL_SENDMAIL=y
 CONFIG_FEATURE_CROND_DIR="/var/spool/cron"
 CONFIG_CRONTAB=y
-CONFIG_DC=y
-CONFIG_FEATURE_DC_LIBM=y
+# CONFIG_DC is not set
+# CONFIG_FEATURE_DC_LIBM is not set
 # CONFIG_DEVFSD is not set
 # CONFIG_DEVFSD_MODLOAD is not set
 # CONFIG_DEVFSD_FG_NP is not set
 # CONFIG_DEVFSD_VERBOSE is not set
 # CONFIG_FEATURE_DEVFS is not set
-# CONFIG_DEVMEM is not set
+CONFIG_DEVMEM=y
 CONFIG_EJECT=y
-CONFIG_FEATURE_EJECT_SCSI=y
-CONFIG_FBSPLASH=y
-CONFIG_FLASHCP=y
-CONFIG_FLASH_LOCK=y
-CONFIG_FLASH_UNLOCK=y
-CONFIG_FLASH_ERASEALL=y
+# CONFIG_FEATURE_EJECT_SCSI is not set
+# CONFIG_FBSPLASH is not set
+# CONFIG_FLASHCP is not set
+# CONFIG_FLASH_LOCK is not set
+# CONFIG_FLASH_UNLOCK is not set
+# CONFIG_FLASH_ERASEALL is not set
 CONFIG_IONICE=y
-CONFIG_INOTIFYD=y
-# CONFIG_LAST is not set
-# CONFIG_FEATURE_LAST_SMALL is not set
+# CONFIG_INOTIFYD is not set
+CONFIG_LAST=y
+CONFIG_FEATURE_LAST_SMALL=y
 # CONFIG_FEATURE_LAST_FANCY is not set
 CONFIG_LESS=y
 CONFIG_FEATURE_LESS_MAXLINES=9999999
@@ -677,13 +672,13 @@ CONFIG_MAKEDEVS=y
 # CONFIG_FEATURE_MAKEDEVS_LEAF is not set
 CONFIG_FEATURE_MAKEDEVS_TABLE=y
 CONFIG_MAN=y
-CONFIG_MICROCOM=y
+# CONFIG_MICROCOM is not set
 CONFIG_MOUNTPOINT=y
 CONFIG_MT=y
 CONFIG_RAIDAUTORUN=y
-CONFIG_READAHEAD=y
+# CONFIG_READAHEAD is not set
 # CONFIG_RFKILL is not set
-# CONFIG_RUNLEVEL is not set
+CONFIG_RUNLEVEL=y
 CONFIG_RX=y
 CONFIG_SETSID=y
 CONFIG_STRINGS=y
@@ -699,49 +694,49 @@ CONFIG_WATCHDOG=y
 #
 # Networking Utilities
 #
-# CONFIG_NC is not set
-# CONFIG_NC_SERVER is not set
-# CONFIG_NC_EXTRA is not set
+CONFIG_NC=y
+CONFIG_NC_SERVER=y
+CONFIG_NC_EXTRA=y
 # CONFIG_NC_110_COMPAT is not set
 CONFIG_FEATURE_IPV6=y
 # CONFIG_FEATURE_UNIX_LOCAL is not set
-# CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set
-# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
+CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
+CONFIG_VERBOSE_RESOLUTION_ERRORS=y
 CONFIG_ARP=y
 CONFIG_ARPING=y
-CONFIG_BRCTL=y
-CONFIG_FEATURE_BRCTL_FANCY=y
-CONFIG_FEATURE_BRCTL_SHOW=y
+# CONFIG_BRCTL is not set
+# CONFIG_FEATURE_BRCTL_FANCY is not set
+# CONFIG_FEATURE_BRCTL_SHOW is not set
 CONFIG_DNSD=y
 CONFIG_ETHER_WAKE=y
 CONFIG_FAKEIDENTD=y
-# CONFIG_FTPD is not set
-# CONFIG_FEATURE_FTP_WRITE is not set
-# CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set
+CONFIG_FTPD=y
+CONFIG_FEATURE_FTP_WRITE=y
+CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y
 CONFIG_FTPGET=y
 CONFIG_FTPPUT=y
 CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
 CONFIG_HOSTNAME=y
-CONFIG_HTTPD=y
-CONFIG_FEATURE_HTTPD_RANGES=y
-CONFIG_FEATURE_HTTPD_USE_SENDFILE=y
-CONFIG_FEATURE_HTTPD_SETUID=y
-CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
-CONFIG_FEATURE_HTTPD_AUTH_MD5=y
-CONFIG_FEATURE_HTTPD_CGI=y
-CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y
-CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y
-CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
-CONFIG_FEATURE_HTTPD_ERROR_PAGES=y
-CONFIG_FEATURE_HTTPD_PROXY=y
+# CONFIG_HTTPD is not set
+# CONFIG_FEATURE_HTTPD_RANGES is not set
+# CONFIG_FEATURE_HTTPD_USE_SENDFILE is not set
+# CONFIG_FEATURE_HTTPD_SETUID is not set
+# CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set
+# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
+# CONFIG_FEATURE_HTTPD_CGI is not set
+# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
+# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
+# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set
+# CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set
+# CONFIG_FEATURE_HTTPD_PROXY is not set
 CONFIG_IFCONFIG=y
 CONFIG_FEATURE_IFCONFIG_STATUS=y
 CONFIG_FEATURE_IFCONFIG_SLIP=y
 CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y
 CONFIG_FEATURE_IFCONFIG_HW=y
 CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y
-CONFIG_IFENSLAVE=y
-# CONFIG_IFPLUGD is not set
+# CONFIG_IFENSLAVE is not set
+CONFIG_IFPLUGD=y
 CONFIG_IFUPDOWN=y
 CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate"
 CONFIG_FEATURE_IFUPDOWN_IP=y
@@ -751,13 +746,13 @@ CONFIG_FEATURE_IFUPDOWN_IPV4=y
 CONFIG_FEATURE_IFUPDOWN_IPV6=y
 CONFIG_FEATURE_IFUPDOWN_MAPPING=y
 # CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set
-CONFIG_INETD=y
-CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO=y
-CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD=y
-CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME=y
-CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME=y
-CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN=y
-CONFIG_FEATURE_INETD_RPC=y
+# CONFIG_INETD is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set
+# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set
+# CONFIG_FEATURE_INETD_RPC is not set
 CONFIG_IP=y
 CONFIG_FEATURE_IP_ADDRESS=y
 CONFIG_FEATURE_IP_LINK=y
@@ -765,7 +760,7 @@ CONFIG_FEATURE_IP_ROUTE=y
 CONFIG_FEATURE_IP_TUNNEL=y
 CONFIG_FEATURE_IP_RULE=y
 CONFIG_FEATURE_IP_SHORT_FORMS=y
-CONFIG_FEATURE_IP_RARE_PROTOCOLS=y
+# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
 CONFIG_IPADDR=y
 CONFIG_IPLINK=y
 CONFIG_IPROUTE=y
@@ -775,20 +770,20 @@ CONFIG_IPCALC=y
 CONFIG_FEATURE_IPCALC_FANCY=y
 CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y
 CONFIG_NAMEIF=y
-CONFIG_FEATURE_NAMEIF_EXTENDED=y
+# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
 CONFIG_NETSTAT=y
 CONFIG_FEATURE_NETSTAT_WIDE=y
-CONFIG_FEATURE_NETSTAT_PRG=y
+# CONFIG_FEATURE_NETSTAT_PRG is not set
 CONFIG_NSLOOKUP=y
-# CONFIG_NTPD is not set
-# CONFIG_FEATURE_NTPD_SERVER is not set
+CONFIG_NTPD=y
+CONFIG_FEATURE_NTPD_SERVER=y
 CONFIG_PING=y
 CONFIG_PING6=y
 CONFIG_FEATURE_FANCY_PING=y
 CONFIG_PSCAN=y
 CONFIG_ROUTE=y
-CONFIG_SLATTACH=y
-CONFIG_TCPSVD=y
+# CONFIG_SLATTACH is not set
+# CONFIG_TCPSVD is not set
 CONFIG_TELNET=y
 CONFIG_FEATURE_TELNET_TTYPE=y
 CONFIG_FEATURE_TELNET_AUTOLOGIN=y
@@ -805,35 +800,34 @@ CONFIG_FEATURE_TFTP_GET=y
 CONFIG_FEATURE_TFTP_PUT=y
 CONFIG_FEATURE_TFTP_BLOCKSIZE=y
 CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
-CONFIG_TFTP_DEBUG=y
+# CONFIG_TFTP_DEBUG is not set
 CONFIG_TRACEROUTE=y
-# CONFIG_TRACEROUTE6 is not set
+CONFIG_TRACEROUTE6=y
 CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
 CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE=y
 CONFIG_FEATURE_TRACEROUTE_USE_ICMP=y
-# CONFIG_TUNCTL is not set
-# CONFIG_FEATURE_TUNCTL_UG is not set
+CONFIG_TUNCTL=y
+CONFIG_FEATURE_TUNCTL_UG=y
 CONFIG_UDHCPD=y
 CONFIG_DHCPRELAY=y
 CONFIG_DUMPLEASES=y
-CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY=y
-CONFIG_DHCPD_LEASES_FILE="/var/lib/misc/udhcpd.leases"
+# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
+CONFIG_DHCPD_LEASES_FILE=""
 CONFIG_UDHCPC=y
 CONFIG_FEATURE_UDHCPC_ARPING=y
-CONFIG_FEATURE_UDHCP_PORT=y
-CONFIG_UDHCP_DEBUG=0
+# CONFIG_FEATURE_UDHCP_PORT is not set
+CONFIG_UDHCP_DEBUG=9
 CONFIG_FEATURE_UDHCP_RFC3397=y
 CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script"
 CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80
-CONFIG_FEATURE_UDHCPC_FAST_REQUEST=y
 CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n"
-CONFIG_UDPSVD=y
+# CONFIG_UDPSVD is not set
 CONFIG_VCONFIG=y
 CONFIG_WGET=y
 CONFIG_FEATURE_WGET_STATUSBAR=y
 CONFIG_FEATURE_WGET_AUTHENTICATION=y
 CONFIG_FEATURE_WGET_LONG_OPTIONS=y
-CONFIG_ZCIP=y
+# CONFIG_ZCIP is not set
 
 #
 # Print Utilities
@@ -845,33 +839,33 @@ CONFIG_ZCIP=y
 #
 # Mail Utilities
 #
-# CONFIG_MAKEMIME is not set
+CONFIG_MAKEMIME=y
 CONFIG_FEATURE_MIME_CHARSET="us-ascii"
-# CONFIG_POPMAILDIR is not set
-# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set
-# CONFIG_REFORMIME is not set
-# CONFIG_FEATURE_REFORMIME_COMPAT is not set
-CONFIG_SENDMAIL=y
+CONFIG_POPMAILDIR=y
+CONFIG_FEATURE_POPMAILDIR_DELIVERY=y
+CONFIG_REFORMIME=y
+CONFIG_FEATURE_REFORMIME_COMPAT=y
+# CONFIG_SENDMAIL is not set
 
 #
 # Process Utilities
 #
-# CONFIG_SMEMCAP is not set
+CONFIG_SMEMCAP=y
 CONFIG_FREE=y
 CONFIG_FUSER=y
 CONFIG_KILL=y
 CONFIG_KILLALL=y
-# CONFIG_KILLALL5 is not set
-CONFIG_NMETER=y
-CONFIG_PGREP=y
-# CONFIG_PIDOF is not set
-# CONFIG_FEATURE_PIDOF_SINGLE is not set
-# CONFIG_FEATURE_PIDOF_OMIT is not set
-CONFIG_PKILL=y
+CONFIG_KILLALL5=y
+# CONFIG_NMETER is not set
+# CONFIG_PGREP is not set
+CONFIG_PIDOF=y
+CONFIG_FEATURE_PIDOF_SINGLE=y
+CONFIG_FEATURE_PIDOF_OMIT=y
+# CONFIG_PKILL is not set
 CONFIG_PS=y
 CONFIG_FEATURE_PS_WIDE=y
 # CONFIG_FEATURE_PS_TIME is not set
-# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set
+CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y
 # CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
 CONFIG_RENICE=y
 CONFIG_BB_SYSCTL=y
@@ -879,7 +873,7 @@ CONFIG_TOP=y
 CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y
 CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y
 CONFIG_FEATURE_TOP_SMP_CPU=y
-CONFIG_FEATURE_TOP_DECIMALS=y
+# CONFIG_FEATURE_TOP_DECIMALS is not set
 CONFIG_FEATURE_TOP_SMP_PROCESS=y
 CONFIG_FEATURE_TOPMEM=y
 CONFIG_FEATURE_SHOW_THREADS=y
@@ -889,17 +883,17 @@ CONFIG_WATCH=y
 #
 # Runit Utilities
 #
-CONFIG_RUNSV=y
-CONFIG_RUNSVDIR=y
-CONFIG_FEATURE_RUNSVDIR_LOG=y
-CONFIG_SV=y
-CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service"
+# CONFIG_RUNSV is not set
+# CONFIG_RUNSVDIR is not set
+# CONFIG_FEATURE_RUNSVDIR_LOG is not set
+# CONFIG_SV is not set
+CONFIG_SV_DEFAULT_SERVICE_DIR=""
 CONFIG_SVLOGD=y
-CONFIG_CHPST=y
-CONFIG_SETUIDGID=y
-CONFIG_ENVUIDGID=y
-CONFIG_ENVDIR=y
-CONFIG_SOFTLIMIT=y
+# CONFIG_CHPST is not set
+# CONFIG_SETUIDGID is not set
+# CONFIG_ENVUIDGID is not set
+# CONFIG_ENVDIR is not set
+# CONFIG_SOFTLIMIT is not set
 # CONFIG_CHCON is not set
 # CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set
 # CONFIG_GETENFORCE is not set
@@ -927,11 +921,11 @@ CONFIG_ASH_GETOPTS=y
 CONFIG_ASH_BUILTIN_ECHO=y
 CONFIG_ASH_BUILTIN_PRINTF=y
 CONFIG_ASH_BUILTIN_TEST=y
-CONFIG_ASH_CMDCMD=y
+# CONFIG_ASH_CMDCMD is not set
 # CONFIG_ASH_MAIL is not set
 CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
-CONFIG_ASH_RANDOM_SUPPORT=y
-CONFIG_ASH_EXPAND_PRMT=y
+# CONFIG_ASH_RANDOM_SUPPORT is not set
+# CONFIG_ASH_EXPAND_PRMT is not set
 # CONFIG_HUSH is not set
 # CONFIG_HUSH_BASH_COMPAT is not set
 # CONFIG_HUSH_HELP is not set
@@ -955,31 +949,22 @@ CONFIG_FEATURE_BASH_IS_NONE=y
 # CONFIG_MSH is not set
 CONFIG_SH_MATH_SUPPORT=y
 CONFIG_SH_MATH_SUPPORT_64=y
-CONFIG_FEATURE_SH_EXTRA_QUIET=y
+# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
 # CONFIG_FEATURE_SH_STANDALONE is not set
 # CONFIG_FEATURE_SH_NOFORK is not set
-# CONFIG_CTTYHACK is not set
-
-#
-# Smack Utilities
-#
-CONFIG_SMACKLOAD=y
-CONFIG_SMACKCIPSO=y
-CONFIG_NEWSMACK=y
-CONFIG_SMACKENABLED=y
+CONFIG_CTTYHACK=y
 
 #
 # System Logging Utilities
 #
 CONFIG_SYSLOGD=y
 CONFIG_FEATURE_ROTATE_LOGFILE=y
-CONFIG_FEATURE_REMOTE_LOG=y
-CONFIG_FEATURE_SYSLOGD_DUP=y
+# CONFIG_FEATURE_REMOTE_LOG is not set
+# CONFIG_FEATURE_SYSLOGD_DUP is not set
 CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256
-CONFIG_FEATURE_IPC_SYSLOG=y
-CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16
-CONFIG_LOGREAD=y
-CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y
+# CONFIG_FEATURE_IPC_SYSLOG is not set
+CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0
+# CONFIG_LOGREAD is not set
+# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
 CONFIG_KLOGD=y
-CONFIG_FEATURE_KLOGD_KLOGCTL=y
 CONFIG_LOGGER=y
index c922afb..017d22d 100644 (file)
@@ -1,8 +1,5 @@
 <manifest>
-        <request>
-                <domain name="_"/>
-        </request>
-        <assign>
-                <filesystem path="/bin/*" exec_label="none" />
-        </assign>
+ <request>
+    <domain name="_"/>
+ </request>
 </manifest>
old mode 100755 (executable)
new mode 100644 (file)
index f84fb1a..e1d6d85
-#sbs-git:slp/pkgs/b/busybox busybox 1.17.1 2af797aea95edf3accab73b53dd8075b78e75ddd
-Summary: Single binary providing simplified versions of system commands
-Name: busybox
-Version: 1.17.1
-Release: 28
-License: GPLv2
-Group: System/Shells
-Source: http://www.busybox.net/downloads/%{name}-%{version}.tar.gz
-Source1: busybox-dahlia.config
-Source2: bin.links
-Source3: sbin.links
-Source4: usrbin.links
-Source5: usrsbin.links
-Source101: tizen.config
-Source201: klogd.service
-Source202: syslogd.service
-Source1001: %{name}.manifest
-Source1002: syslogd.manifest
-
-Patch0: 06ls.patch
-Patch1: doc-man-name.patch
-Patch2: shell-ash-export-HOME.patch
-Patch3: applets-fallback.patch
-Patch4: version.patch
-Patch5: init-console.patch
-#Patch6: 05thumb.dpatch
-#Patch7: 06ls.patch
-Patch8: busybox-zero-ifr.ifr_hwaddr.sa_data.patch
-Patch9: top-display-rss.patch
-Patch10: strip.patch
-Patch11: make_gen_build_files_skip_quilt.patch
-
-# The following patches have been merged upstream and will be in version 1.18
-Patch12: readlink-use-xmalloc_realpath.patch
-Patch13: mark-Linux-specific-configuration-options.patch
-Patch14: init-loginutils-termios-portability-fixes.patch
-Patch15: init-halt-portability-improvements.patch
-Patch16: init-make-the-initial-TERM-value-configurable.patch
-Patch17: libbb.h-add-device-names-for-Hurd-and-FreeBSD.patch
-Patch18: mkdir-fix-p-on-FreeBSD.patch
-Patch19: libbb-conditionalize-AF_-usage-in-error-reporting.patch
-Patch20: tcpsvd-udpsvd-conditionalize-usage-of-SO_ORIGINAL_DS.patch
-Patch21: less-remove-misguided-dependency-on-PLATFORM_LINUX.patch
-Patch22: bootchartd-mounting-tmpfs-is-Linux-specific.patch
-Patch23: vlock-disable-linux-console-calls-on-other-systems.patch
-Patch24: cttyhack-serial-console-detection-is-Linux-specific.patch
-Patch25: klogd-make-it-work-on-non-linux-systems.patch
-Patch26: stty-sort-out-preprocessor-conditionals.patch
-Patch27: update-scripts-kconfig-_shipped.patch
-Patch28: blockdev.patch
-
-# The following patches will likely be merged soon
-Patch29: u-mount-FreeBSD-support.patch
-Patch30: swaponoff-FreeBSD-support.patch
-
-# not sent upstream
-Patch31: init-console-CRTSCTS.patch
-Patch32: debian-changes-1.17.1-10
-
-# SLP
-Patch33: udhcpc-fast-request.patch
-Patch34: smack-busybox-1.17.1.patch
-Patch35: smack-conflict-with-selinux.patch
-Patch36: make_unicode_printable.patch
-
-# The following patches have been merged upstream and will be in version 1.20
-Patch37: busybox-1.20.2-fix-resource-h-failure.patch
-
-Patch100: busybox-1.17.1-make.patch
-Patch101: sysinfo-multiple-define-error-fix.patch
-
-Patch999: syslogd-disable-systemd-sa.patch
-
-URL: http://www.busybox.net
-
-BuildRequires: pkgconfig(libsystemd-daemon)
+Name:           busybox
+Version:        1.22.1
+Release:        0
+License:        GPL-2.0+
+Summary:        The Swiss Army Knife of Embedded Linux
+Url:            http://www.busybox.net/
+Group:          System/Utilities
+Source:         http://busybox.net/downloads/%{name}-%{version}.tar.bz2
+Source2:        busybox.tizen.config
+Source1001:     busybox.manifest
 
 %description
-Busybox is a single binary which includes versions of a large number
-of system commands, including a shell.  This package can be very
-useful for recovering from certain types of system failures,
-particularly those involving broken shared libraries.
-
-%package docs
-Group: Documentation
-Summary: Busybox Documentation
-
-%description docs
-Busybox documentation and user guides
-
-%package symlinks-busybox
-Group: tools
-Summary: BusyBox specific symlinks
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-busybox
- BusyBox symlinks for utilities without counterparts in Debian.
- These are in separate package because they aren't essentials
- (e.g. needed for system package upgrades).
-
-%package symlinks-adduser
-Group: tools
-Summary: BusyBox symlinks to provide 'adduser'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-adduser
-BusyBox symlinks for utilities corresponding to 'adduser' package.
-
-%package symlinks-adjtimex
-Group: tools
-Summary: BusyBox symlinks to provide 'adjtimex'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-adjtimex
-BusyBox symlinks for utilities corresponding to 'adjtimex' package.
-
-%package symlinks-binutils
-Group: tools
-Summary: BusyBox symlinks to provide 'binutils'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-binutils
-BusyBox symlinks for utilities corresponding to 'binutils' package.
-
-%package symlinks-bridge-utils
-Group: tools
-Summary: BusyBox symlinks to provide 'bridge-utils'
-
-%description symlinks-bridge-utils
-BusyBox symlinks for utilities corresponding to 'bridge-utils' package.
-
-%package symlinks-bsdmainutils
-Group: tools
-Summary: BusyBox symlinks to provide 'bsdmainutils'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-bsdmainutils
-BusyBox symlinks for utilities corresponding to 'bsdmainutils' package.
-
-%package symlinks-bzip2
-Group: tools
-Summary: BusyBox symlinks to provide 'bzip2'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-bzip2
-BusyBox symlinks for utilities corresponding to 'bzip2' package.
-
-%package symlinks-console-tools
-Group: tools
-Summary: BusyBox symlinks to provide 'console-tools'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-console-tools
-BusyBox symlinks for utilities corresponding to 'console-tools' package.
-
-%package symlinks-cpio
-Group: tools
-Summary: BusyBox symlinks to provide 'cpio'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-cpio
-BusyBox symlinks for utilities corresponding to 'cpio' package.
-
-%package symlinks-cron
-Group: tools
-Summary: BusyBox symlinks to provide 'cron'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-cron
-BusyBox symlinks for utilities corresponding to 'cron' package.
-
-%package symlinks-daemontools
-Group: tools
-Summary: BusyBox symlinks to provide 'daemontools'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-daemontools
-BusyBox symlinks for utilities corresponding to 'daemontools' package.
-
-%package symlinks-dc
-Group: tools
-Summary: BusyBox symlinks to provide 'dc'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-dc
-BusyBox symlinks for utilities corresponding to 'dc' package.
-
-%package symlinks-dnsutils
-Group: tools
-Summary: BusyBox symlinks to provide 'dnsutils'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-dnsutils
-BusyBox symlinks for utilities corresponding to 'dnsutils' package.
-
-%package symlinks-dosfstools
-Group: tools
-Summary: BusyBox symlinks to provide 'dosfstools'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-dosfstools
-BusyBox symlinks for utilities corresponding to 'dosfstools' package.
-
-%package symlinks-ed
-Group: tools
-Summary: BusyBox symlinks to provide 'ed'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-ed
-BusyBox symlinks for utilities corresponding to 'ed' package.
-
-%package symlinks-eject
-Group: tools
-Summary: BusyBox symlinks to provide 'eject'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-eject
-BusyBox symlinks for utilities corresponding to 'eject' package.
-
-%package symlinks-fbset
-Group: tools
-Summary: BusyBox symlinks to provide 'fbset'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-fbset
-BusyBox symlinks for utilities corresponding to 'fbset' package.
-
-%package symlinks-fdflush
-Group: tools
-Summary: BusyBox symlinks to provide 'fdflush'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-fdflush
-BusyBox symlinks for utilities corresponding to 'fdflush' package.
-
-%package symlinks-hdparm
-Group: tools
-Summary: BusyBox symlinks to provide 'hdparm'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-hdparm
-BusyBox symlinks for utilities corresponding to 'hdparm' package.
-
-%package symlinks-ifupdown
-Group: tools
-Summary: BusyBox symlinks to provide 'ifupdown'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-ifupdown
-BusyBox symlinks for utilities corresponding to 'ifupdown' package.
-
-%package symlinks-initscripts
-Group: tools
-Summary: BusyBox symlinks to provide 'initscripts'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-initscripts
-BusyBox symlinks for utilities corresponding to 'initscripts' package.
-
-%package symlinks-ipcalc
-Group: tools
-Summary: BusyBox symlinks to provide 'ipcalc'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-ipcalc
-BusyBox symlinks for utilities corresponding to 'ipcalc' package.
-
-%package symlinks-iproute
-Group: tools
-Summary: BusyBox symlinks to provide 'iproute'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-iproute
-BusyBox symlinks for utilities corresponding to 'iproute' package.
-
-%package symlinks-ipsvd
-Group: tools
-Summary: BusyBox symlinks to provide 'ipsvd'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-ipsvd
-BusyBox symlinks for utilities corresponding to 'ipsvd' package.
-
-%package symlinks-iputils-arping
-Group: tools
-Summary: BusyBox symlinks to provide 'iputils-arping'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-iputils-arping
-BusyBox symlinks for utilities corresponding to 'iputils-arping' package.
-
-%package symlinks-iputils-ping
-Group: tools
-Summary: BusyBox symlinks to provide 'iputils-ping'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-iputils-ping
-BusyBox symlinks for utilities corresponding to 'iputils-ping' package.
-
-%package symlinks-klogd
-Group: tools
-Summary: BusyBox symlinks to provide 'klogd'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-klogd
-BusyBox symlinks for utilities corresponding to 'klogd' package.
-
-%package symlinks-loadlin
-Group: tools
-Summary: BusyBox symlinks to provide 'loadlin'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-loadlin
-BusyBox symlinks for utilities corresponding to 'loadlin' package.
-
-%package symlinks-lrzsz
-Group: tools
-Summary: BusyBox symlinks to provide 'lrzsz'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-lrzsz
-BusyBox symlinks for utilities corresponding to 'lrzsz' package.
-
-%package symlinks-lzma
-Group: tools
-Summary: BusyBox symlinks to provide 'lzma'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-lzma
-BusyBox symlinks for utilities corresponding to 'lzma' package.
-
-%package symlinks-lzop
-Group: tools
-Summary: BusyBox symlinks to provide 'lzop'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-lzop
-BusyBox symlinks for utilities corresponding to 'lzop' package.
-
-%package symlinks-module-init-tools
-Group: tools
-Summary: BusyBox symlinks to provide 'module-init-tools'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-module-init-tools
-BusyBox symlinks for utilities corresponding to 'module-init-tools' package.
-
-%package symlinks-mtd-utils
-Group: tools
-Summary: BusyBox symlinks to provide 'mtd-utils'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-mtd-utils
-BusyBox symlinks for utilities corresponding to 'mtd-utils' package.
-
-%package symlinks-net-tools
-Group: tools
-Summary: BusyBox symlinks to provide 'net-tools'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-net-tools
-BusyBox symlinks for utilities corresponding to 'net-tools' package.
-
-%package symlinks-openbsd-inetd
-Group: tools
-Summary: BusyBox symlinks to provide 'openbsd-inetd'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-openbsd-inetd
-BusyBox symlinks for utilities corresponding to 'openbsd-inetd' package.
-
-%package symlinks-passwd
-Group: tools
-Summary: BusyBox symlinks to provide 'passwd'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-passwd
-BusyBox symlinks for utilities corresponding to 'passwd' package.
-
-%package symlinks-patch
-Group: tools
-Summary: BusyBox symlinks to provide 'patch'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-patch
-BusyBox symlinks for utilities corresponding to 'patch' package.
-
-%package symlinks-ppp
-Group: tools
-Summary: BusyBox symlinks to provide 'ppp'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-ppp
-BusyBox symlinks for utilities corresponding to 'ppp' package.
-
-%package symlinks-procps
-Group: tools
-Summary: BusyBox symlinks to provide 'procps'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-procps
-BusyBox symlinks for utilities corresponding to 'procps' package.
-
-%package symlinks-psmisc
-Group: tools
-Summary: BusyBox symlinks to provide 'psmisc'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-psmisc
-BusyBox symlinks for utilities corresponding to 'psmisc' package.
-
-%package symlinks-rdate
-Group: tools
-Summary: BusyBox symlinks to provide 'rdate'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-rdate
-BusyBox symlinks for utilities corresponding to 'rdate' package.
-
-%package symlinks-realpath
-Group: tools
-Summary: BusyBox symlinks to provide 'realpath'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-realpath
-BusyBox symlinks for utilities corresponding to 'realpath' package.
-
-%package symlinks-runit
-Group: tools
-Summary: BusyBox symlinks to provide 'runit'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-runit
-BusyBox symlinks for utilities corresponding to 'runit' package.
-
-%package symlinks-sharutils
-Group: tools
-Summary: BusyBox symlinks to provide 'sharutils'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-sharutils
-BusyBox symlinks for utilities corresponding to 'sharutils' package.
-
-%package symlinks-ssmtp
-Group: tools
-Summary: BusyBox symlinks to provide 'ssmtp'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-ssmtp
-BusyBox symlinks for utilities corresponding to 'ssmtp' package.
-
-%package symlinks-sysklogd
-Group: tools
-Summary: BusyBox symlinks to provide 'sysklogd'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-sysklogd
-BusyBox symlinks for utilities corresponding to 'sysklogd' package.
-
-%package symlinks-telnetd
-Group: tools
-Summary: BusyBox symlinks to provide 'telnetd'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-telnetd
-BusyBox symlinks for utilities corresponding to 'telnetd' package.
-
-%package symlinks-tftp
-Group: tools
-Summary: BusyBox symlinks to provide 'tftp'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-tftp
-BusyBox symlinks for utilities corresponding to 'tftp' package.
-
-%package symlinks-time
-Group: tools
-Summary: BusyBox symlinks to provide 'time'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-time
-BusyBox symlinks for utilities corresponding to 'time' package.
-
-%package symlinks-tofrodos
-Group: tools
-Summary: BusyBox symlinks to provide 'tofrodos'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-tofrodos
-BusyBox symlinks for utilities corresponding to 'tofrodos' package.
-
-%package symlinks-udhcpc
-Group: tools
-Summary: BusyBox symlinks to provide 'udhcpc'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-udhcpc
-BusyBox symlinks for utilities corresponding to 'udhcpc' package.
-
-%package symlinks-udhcpd
-Group: tools
-Summary: BusyBox symlinks to provide 'udhcpd'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-udhcpd
-BusyBox symlinks for utilities corresponding to 'udhcpd' package.
-
-%package symlinks-unzip
-Group: tools
-Summary: BusyBox symlinks to provide 'unzip'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-unzip
-BusyBox symlinks for utilities corresponding to 'unzip' package.
-
-%package symlinks-vlan
-Group: tools
-Summary: BusyBox symlinks to provide 'vlan'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-vlan
-BusyBox symlinks for utilities corresponding to 'vlan' package.
-
-%package symlinks-vlock
-Group: tools
-Summary: BusyBox symlinks to provide 'vlock'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-vlock
-BusyBox symlinks for utilities corresponding to 'vlock' package.
-
-%package symlinks-watchdog
-Group: tools
-Summary: BusyBox symlinks to provide 'watchdog'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-watchdog
-BusyBox symlinks for utilities corresponding to 'watchdog' package.
-
-%package symlinks-wget
-Group: tools
-Summary: BusyBox symlinks to provide 'wget'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-wget
-BusyBox symlinks for utilities corresponding to 'wget' package.
-
-%package symlinks-xterm
-Group: tools
-Summary: BusyBox symlinks to provide 'xterm'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-xterm
-BusyBox symlinks for utilities corresponding to 'xterm' package.
-
-%package symlinks-zcip
-Group: tools
-Summary: BusyBox symlinks to provide 'zcip'
-Requires: %{name} = %{version}-%{release}
-
-%description symlinks-zcip
-BusyBox symlinks for utilities corresponding to 'zcip' package.
-
+BusyBox combines tiny versions of many common UNIX utilities into a
+small single executable. It provides minimalist replacements for most
+of the utilities usually found in fileutils, shellutils, findutils,
+textutils, grep, gzip, tar, and more. BusyBox provides a fairly
+complete POSIX environment for any small or embedded system. The
+utilities in BusyBox generally have fewer options than their
+full-featured GNU cousins. The options that are included provide the
+expected functionality and behave very much like their GNU
+counterparts.
 
 %prep
 %setup -q
-%patch0 -p1
-%patch1 -p1
-%patch2 -p1
-%patch3 -p1
-%patch4 -p1
-%patch5 -p1
-#%patch6 -p1
-#%patch7 -p1
-%patch8 -p1
-%patch9 -p1
-%patch10 -p1
-%patch11 -p1
-%patch12 -p1
-%patch13 -p1
-%patch14 -p1
-%patch15 -p1
-%patch16 -p1
-%patch17 -p1
-%patch18 -p1
-%patch19 -p1
-%patch20 -p1
-%patch21 -p1
-%patch22 -p1
-%patch23 -p1
-%patch24 -p1
-%patch25 -p1
-%patch26 -p1
-%patch27 -p1
-%patch28 -p1
-%patch29 -p1
-%patch30 -p1
-%patch31 -p1
-%patch32 -p1
-%patch33 -p1
-%patch36 -p1
-%patch37 -p1
-
-%patch100 -p1
-%patch101 -p1
-
-%patch999 -p1
+cp %{SOURCE1001} .
+cp -a %{SOURCE2} .config
 
 %build
-cp %{SOURCE1001} .
-cp %{SOURCE1002} .
-# create dynamic busybox - the executable is busybox
-cp %{SOURCE101} .config
-make oldconfig
-make CC="gcc $RPM_OPT_FLAGS"
-cp busybox busybox-dynamic
+export VERBOSE=-v
+export BUILD_VERBOSE=2
+export CFLAGS="%{optflags} -fno-strict-aliasing"
+export CC="gcc"
+export HOSTCC=gcc
+%__make -e oldconfig
+%__make -e %{?_smp_mflags}
+%__make -e doc busybox.links %{?_smp_mflags}
 
 %install
-rm -rf $RPM_BUILD_ROOT
-mkdir -p $RPM_BUILD_ROOT/bin
-install -m 755 busybox-dynamic $RPM_BUILD_ROOT/bin/busybox
-
-# debian/busybox.links
-pushd %{buildroot}
-mkdir -p usr/bin usr/sbin sbin
-cd bin
-for f in `cat %SOURCE2` ; do ln -s busybox $f ; done
-cd ../sbin
-for f in `cat %SOURCE3` ; do ln -s ../bin/busybox $f ; done
-cd ../usr/bin
-for f in `cat %SOURCE4` ; do ln -s ../../bin/busybox $f ; done
-cd ../../usr/sbin
-for f in `cat %SOURCE5` ; do ln -s ../../bin/busybox $f ; done
-popd
-
-# install systemd service files for syslogd and klogd
-mkdir -p %{buildroot}%{_libdir}/systemd/system
-mkdir -p %{buildroot}%{_libdir}/systemd/system/basic.target.wants
-install -m 644 %SOURCE201 %{buildroot}%{_libdir}/systemd/system/klogd.service
-ln -s ../klogd.service %{buildroot}%{_libdir}/systemd/system/basic.target.wants/klogd.service
-install -m 644 %SOURCE202 %{buildroot}%{_libdir}/systemd/system/syslogd.service
-ln -s ../syslogd.service %{buildroot}%{_libdir}/systemd/system/basic.target.wants/syslogd.service
-
-rm -rf $RPM_BUILD_ROOT/sbin/syslogd
-cp -f $RPM_BUILD_ROOT/bin/busybox $RPM_BUILD_ROOT/sbin/syslogd
-
-mkdir -p $RPM_BUILD_ROOT%{_datadir}/license
-for keyword in LICENSE COPYING COPYRIGHT;
-do
-       for file in `find %{_builddir} -name $keyword`;
-       do
-               cat $file >> $RPM_BUILD_ROOT%{_datadir}/license/%{name};
-               echo "";
-       done;
-done
+install -d %{buildroot}%{_prefix}/bin
+install -d %{buildroot}%{_datadir}/busybox
+install busybox.links %{buildroot}%{_datadir}/busybox
+install applets/install.sh %{buildroot}%{_bindir}/busybox.install
+install busybox %{buildroot}%{_prefix}/bin
 
 %files
-%defattr(-,root,root,-)
-%manifest %{name}.manifest
-%doc LICENSE
-%{_datadir}/license/%{name}
-%exclude /bin/mktemp
-/bin/busybox
-%exclude /bin/ash
-%exclude /bin/cat
-%exclude /bin/chgrp
-%exclude /bin/chmod
-%exclude /bin/chown
-%exclude /bin/cp
-%exclude /bin/date
-%exclude /bin/dd
-%exclude /bin/df
-%exclude /bin/dmesg
-%exclude /bin/dnsdomainname
-%exclude /bin/echo
-%exclude /bin/egrep
-%exclude /bin/false
-%exclude /bin/fgrep
-%exclude /bin/grep
-%exclude /bin/gunzip
-%exclude /bin/gzip
-%exclude /bin/hostname
-%exclude /bin/ln
-%exclude /bin/ls
-%exclude /bin/mkdir
-%exclude /bin/mknod
-%exclude /bin/more
-%exclude /bin/mount
-%exclude /bin/mv
-%exclude /bin/pwd
-%exclude /bin/readlink
-%exclude /bin/rm
-%exclude /bin/rmdir
-%exclude /bin/sed
-#/bin/sh
-%exclude /bin/sleep
-%exclude /bin/stty
-%exclude /bin/sync
-%exclude /bin/tar
-%exclude /bin/touch
-%exclude /bin/true
-%exclude /bin/umount
-%exclude /bin/uname
-%exclude /bin/uncompress
-%exclude /bin/zcat
-%exclude /sbin/blkid
-%exclude /sbin/blockdev
-%exclude /sbin/fdisk
-%exclude /sbin/fsck.minix
-%exclude /sbin/getty
-%exclude /sbin/hwclock
-%exclude /sbin/losetup
-%exclude /sbin/mkfs.minix
-%exclude /sbin/mkswap
-%exclude /sbin/pivot_root
-%exclude /sbin/swapoff
-%exclude /sbin/swapon
-%exclude /sbin/switch_root
-%exclude /usr/bin/[
-%exclude /usr/bin/basename
-%exclude /usr/bin/chrt
-%exclude /usr/bin/cksum
-%exclude /usr/bin/cmp
-%exclude /usr/bin/comm
-%exclude /usr/bin/cut
-%exclude /usr/bin/diff
-%exclude /usr/bin/dirname
-%exclude /usr/bin/du
-%exclude /usr/bin/env
-%exclude /usr/bin/expand
-%exclude /usr/bin/expr
-%exclude /usr/bin/fdformat
-%exclude /usr/bin/find
-%exclude /usr/bin/flock
-%exclude /usr/bin/fold
-%exclude /usr/bin/getopt
-%exclude /usr/bin/head
-%exclude /usr/bin/hostid
-%exclude /usr/bin/id
-%exclude /usr/bin/install
-%exclude /usr/bin/ionice
-%exclude /usr/bin/ipcrm
-%exclude /usr/bin/ipcs
-%exclude /usr/bin/less
-%exclude /usr/bin/linux32
-%exclude /usr/bin/linux64
-%exclude /usr/bin/logger
-%exclude /usr/bin/logname
-%exclude /usr/bin/md5sum
-%exclude /usr/bin/mkfifo
-%exclude /usr/bin/nice
-%exclude /usr/bin/nohup
-%exclude /usr/bin/od
-%exclude /usr/bin/printenv
-%exclude /usr/bin/printf
-%exclude /usr/bin/renice
-%exclude /usr/bin/rev
-%exclude /usr/bin/rtcwake
-%exclude /usr/bin/script
-%exclude /usr/bin/scriptreplay
-%exclude /usr/bin/seq
-%exclude /usr/bin/setarch
-%exclude /usr/bin/setsid
-%exclude /usr/bin/sha1sum
-%exclude /usr/bin/sha256sum
-%exclude /usr/bin/sha512sum
-%exclude /usr/bin/sort
-%exclude /usr/bin/split
-%exclude /usr/bin/stat
-%exclude /usr/bin/sum
-%exclude /usr/bin/tac
-%exclude /usr/bin/tail
-%exclude /usr/bin/taskset
-%exclude /usr/bin/tee
-%exclude /usr/bin/test
-%exclude /usr/bin/timeout
-%exclude /usr/bin/tr
-%exclude /usr/bin/tty
-%exclude /usr/bin/unexpand
-%exclude /usr/bin/uniq
-%exclude /usr/bin/wall
-%exclude /usr/bin/wc
-%exclude /usr/bin/who
-%exclude /usr/bin/whoami
-/usr/bin/vi
-%exclude /usr/bin/xargs
-%exclude /usr/bin/yes
-%exclude /usr/sbin/chroot
-%exclude /usr/sbin/rdev
-%exclude /usr/sbin/readprofile
-
-%files docs
-%doc LICENSE docs/busybox.net/*.html
-
-%files symlinks-adduser
-%exclude /usr/sbin/addgroup
-%exclude /usr/sbin/adduser
-%exclude /usr/sbin/delgroup
-%exclude /usr/sbin/deluser
-
-%files symlinks-adjtimex
-%exclude /usr/bin/adjtimex
-
-%files symlinks-binutils
-%exclude /usr/bin/ar
-%exclude /usr/bin/strings
-
-%files symlinks-bridge-utils
-%exclude /usr/bin/brctl
-
-%files symlinks-bsdmainutils
-%exclude /usr/bin/cal
-%exclude /usr/bin/hd
-%exclude /usr/bin/hexdump
-
-%files symlinks-busybox
-%manifest %{name}.manifest
-%exclude /usr/bin/[[
-%exclude /usr/bin/catv
-%exclude /usr/sbin/crond
-%exclude /usr/sbin/dhcprelay
-%exclude /usr/sbin/dnsd
-%exclude /bin/dumpkmap
-%exclude /usr/bin/ether-wake
-%exclude /usr/sbin/fakeidentd
-%exclude /sbin/fbsplash
-%exclude /bin/fsync
-%exclude /usr/bin/ftpget
-%exclude /usr/bin/ftpput
-%exclude /usr/sbin/httpd
-%exclude /sbin/ifenslave
-%exclude /sbin/inotifyd
-%exclude /bin/ipaddr
-%exclude /bin/iplink
-%exclude /bin/iproute
-%exclude /bin/iprule
-%exclude /usr/bin/length
-%exclude /usr/bin/loadfont
-%exclude /sbin/loadkmap
-%exclude /sbin/logread
-%exclude /sbin/makedevs
-%exclude /sbin/mdev
-%exclude /usr/bin/microcom
-%exclude /usr/bin/nmeter
-%exclude /usr/bin/pscan
-%exclude /sbin/raidautorun
-%exclude /usr/bin/readahead
-%exclude /sbin/setconsole
-%exclude /usr/sbin/tftpd
-%exclude /usr/bin/ttysize
-%exclude /bin/usleep
-%exclude /usr/bin/volname
-
-%files symlinks-bzip2
-%exclude /bin/bunzip2
-%exclude /bin/bzcat
-%exclude /bin/bzip2
-
-%files symlinks-console-tools
-%exclude /usr/bin/chvt
-%exclude /usr/bin/deallocvt
-%exclude /bin/fgconsole
-%exclude /usr/bin/kbd_mode
-%exclude /usr/bin/openvt
-%exclude /usr/bin/setkeycodes
-%exclude /usr/bin/setlogcons
-%exclude /usr/bin/showkey
-
-%files symlinks-cpio
-%exclude /bin/cpio
-
-%files symlinks-cron
-%exclude /usr/bin/crontab
-
-%files symlinks-daemontools
-%exclude /usr/bin/envdir
-%exclude /usr/bin/envuidgid
-%exclude /usr/bin/setuidgid
-%exclude /usr/bin/softlimit
-
-%files symlinks-dc
-%exclude /usr/bin/dc
-
-%files symlinks-dnsutils
-%manifest %{name}.manifest
-%exclude /usr/bin/nslookup
-
-%files symlinks-dosfstools
-%exclude /sbin/mkdosfs
-%exclude /sbin/mkfs.vfat
-
-%files symlinks-ed
-%exclude /bin/ed
-
-%files symlinks-eject
-%exclude /usr/bin/eject
-
-%files symlinks-fbset
-%exclude /bin/fbset
-
-%files symlinks-fdflush
-%exclude /bin/fdflush
-
-%files symlinks-hdparm
-%exclude /sbin/hdparm
-
-%files symlinks-ifupdown
-%manifest %{name}.manifest
-%exclude /sbin/ifdown
-%exclude /sbin/ifup
-
-%files symlinks-initscripts
-%exclude /bin/mountpoint
-
-%files symlinks-ipcalc
-%exclude /usr/bin/ipcalc
-
-%files symlinks-iproute
-%exclude /bin/ip
-%exclude /sbin/ip
-
-%files symlinks-ipsvd
-%exclude /usr/bin/tcpsvd
-%exclude /usr/bin/udpsvd
-
-%files symlinks-iputils-arping
-%exclude /usr/bin/arping
-
-%files symlinks-iputils-ping
 %manifest %{name}.manifest
-%exclude /bin/ping
-%exclude /bin/ping6
-
-%files symlinks-klogd
-%manifest %{name}.manifest
-%exclude /sbin/klogd
-%exclude %{_libdir}/systemd/system/klogd.service
-%exclude %{_libdir}/systemd/system/basic.target.wants/klogd.service
-
-%files symlinks-loadlin
-%exclude /usr/bin/freeramdisk
-
-%files symlinks-lrzsz
-%exclude /usr/bin/rx
-
-%files symlinks-lzma
-%exclude /usr/bin/lzcat
-%exclude /usr/bin/lzma
-%exclude /usr/bin/unlzma
-
-%files symlinks-lzop
-%exclude /usr/bin/lzop
-%exclude /usr/bin/lzopcat
-%exclude /usr/bin/unlzop
-
-%files symlinks-module-init-tools
-%exclude /sbin/depmod
-%exclude /sbin/insmod
-%exclude /sbin/lsmod
-%exclude /sbin/modinfo
-%exclude /sbin/modprobe
-%exclude /sbin/rmmod
-
-%files symlinks-mtd-utils
-%exclude /usr/sbin/flash_eraseall
-%exclude /usr/sbin/flash_lock
-%exclude /usr/sbin/flash_unlock
-%exclude /usr/sbin/flashcp
-%exclude /usr/sbin/ubiattach
-%exclude /usr/sbin/ubidetach
-
-%files symlinks-net-tools
-%exclude /usr/sbin/arp
-%exclude /sbin/ifconfig
-%exclude /sbin/iptunnel
-%exclude /sbin/nameif
-%exclude /bin/netstat
-%exclude /sbin/route
-%exclude /sbin/slattach
-
-%files symlinks-openbsd-inetd
-%exclude /usr/sbin/inetd
-
-%files symlinks-passwd
-%exclude /usr/sbin/chpasswd
-%exclude /usr/bin/passwd
-
-%files symlinks-patch
-%exclude /usr/bin/patch
-
-%files symlinks-ppp
-%exclude /usr/sbin/chat
-
-%files symlinks-procps
-%exclude /usr/bin/free
-%exclude /bin/kill
-%exclude /usr/bin/pgrep
-%exclude /usr/bin/pkill
-%exclude /bin/ps
-%exclude /sbin/sysctl
-%exclude /usr/bin/top
-%exclude /usr/bin/uptime
-%exclude /usr/bin/watch
-
-%files symlinks-psmisc
-%exclude /bin/fuser
-%exclude /usr/bin/killall
-
-%files symlinks-rdate
-%exclude /usr/sbin/rdate
-
-%files symlinks-realpath
-%exclude /usr/bin/realpath
-
-#%files symlinks-rpm
-#/usr/bin/rpm
-#/usr/bin/rpm2cpio
-
-%files symlinks-runit
-%exclude /usr/bin/chpst
-%exclude /usr/bin/runsv
-%exclude /usr/bin/runsvdir
-%exclude /usr/bin/sv
-%exclude /usr/bin/svlogd
-
-%files symlinks-sharutils
-%exclude /usr/bin/uudecode
-%exclude /usr/bin/uuencode
-
-%files symlinks-ssmtp
-%exclude /usr/sbin/sendmail
-
-%files symlinks-sysklogd
-%exclude /sbin/syslogd
-%exclude %{_libdir}/systemd/system/syslogd.service
-%exclude %{_libdir}/systemd/system/basic.target.wants/syslogd.service
-
-%files symlinks-telnetd
-%exclude /usr/sbin/telnetd
-
-%files symlinks-tftp
-%exclude /usr/bin/tftp
-
-%files symlinks-time
-%exclude /usr/bin/time
-
-%files symlinks-tofrodos
-%exclude /usr/bin/dos2unix
-%exclude /usr/bin/unix2dos
-
-%files symlinks-udhcpc
-%exclude /usr/bin/udhcpc
-
-%files symlinks-udhcpd
-%exclude /usr/bin/dumpleases
-%exclude /usr/sbin/udhcpd
-
-%files symlinks-unzip
-%exclude /usr/bin/unzip
-
-%files symlinks-vlan
-%exclude /sbin/vconfig
-
-%files symlinks-vlock
-%exclude /usr/bin/vlock
-
-%files symlinks-watchdog
-%exclude /usr/sbin/watchdog
-
-%files symlinks-wget
-%exclude /usr/bin/wget
-
-%files symlinks-xterm
-%exclude /usr/bin/resize
-
-%files symlinks-zcip
-%exclude /usr/bin/zcip
+%defattr(-,root,root)
+%license LICENSE
+%{_bindir}/busybox
+%{_bindir}/busybox.install
+%config %{_datadir}/busybox/busybox.links
similarity index 63%
rename from debian/config/pkg/static
rename to packaging/busybox.tizen.config
index 45d0b90..aea41b0 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Busybox version: 1.17.1
-# Tue Nov  9 10:36:06 2010
+# Busybox version: 1.20.2
+# Tue Aug  6 16:59:06 2013
 #
 CONFIG_HAVE_DOT_CONFIG=y
 
@@ -13,24 +13,26 @@ CONFIG_HAVE_DOT_CONFIG=y
 # General Configuration
 #
 CONFIG_DESKTOP=y
-CONFIG_EXTRA_COMPAT=y
+# CONFIG_EXTRA_COMPAT is not set
 CONFIG_INCLUDE_SUSv2=y
 # CONFIG_USE_PORTABLE_CODE is not set
+CONFIG_PLATFORM_LINUX=y
 CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
 # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
 # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
 CONFIG_SHOW_USAGE=y
 CONFIG_FEATURE_VERBOSE_USAGE=y
 CONFIG_FEATURE_COMPRESS_USAGE=y
-CONFIG_FEATURE_INSTALLER=y
+# CONFIG_FEATURE_INSTALLER is not set
+# CONFIG_INSTALL_NO_USR is not set
 # CONFIG_LOCALE_SUPPORT is not set
 CONFIG_UNICODE_SUPPORT=y
 # CONFIG_UNICODE_USING_LOCALE is not set
-CONFIG_FEATURE_CHECK_UNICODE_IN_ENV=y
+# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
 CONFIG_SUBST_WCHAR=63
 CONFIG_LAST_SUPPORTED_WCHAR=767
-CONFIG_UNICODE_COMBINING_WCHARS=y
-CONFIG_UNICODE_WIDE_WCHARS=y
+# CONFIG_UNICODE_COMBINING_WCHARS is not set
+# CONFIG_UNICODE_WIDE_WCHARS is not set
 # CONFIG_UNICODE_BIDI_SUPPORT is not set
 # CONFIG_UNICODE_NEUTRAL_TABLE is not set
 # CONFIG_UNICODE_PRESERVE_BROKEN is not set
@@ -41,18 +43,18 @@ CONFIG_FEATURE_UTMP=y
 CONFIG_FEATURE_WTMP=y
 CONFIG_FEATURE_PIDFILE=y
 CONFIG_FEATURE_SUID=y
-CONFIG_FEATURE_SUID_CONFIG=y
-CONFIG_FEATURE_SUID_CONFIG_QUIET=y
+# CONFIG_FEATURE_SUID_CONFIG is not set
+# CONFIG_FEATURE_SUID_CONFIG_QUIET is not set
 # CONFIG_SELINUX is not set
-CONFIG_FEATURE_PREFER_APPLETS=y
-CONFIG_BUSYBOX_EXEC_PATH="/bin/busybox"
+# CONFIG_FEATURE_PREFER_APPLETS is not set
+CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
 CONFIG_FEATURE_SYSLOG=y
-# CONFIG_FEATURE_HAVE_RPC is not set
+CONFIG_FEATURE_HAVE_RPC=y
 
 #
 # Build Options
 #
-CONFIG_STATIC=y
+# CONFIG_STATIC is not set
 # CONFIG_PIE is not set
 # CONFIG_NOMMU is not set
 # CONFIG_BUILD_LIBBUSYBOX is not set
@@ -60,7 +62,10 @@ CONFIG_STATIC=y
 # CONFIG_FEATURE_SHARED_BUSYBOX is not set
 CONFIG_LFS=y
 CONFIG_CROSS_COMPILER_PREFIX=""
+CONFIG_SYSROOT=""
 CONFIG_EXTRA_CFLAGS=""
+CONFIG_EXTRA_LDFLAGS=""
+CONFIG_EXTRA_LDLIBS=""
 
 #
 # Debugging Options
@@ -73,37 +78,42 @@ CONFIG_NO_DEBUG_LIB=y
 # CONFIG_EFENCE is not set
 
 #
-# Installation Options
+# Installation Options ("make install" behavior)
 #
-# CONFIG_INSTALL_NO_USR is not set
-# CONFIG_INSTALL_APPLET_SYMLINKS is not set
+CONFIG_INSTALL_APPLET_SYMLINKS=y
 # CONFIG_INSTALL_APPLET_HARDLINKS is not set
 # CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
-CONFIG_INSTALL_APPLET_DONT=y
+# CONFIG_INSTALL_APPLET_DONT is not set
 # CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
 # CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
 # CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
-CONFIG_PREFIX="./_install"
+CONFIG_PREFIX="/usr"
 
 #
 # Busybox Library Tuning
 #
+CONFIG_FEATURE_SYSTEMD=y
+CONFIG_FEATURE_RTMINMAX=y
 CONFIG_PASSWORD_MINLEN=6
-CONFIG_MD5_SIZE_VS_SPEED=1
-# CONFIG_FEATURE_FAST_TOP is not set
+CONFIG_MD5_SMALL=1
+CONFIG_FEATURE_FAST_TOP=y
 # CONFIG_FEATURE_ETC_NETWORKS is not set
+CONFIG_FEATURE_USE_TERMIOS=y
 CONFIG_FEATURE_EDITING=y
 CONFIG_FEATURE_EDITING_MAX_LEN=1024
 # CONFIG_FEATURE_EDITING_VI is not set
-CONFIG_FEATURE_EDITING_HISTORY=15
-# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set
+CONFIG_FEATURE_EDITING_HISTORY=666
+CONFIG_FEATURE_EDITING_SAVEHISTORY=y
+# CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set
+CONFIG_FEATURE_REVERSE_SEARCH=y
 CONFIG_FEATURE_TAB_COMPLETION=y
-CONFIG_FEATURE_USERNAME_COMPLETION=y
-CONFIG_FEATURE_EDITING_FANCY_PROMPT=y
-CONFIG_FEATURE_EDITING_ASK_TERMINAL=y
-# CONFIG_FEATURE_NON_POSIX_CP is not set
-CONFIG_FEATURE_VERBOSE_CP_MESSAGE=y
+# CONFIG_FEATURE_USERNAME_COMPLETION is not set
+# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set
+# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
+CONFIG_FEATURE_NON_POSIX_CP=y
+# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
 CONFIG_FEATURE_COPYBUF_KB=4
+CONFIG_FEATURE_SKIP_ROOTFS=y
 CONFIG_MONOTONIC_SYSCALL=y
 CONFIG_IOCTL_HEX2STR_ERROR=y
 CONFIG_FEATURE_HWIB=y
@@ -115,47 +125,48 @@ CONFIG_FEATURE_HWIB=y
 #
 # Archival Utilities
 #
-# CONFIG_FEATURE_SEAMLESS_XZ is not set
-CONFIG_FEATURE_SEAMLESS_LZMA=y
-CONFIG_FEATURE_SEAMLESS_BZ2=y
-CONFIG_FEATURE_SEAMLESS_GZ=y
-CONFIG_FEATURE_SEAMLESS_Z=y
+CONFIG_FEATURE_SEAMLESS_XZ=y
+# CONFIG_FEATURE_SEAMLESS_LZMA is not set
+# CONFIG_FEATURE_SEAMLESS_BZ2 is not set
+# CONFIG_FEATURE_SEAMLESS_GZ is not set
+# CONFIG_FEATURE_SEAMLESS_Z is not set
 CONFIG_AR=y
 CONFIG_FEATURE_AR_LONG_FILENAMES=y
-# CONFIG_FEATURE_AR_CREATE is not set
+CONFIG_FEATURE_AR_CREATE=y
 CONFIG_BUNZIP2=y
 CONFIG_BZIP2=y
 CONFIG_CPIO=y
-CONFIG_FEATURE_CPIO_O=y
+# CONFIG_FEATURE_CPIO_O is not set
 # CONFIG_FEATURE_CPIO_P is not set
-CONFIG_DPKG=y
-CONFIG_DPKG_DEB=y
+# CONFIG_DPKG is not set
+# CONFIG_DPKG_DEB is not set
 # CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
 CONFIG_GUNZIP=y
 CONFIG_GZIP=y
 CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
-# CONFIG_LZOP is not set
+CONFIG_GZIP_FAST=0
+CONFIG_LZOP=y
 # CONFIG_LZOP_COMPR_HIGH is not set
 CONFIG_RPM2CPIO=y
 CONFIG_RPM=y
 CONFIG_TAR=y
 CONFIG_FEATURE_TAR_CREATE=y
 # CONFIG_FEATURE_TAR_AUTODETECT is not set
-# CONFIG_FEATURE_TAR_FROM is not set
+CONFIG_FEATURE_TAR_FROM=y
 # CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY is not set
 # CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set
 CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
 CONFIG_FEATURE_TAR_LONG_OPTIONS=y
-# CONFIG_FEATURE_TAR_TO_COMMAND is not set
-CONFIG_FEATURE_TAR_UNAME_GNAME=y
-# CONFIG_FEATURE_TAR_NOPRESERVE_TIME is not set
+CONFIG_FEATURE_TAR_TO_COMMAND=y
+# CONFIG_FEATURE_TAR_UNAME_GNAME is not set
+CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
 # CONFIG_FEATURE_TAR_SELINUX is not set
 CONFIG_UNCOMPRESS=y
 CONFIG_UNLZMA=y
 # CONFIG_FEATURE_LZMA_FAST is not set
-# CONFIG_LZMA is not set
-# CONFIG_UNXZ is not set
-# CONFIG_XZ is not set
+CONFIG_LZMA=y
+CONFIG_UNXZ=y
+CONFIG_XZ=y
 CONFIG_UNZIP=y
 
 #
@@ -167,27 +178,35 @@ CONFIG_DATE=y
 CONFIG_FEATURE_DATE_ISOFMT=y
 # CONFIG_FEATURE_DATE_NANO is not set
 CONFIG_FEATURE_DATE_COMPAT=y
+CONFIG_HOSTID=y
+CONFIG_ID=y
+CONFIG_GROUPS=y
 CONFIG_TEST=y
 CONFIG_FEATURE_TEST_64=y
+CONFIG_TOUCH=y
+CONFIG_FEATURE_TOUCH_SUSV3=y
 CONFIG_TR=y
-# CONFIG_FEATURE_TR_CLASSES is not set
-# CONFIG_FEATURE_TR_EQUIV is not set
+CONFIG_FEATURE_TR_CLASSES=y
+CONFIG_FEATURE_TR_EQUIV=y
+CONFIG_BASE64=y
+CONFIG_WHO=y
+CONFIG_USERS=y
 CONFIG_CAL=y
-# CONFIG_CATV is not set
+CONFIG_CATV=y
 CONFIG_CHGRP=y
 CONFIG_CHMOD=y
 CONFIG_CHOWN=y
 CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y
 CONFIG_CHROOT=y
-# CONFIG_CKSUM is not set
-# CONFIG_COMM is not set
+CONFIG_CKSUM=y
+CONFIG_COMM=y
 CONFIG_CP=y
 CONFIG_FEATURE_CP_LONG_OPTIONS=y
 CONFIG_CUT=y
 CONFIG_DD=y
 CONFIG_FEATURE_DD_SIGNAL_HANDLING=y
-# CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set
-# CONFIG_FEATURE_DD_IBS_OBS is not set
+CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y
+CONFIG_FEATURE_DD_IBS_OBS=y
 CONFIG_DF=y
 CONFIG_FEATURE_DF_FANCY=y
 CONFIG_DIRNAME=y
@@ -199,20 +218,17 @@ CONFIG_ECHO=y
 CONFIG_FEATURE_FANCY_ECHO=y
 CONFIG_ENV=y
 CONFIG_FEATURE_ENV_LONG_OPTIONS=y
-CONFIG_EXPAND=y
-CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y
+# CONFIG_EXPAND is not set
+# CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set
 CONFIG_EXPR=y
 CONFIG_EXPR_MATH_SUPPORT_64=y
 CONFIG_FALSE=y
 CONFIG_FOLD=y
-# CONFIG_FSYNC is not set
+CONFIG_FSYNC=y
 CONFIG_HEAD=y
 CONFIG_FEATURE_FANCY_HEAD=y
-CONFIG_HOSTID=y
-CONFIG_ID=y
-# CONFIG_INSTALL is not set
-# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set
-CONFIG_LENGTH=y
+CONFIG_INSTALL=y
+CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y
 CONFIG_LN=y
 CONFIG_LOGNAME=y
 CONFIG_LS=y
@@ -222,7 +238,7 @@ CONFIG_FEATURE_LS_RECURSIVE=y
 CONFIG_FEATURE_LS_SORTFILES=y
 CONFIG_FEATURE_LS_TIMESTAMPS=y
 CONFIG_FEATURE_LS_USERNAME=y
-CONFIG_FEATURE_LS_COLOR=y
+# CONFIG_FEATURE_LS_COLOR is not set
 # CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set
 CONFIG_MD5SUM=y
 CONFIG_MKDIR=y
@@ -231,59 +247,57 @@ CONFIG_MKFIFO=y
 CONFIG_MKNOD=y
 CONFIG_MV=y
 CONFIG_FEATURE_MV_LONG_OPTIONS=y
-# CONFIG_NICE is not set
-# CONFIG_NOHUP is not set
+CONFIG_NICE=y
+CONFIG_NOHUP=y
 CONFIG_OD=y
-# CONFIG_PRINTENV is not set
+CONFIG_PRINTENV=y
 CONFIG_PRINTF=y
 CONFIG_PWD=y
 CONFIG_READLINK=y
-CONFIG_FEATURE_READLINK_FOLLOW=y
+# CONFIG_FEATURE_READLINK_FOLLOW is not set
 CONFIG_REALPATH=y
 CONFIG_RM=y
 CONFIG_RMDIR=y
-CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y
-# CONFIG_SEQ is not set
+# CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set
+CONFIG_SEQ=y
 CONFIG_SHA1SUM=y
 CONFIG_SHA256SUM=y
 CONFIG_SHA512SUM=y
 CONFIG_SLEEP=y
-CONFIG_FEATURE_FANCY_SLEEP=y
+# CONFIG_FEATURE_FANCY_SLEEP is not set
 # CONFIG_FEATURE_FLOAT_SLEEP is not set
 CONFIG_SORT=y
 CONFIG_FEATURE_SORT_BIG=y
-# CONFIG_SPLIT is not set
-# CONFIG_FEATURE_SPLIT_FANCY is not set
-# CONFIG_STAT is not set
-# CONFIG_FEATURE_STAT_FORMAT is not set
+CONFIG_SPLIT=y
+CONFIG_FEATURE_SPLIT_FANCY=y
+CONFIG_STAT=y
+CONFIG_FEATURE_STAT_FORMAT=y
 CONFIG_STTY=y
-# CONFIG_SUM is not set
+CONFIG_SUM=y
 CONFIG_SYNC=y
-CONFIG_TAC=y
+# CONFIG_TAC is not set
 CONFIG_TAIL=y
-CONFIG_FEATURE_FANCY_TAIL=y
+# CONFIG_FEATURE_FANCY_TAIL is not set
 CONFIG_TEE=y
-CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
-CONFIG_TOUCH=y
+# CONFIG_FEATURE_TEE_USE_BLOCK_IO is not set
 CONFIG_TRUE=y
 CONFIG_TTY=y
 CONFIG_UNAME=y
-CONFIG_UNEXPAND=y
-CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y
+# CONFIG_UNEXPAND is not set
+# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set
 CONFIG_UNIQ=y
 CONFIG_USLEEP=y
 CONFIG_UUDECODE=y
 CONFIG_UUENCODE=y
 CONFIG_WC=y
-# CONFIG_FEATURE_WC_LARGE is not set
-CONFIG_WHO=y
+CONFIG_FEATURE_WC_LARGE=y
 CONFIG_WHOAMI=y
 CONFIG_YES=y
 
 #
 # Common options for cp and mv
 #
-CONFIG_FEATURE_PRESERVE_HARDLINKS=y
+# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set
 
 #
 # Common options for ls, more and telnet
@@ -298,13 +312,13 @@ CONFIG_FEATURE_HUMAN_READABLE=y
 #
 # Common options for md5sum, sha1sum, sha256sum, sha512sum
 #
-# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set
+CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
 
 #
 # Console Utilities
 #
 CONFIG_CHVT=y
-# CONFIG_FGCONSOLE is not set
+CONFIG_FGCONSOLE=y
 CONFIG_CLEAR=y
 CONFIG_DEALLOCVT=y
 CONFIG_DUMPKMAP=y
@@ -313,22 +327,22 @@ CONFIG_LOADFONT=y
 CONFIG_LOADKMAP=y
 CONFIG_OPENVT=y
 CONFIG_RESET=y
-# CONFIG_RESIZE is not set
-# CONFIG_FEATURE_RESIZE_PRINT is not set
-# CONFIG_SETCONSOLE is not set
+CONFIG_RESIZE=y
+CONFIG_FEATURE_RESIZE_PRINT=y
+CONFIG_SETCONSOLE=y
 # CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
-# CONFIG_SETFONT is not set
-# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
+CONFIG_SETFONT=y
+CONFIG_FEATURE_SETFONT_TEXTUAL_MAP=y
 CONFIG_DEFAULT_SETFONT_DIR=""
 CONFIG_SETKEYCODES=y
-# CONFIG_SETLOGCONS is not set
-# CONFIG_SHOWKEY is not set
+CONFIG_SETLOGCONS=y
+CONFIG_SHOWKEY=y
 
 #
 # Common options for loadfont and setfont
 #
-# CONFIG_FEATURE_LOADFONT_PSF2 is not set
-# CONFIG_FEATURE_LOADFONT_RAW is not set
+CONFIG_FEATURE_LOADFONT_PSF2=y
+CONFIG_FEATURE_LOADFONT_RAW=y
 
 #
 # Debian Utilities
@@ -337,38 +351,39 @@ CONFIG_MKTEMP=y
 # CONFIG_PIPE_PROGRESS is not set
 CONFIG_RUN_PARTS=y
 CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y
-# CONFIG_FEATURE_RUN_PARTS_FANCY is not set
-CONFIG_START_STOP_DAEMON=y
-CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y
-CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS=y
+CONFIG_FEATURE_RUN_PARTS_FANCY=y
+# CONFIG_START_STOP_DAEMON is not set
+# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set
+# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set
 CONFIG_WHICH=y
 
 #
 # Editors
 #
-CONFIG_AWK=y
-CONFIG_FEATURE_AWK_LIBM=y
-CONFIG_CMP=y
-# CONFIG_DIFF is not set
-# CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set
-# CONFIG_FEATURE_DIFF_DIR is not set
-CONFIG_ED=y
 CONFIG_PATCH=y
-CONFIG_SED=y
 CONFIG_VI=y
 CONFIG_FEATURE_VI_MAX_LEN=1024
 CONFIG_FEATURE_VI_8BIT=y
 CONFIG_FEATURE_VI_COLON=y
 CONFIG_FEATURE_VI_YANKMARK=y
 CONFIG_FEATURE_VI_SEARCH=y
+# CONFIG_FEATURE_VI_REGEX_SEARCH is not set
 CONFIG_FEATURE_VI_USE_SIGNALS=y
 CONFIG_FEATURE_VI_DOT_CMD=y
 CONFIG_FEATURE_VI_READONLY=y
 CONFIG_FEATURE_VI_SETOPTS=y
 CONFIG_FEATURE_VI_SET=y
 CONFIG_FEATURE_VI_WIN_RESIZE=y
-# CONFIG_FEATURE_VI_ASK_TERMINAL is not set
+CONFIG_FEATURE_VI_ASK_TERMINAL=y
 CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y
+CONFIG_AWK=y
+# CONFIG_FEATURE_AWK_LIBM is not set
+CONFIG_CMP=y
+CONFIG_DIFF=y
+CONFIG_FEATURE_DIFF_LONG_OPTIONS=y
+CONFIG_FEATURE_DIFF_DIR=y
+CONFIG_ED=y
+CONFIG_SED=y
 CONFIG_FEATURE_ALLOW_EXEC=y
 
 #
@@ -402,61 +417,66 @@ CONFIG_FEATURE_GREP_EGREP_ALIAS=y
 CONFIG_FEATURE_GREP_FGREP_ALIAS=y
 CONFIG_FEATURE_GREP_CONTEXT=y
 CONFIG_XARGS=y
-# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set
-# CONFIG_FEATURE_XARGS_SUPPORT_QUOTES is not set
-# CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT is not set
-# CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM is not set
+CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
+CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
+CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
+CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
 
 #
 # Init Utilities
 #
-# CONFIG_BOOTCHARTD is not set
-# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set
-# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set
+CONFIG_BOOTCHARTD=y
+CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER=y
+CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE=y
+CONFIG_HALT=y
+# CONFIG_FEATURE_CALL_TELINIT is not set
+CONFIG_TELINIT_PATH=""
 CONFIG_INIT=y
 CONFIG_FEATURE_USE_INITTAB=y
-# CONFIG_FEATURE_KILL_REMOVED is not set
+CONFIG_FEATURE_KILL_REMOVED=y
 CONFIG_FEATURE_KILL_DELAY=0
 CONFIG_FEATURE_INIT_SCTTY=y
-# CONFIG_FEATURE_INIT_SYSLOG is not set
-# CONFIG_FEATURE_EXTRA_QUIET is not set
-# CONFIG_FEATURE_INIT_COREDUMPS is not set
+CONFIG_FEATURE_INIT_SYSLOG=y
+CONFIG_FEATURE_EXTRA_QUIET=y
+CONFIG_FEATURE_INIT_COREDUMPS=y
 CONFIG_FEATURE_INITRD=y
 CONFIG_INIT_TERMINAL_TYPE="linux"
-CONFIG_HALT=y
-# CONFIG_FEATURE_CALL_TELINIT is not set
-CONFIG_TELINIT_PATH=""
 CONFIG_MESG=y
+CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y
 
 #
 # Login/Password Management Utilities
 #
+CONFIG_ADD_SHELL=y
+CONFIG_REMOVE_SHELL=y
 CONFIG_FEATURE_SHADOWPASSWDS=y
 CONFIG_USE_BB_PWD_GRP=y
 CONFIG_USE_BB_SHADOW=y
 CONFIG_USE_BB_CRYPT=y
 CONFIG_USE_BB_CRYPT_SHA=y
-CONFIG_ADDGROUP=y
-# CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set
-# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
-CONFIG_DELGROUP=y
-# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
-# CONFIG_FEATURE_CHECK_NAMES is not set
 CONFIG_ADDUSER=y
-CONFIG_FEATURE_ADDUSER_LONG_OPTIONS=y
+# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set
+# CONFIG_FEATURE_CHECK_NAMES is not set
 CONFIG_FIRST_SYSTEM_ID=100
 CONFIG_LAST_SYSTEM_ID=999
+CONFIG_ADDGROUP=y
+CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y
+# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
 CONFIG_DELUSER=y
+CONFIG_DELGROUP=y
+# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
 CONFIG_GETTY=y
 CONFIG_LOGIN=y
+# CONFIG_LOGIN_SESSION_AS_CHILD is not set
 # CONFIG_PAM is not set
 # CONFIG_LOGIN_SCRIPTS is not set
-CONFIG_FEATURE_NOLOGIN=y
+# CONFIG_FEATURE_NOLOGIN is not set
 CONFIG_FEATURE_SECURETTY=y
 CONFIG_PASSWD=y
-CONFIG_FEATURE_PASSWD_WEAK_CHECK=y
-# CONFIG_CRYPTPW is not set
+# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set
+CONFIG_CRYPTPW=y
 # CONFIG_CHPASSWD is not set
+CONFIG_FEATURE_DEFAULT_PASSWD_ALGO="des"
 CONFIG_SU=y
 CONFIG_FEATURE_SU_SYSLOG=y
 CONFIG_FEATURE_SU_CHECKS_SHELLS=y
@@ -466,23 +486,23 @@ CONFIG_VLOCK=y
 #
 # Linux Ext2 FS Progs
 #
-# CONFIG_CHATTR is not set
-# CONFIG_FSCK is not set
-# CONFIG_LSATTR is not set
+CONFIG_CHATTR=y
+CONFIG_FSCK=y
+CONFIG_LSATTR=y
 # CONFIG_TUNE2FS is not set
 
 #
 # Linux Module Utilities
 #
-# CONFIG_MODINFO is not set
+CONFIG_MODINFO=y
 # CONFIG_MODPROBE_SMALL is not set
 # CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set
 # CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set
-# CONFIG_INSMOD is not set
-# CONFIG_RMMOD is not set
-# CONFIG_LSMOD is not set
-# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set
-# CONFIG_MODPROBE is not set
+CONFIG_INSMOD=y
+CONFIG_RMMOD=y
+CONFIG_LSMOD=y
+CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT=y
+CONFIG_MODPROBE=y
 # CONFIG_FEATURE_MODPROBE_BLACKLIST is not set
 # CONFIG_DEPMOD is not set
 
@@ -496,44 +516,52 @@ CONFIG_VLOCK=y
 # CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
 # CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
 # CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set
-# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
-# CONFIG_FEATURE_MODUTILS_ALIAS is not set
-# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set
-CONFIG_DEFAULT_MODULES_DIR=""
-CONFIG_DEFAULT_DEPMOD_FILE=""
+CONFIG_FEATURE_CHECK_TAINTED_MODULE=y
+CONFIG_FEATURE_MODUTILS_ALIAS=y
+CONFIG_FEATURE_MODUTILS_SYMBOLS=y
+CONFIG_DEFAULT_MODULES_DIR="/lib/modules"
+CONFIG_DEFAULT_DEPMOD_FILE="modules.dep"
 
 #
 # Linux System Utilities
 #
 CONFIG_BLOCKDEV=y
-# CONFIG_REV is not set
+CONFIG_MDEV=y
+CONFIG_FEATURE_MDEV_CONF=y
+# CONFIG_FEATURE_MDEV_RENAME is not set
+# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set
+CONFIG_FEATURE_MDEV_EXEC=y
+CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y
+CONFIG_REV=y
 CONFIG_ACPID=y
-# CONFIG_FEATURE_ACPID_COMPAT is not set
-# CONFIG_BLKID is not set
+CONFIG_FEATURE_ACPID_COMPAT=y
+CONFIG_BLKID=y
+# CONFIG_FEATURE_BLKID_TYPE is not set
 CONFIG_DMESG=y
 CONFIG_FEATURE_DMESG_PRETTY=y
 CONFIG_FBSET=y
-CONFIG_FEATURE_FBSET_FANCY=y
-CONFIG_FEATURE_FBSET_READMODE=y
-CONFIG_FDFLUSH=y
+# CONFIG_FEATURE_FBSET_FANCY is not set
+# CONFIG_FEATURE_FBSET_READMODE is not set
+# CONFIG_FDFLUSH is not set
 # CONFIG_FDFORMAT is not set
 CONFIG_FDISK=y
-CONFIG_FDISK_SUPPORT_LARGE_DISKS=y
+# CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set
 CONFIG_FEATURE_FDISK_WRITABLE=y
-CONFIG_FEATURE_AIX_LABEL=y
-CONFIG_FEATURE_SGI_LABEL=y
-CONFIG_FEATURE_SUN_LABEL=y
-CONFIG_FEATURE_OSF_LABEL=y
-CONFIG_FEATURE_FDISK_ADVANCED=y
+# CONFIG_FEATURE_AIX_LABEL is not set
+# CONFIG_FEATURE_SGI_LABEL is not set
+# CONFIG_FEATURE_SUN_LABEL is not set
+# CONFIG_FEATURE_OSF_LABEL is not set
+# CONFIG_FEATURE_GPT_LABEL is not set
+# CONFIG_FEATURE_FDISK_ADVANCED is not set
 # CONFIG_FINDFS is not set
-# CONFIG_FLOCK is not set
-CONFIG_FREERAMDISK=y
-CONFIG_FSCK_MINIX=y
-# CONFIG_MKFS_EXT2 is not set
-CONFIG_MKFS_MINIX=y
-CONFIG_FEATURE_MINIX2=y
+CONFIG_FLOCK=y
+# CONFIG_FREERAMDISK is not set
+# CONFIG_FSCK_MINIX is not set
+CONFIG_MKFS_EXT2=y
+# CONFIG_MKFS_MINIX is not set
+# CONFIG_FEATURE_MINIX2 is not set
 # CONFIG_MKFS_REISER is not set
-# CONFIG_MKFS_VFAT is not set
+CONFIG_MKFS_VFAT=y
 CONFIG_GETOPT=y
 CONFIG_FEATURE_GETOPT_LONG=y
 CONFIG_HEXDUMP=y
@@ -542,48 +570,37 @@ CONFIG_HEXDUMP=y
 CONFIG_HWCLOCK=y
 CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS=y
 CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS=y
-# CONFIG_IPCRM is not set
-# CONFIG_IPCS is not set
+CONFIG_IPCRM=y
+CONFIG_IPCS=y
 CONFIG_LOSETUP=y
-# CONFIG_LSPCI is not set
-# CONFIG_LSUSB is not set
-CONFIG_MDEV=y
-# CONFIG_FEATURE_MDEV_CONF is not set
-# CONFIG_FEATURE_MDEV_RENAME is not set
-# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set
-# CONFIG_FEATURE_MDEV_EXEC is not set
-# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set
+CONFIG_LSPCI=y
+CONFIG_LSUSB=y
 CONFIG_MKSWAP=y
 CONFIG_FEATURE_MKSWAP_UUID=y
 CONFIG_MORE=y
-CONFIG_FEATURE_USE_TERMIOS=y
-CONFIG_MOUNT=y
+# CONFIG_MOUNT is not set
 # CONFIG_FEATURE_MOUNT_FAKE is not set
 # CONFIG_FEATURE_MOUNT_VERBOSE is not set
-CONFIG_FEATURE_MOUNT_HELPERS=y
-CONFIG_FEATURE_MOUNT_LABEL=y
+# CONFIG_FEATURE_MOUNT_HELPERS is not set
+# CONFIG_FEATURE_MOUNT_LABEL is not set
 # CONFIG_FEATURE_MOUNT_NFS is not set
 # CONFIG_FEATURE_MOUNT_CIFS is not set
-CONFIG_FEATURE_MOUNT_FLAGS=y
-CONFIG_FEATURE_MOUNT_FSTAB=y
-CONFIG_PIVOT_ROOT=y
+# CONFIG_FEATURE_MOUNT_FLAGS is not set
+# CONFIG_FEATURE_MOUNT_FSTAB is not set
+# CONFIG_PIVOT_ROOT is not set
 CONFIG_RDATE=y
 # CONFIG_RDEV is not set
-# CONFIG_READPROFILE is not set
+CONFIG_READPROFILE=y
 # CONFIG_RTCWAKE is not set
 # CONFIG_SCRIPT is not set
-# CONFIG_SCRIPTREPLAY is not set
+CONFIG_SCRIPTREPLAY=y
 # CONFIG_SETARCH is not set
 CONFIG_SWAPONOFF=y
 # CONFIG_FEATURE_SWAPON_PRI is not set
-# CONFIG_SWITCH_ROOT is not set
-CONFIG_UMOUNT=y
-CONFIG_FEATURE_UMOUNT_ALL=y
-
-#
-# Common options for mount/umount
-#
-CONFIG_FEATURE_MOUNT_LOOP=y
+CONFIG_SWITCH_ROOT=y
+# CONFIG_UMOUNT is not set
+# CONFIG_FEATURE_UMOUNT_ALL is not set
+# CONFIG_FEATURE_MOUNT_LOOP is not set
 # CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set
 # CONFIG_FEATURE_MTAB_SUPPORT is not set
 CONFIG_VOLUMEID=y
@@ -591,35 +608,53 @@ CONFIG_VOLUMEID=y
 #
 # Filesystem/Volume identification
 #
-CONFIG_FEATURE_VOLUMEID_EXT=y
+# CONFIG_FEATURE_VOLUMEID_EXT is not set
 CONFIG_FEATURE_VOLUMEID_BTRFS=y
-CONFIG_FEATURE_VOLUMEID_REISERFS=y
-CONFIG_FEATURE_VOLUMEID_FAT=y
-CONFIG_FEATURE_VOLUMEID_HFS=y
-CONFIG_FEATURE_VOLUMEID_JFS=y
-CONFIG_FEATURE_VOLUMEID_XFS=y
-CONFIG_FEATURE_VOLUMEID_NTFS=y
-CONFIG_FEATURE_VOLUMEID_ISO9660=y
-CONFIG_FEATURE_VOLUMEID_UDF=y
-CONFIG_FEATURE_VOLUMEID_LUKS=y
-CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y
-CONFIG_FEATURE_VOLUMEID_CRAMFS=y
-CONFIG_FEATURE_VOLUMEID_ROMFS=y
-CONFIG_FEATURE_VOLUMEID_SYSV=y
-CONFIG_FEATURE_VOLUMEID_OCFS2=y
-CONFIG_FEATURE_VOLUMEID_LINUXRAID=y
+# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
+# CONFIG_FEATURE_VOLUMEID_FAT is not set
+# CONFIG_FEATURE_VOLUMEID_HFS is not set
+# CONFIG_FEATURE_VOLUMEID_JFS is not set
+# CONFIG_FEATURE_VOLUMEID_XFS is not set
+# CONFIG_FEATURE_VOLUMEID_NTFS is not set
+# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set
+# CONFIG_FEATURE_VOLUMEID_UDF is not set
+# CONFIG_FEATURE_VOLUMEID_LUKS is not set
+# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set
+# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set
+# CONFIG_FEATURE_VOLUMEID_ROMFS is not set
+# CONFIG_FEATURE_VOLUMEID_SYSV is not set
+# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set
+# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set
 
 #
 # Miscellaneous Utilities
 #
 # CONFIG_CONSPY is not set
+CONFIG_LESS=y
+CONFIG_FEATURE_LESS_MAXLINES=9999999
+CONFIG_FEATURE_LESS_BRACKETS=y
+CONFIG_FEATURE_LESS_FLAGS=y
+CONFIG_FEATURE_LESS_MARKS=y
+CONFIG_FEATURE_LESS_REGEXP=y
+CONFIG_FEATURE_LESS_WINCH=y
+CONFIG_FEATURE_LESS_ASK_TERMINAL=y
+CONFIG_FEATURE_LESS_DASHCMD=y
+CONFIG_FEATURE_LESS_LINENUMS=y
+CONFIG_NANDWRITE=y
+CONFIG_NANDDUMP=y
+CONFIG_SETSERIAL=y
 # CONFIG_UBIATTACH is not set
 # CONFIG_UBIDETACH is not set
+CONFIG_UBIMKVOL=y
+CONFIG_UBIRMVOL=y
+CONFIG_UBIRSVOL=y
+CONFIG_UBIUPDATEVOL=y
 CONFIG_ADJTIMEX=y
 # CONFIG_BBCONFIG is not set
-# CONFIG_BEEP is not set
-CONFIG_FEATURE_BEEP_FREQ=0
-CONFIG_FEATURE_BEEP_LENGTH_MS=0
+# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
+CONFIG_BEEP=y
+CONFIG_FEATURE_BEEP_FREQ=4000
+CONFIG_FEATURE_BEEP_LENGTH_MS=30
 # CONFIG_CHAT is not set
 # CONFIG_FEATURE_CHAT_NOFAIL is not set
 # CONFIG_FEATURE_CHAT_TTY_HIFI is not set
@@ -628,22 +663,22 @@ CONFIG_FEATURE_BEEP_LENGTH_MS=0
 # CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set
 # CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set
 # CONFIG_FEATURE_CHAT_CLR_ABORT is not set
-# CONFIG_CHRT is not set
+CONFIG_CHRT=y
 CONFIG_CROND=y
-# CONFIG_FEATURE_CROND_D is not set
+CONFIG_FEATURE_CROND_D=y
 CONFIG_FEATURE_CROND_CALL_SENDMAIL=y
 CONFIG_FEATURE_CROND_DIR="/var/spool/cron"
 CONFIG_CRONTAB=y
-CONFIG_DC=y
-CONFIG_FEATURE_DC_LIBM=y
+# CONFIG_DC is not set
+# CONFIG_FEATURE_DC_LIBM is not set
 # CONFIG_DEVFSD is not set
 # CONFIG_DEVFSD_MODLOAD is not set
 # CONFIG_DEVFSD_FG_NP is not set
 # CONFIG_DEVFSD_VERBOSE is not set
 # CONFIG_FEATURE_DEVFS is not set
-# CONFIG_DEVMEM is not set
+CONFIG_DEVMEM=y
 CONFIG_EJECT=y
-CONFIG_FEATURE_EJECT_SCSI=y
+# CONFIG_FEATURE_EJECT_SCSI is not set
 # CONFIG_FBSPLASH is not set
 # CONFIG_FLASHCP is not set
 # CONFIG_FLASH_LOCK is not set
@@ -654,83 +689,82 @@ CONFIG_IONICE=y
 CONFIG_LAST=y
 CONFIG_FEATURE_LAST_SMALL=y
 # CONFIG_FEATURE_LAST_FANCY is not set
-CONFIG_LESS=y
-CONFIG_FEATURE_LESS_MAXLINES=9999999
-CONFIG_FEATURE_LESS_BRACKETS=y
-CONFIG_FEATURE_LESS_FLAGS=y
-# CONFIG_FEATURE_LESS_MARKS is not set
-# CONFIG_FEATURE_LESS_REGEXP is not set
-# CONFIG_FEATURE_LESS_WINCH is not set
-# CONFIG_FEATURE_LESS_DASHCMD is not set
-# CONFIG_FEATURE_LESS_LINENUMS is not set
-# CONFIG_HDPARM is not set
-# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set
-# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set
-# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set
-# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set
-# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set
-# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set
+CONFIG_HDPARM=y
+CONFIG_FEATURE_HDPARM_GET_IDENTITY=y
+CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y
+CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y
 CONFIG_MAKEDEVS=y
 # CONFIG_FEATURE_MAKEDEVS_LEAF is not set
 CONFIG_FEATURE_MAKEDEVS_TABLE=y
-# CONFIG_MAN is not set
-CONFIG_MICROCOM=y
+CONFIG_MAN=y
+# CONFIG_MICROCOM is not set
 # CONFIG_MOUNTPOINT is not set
 CONFIG_MT=y
-# CONFIG_RAIDAUTORUN is not set
+CONFIG_RAIDAUTORUN=y
 # CONFIG_READAHEAD is not set
 # CONFIG_RFKILL is not set
-# CONFIG_RUNLEVEL is not set
-# CONFIG_RX is not set
-# CONFIG_SETSID is not set
+CONFIG_RUNLEVEL=y
+CONFIG_RX=y
+CONFIG_SETSID=y
 CONFIG_STRINGS=y
-# CONFIG_TASKSET is not set
-# CONFIG_FEATURE_TASKSET_FANCY is not set
+CONFIG_TASKSET=y
+CONFIG_FEATURE_TASKSET_FANCY=y
 CONFIG_TIME=y
 CONFIG_TIMEOUT=y
-# CONFIG_TTYSIZE is not set
-# CONFIG_VOLNAME is not set
-# CONFIG_WALL is not set
+CONFIG_TTYSIZE=y
+CONFIG_VOLNAME=y
+CONFIG_WALL=y
 CONFIG_WATCHDOG=y
 
 #
 # Networking Utilities
 #
+CONFIG_NAMEIF=y
+# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
+CONFIG_NBDCLIENT=y
 CONFIG_NC=y
-# CONFIG_NC_SERVER is not set
-# CONFIG_NC_EXTRA is not set
+CONFIG_NC_SERVER=y
+CONFIG_NC_EXTRA=y
 # CONFIG_NC_110_COMPAT is not set
+CONFIG_PING=y
+CONFIG_PING6=y
+CONFIG_FEATURE_FANCY_PING=y
+CONFIG_WHOIS=y
 CONFIG_FEATURE_IPV6=y
 # CONFIG_FEATURE_UNIX_LOCAL is not set
-# CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set
-# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
-# CONFIG_ARP is not set
+CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
+CONFIG_VERBOSE_RESOLUTION_ERRORS=y
+CONFIG_ARP=y
 CONFIG_ARPING=y
-CONFIG_BRCTL=y
+# CONFIG_BRCTL is not set
 # CONFIG_FEATURE_BRCTL_FANCY is not set
 # CONFIG_FEATURE_BRCTL_SHOW is not set
-# CONFIG_DNSD is not set
-# CONFIG_ETHER_WAKE is not set
-# CONFIG_FAKEIDENTD is not set
-# CONFIG_FTPD is not set
-# CONFIG_FEATURE_FTP_WRITE is not set
-# CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set
+CONFIG_DNSD=y
+CONFIG_ETHER_WAKE=y
+CONFIG_FAKEIDENTD=y
+CONFIG_FTPD=y
+CONFIG_FEATURE_FTP_WRITE=y
+CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y
 CONFIG_FTPGET=y
 CONFIG_FTPPUT=y
 CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
 CONFIG_HOSTNAME=y
-CONFIG_HTTPD=y
+# CONFIG_HTTPD is not set
 # CONFIG_FEATURE_HTTPD_RANGES is not set
 # CONFIG_FEATURE_HTTPD_USE_SENDFILE is not set
 # CONFIG_FEATURE_HTTPD_SETUID is not set
-CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
+# CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set
 # CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
 # CONFIG_FEATURE_HTTPD_CGI is not set
 # CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
 # CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
 # CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set
-CONFIG_FEATURE_HTTPD_ERROR_PAGES=y
+# CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set
 # CONFIG_FEATURE_HTTPD_PROXY is not set
+# CONFIG_FEATURE_HTTPD_GZIP is not set
 CONFIG_IFCONFIG=y
 CONFIG_FEATURE_IFCONFIG_STATUS=y
 CONFIG_FEATURE_IFCONFIG_SLIP=y
@@ -738,7 +772,7 @@ CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y
 CONFIG_FEATURE_IFCONFIG_HW=y
 CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y
 # CONFIG_IFENSLAVE is not set
-# CONFIG_IFPLUGD is not set
+CONFIG_IFPLUGD=y
 CONFIG_IFUPDOWN=y
 CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate"
 CONFIG_FEATURE_IFUPDOWN_IP=y
@@ -761,28 +795,23 @@ CONFIG_FEATURE_IP_LINK=y
 CONFIG_FEATURE_IP_ROUTE=y
 CONFIG_FEATURE_IP_TUNNEL=y
 CONFIG_FEATURE_IP_RULE=y
-# CONFIG_FEATURE_IP_SHORT_FORMS is not set
+CONFIG_FEATURE_IP_SHORT_FORMS=y
 # CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
-# CONFIG_IPADDR is not set
-# CONFIG_IPLINK is not set
-# CONFIG_IPROUTE is not set
-# CONFIG_IPTUNNEL is not set
-# CONFIG_IPRULE is not set
+CONFIG_IPADDR=y
+CONFIG_IPLINK=y
+CONFIG_IPROUTE=y
+CONFIG_IPTUNNEL=y
+CONFIG_IPRULE=y
 CONFIG_IPCALC=y
 CONFIG_FEATURE_IPCALC_FANCY=y
 CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y
-CONFIG_NAMEIF=y
-# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
 CONFIG_NETSTAT=y
-# CONFIG_FEATURE_NETSTAT_WIDE is not set
+CONFIG_FEATURE_NETSTAT_WIDE=y
 # CONFIG_FEATURE_NETSTAT_PRG is not set
 CONFIG_NSLOOKUP=y
-# CONFIG_NTPD is not set
-# CONFIG_FEATURE_NTPD_SERVER is not set
-CONFIG_PING=y
-CONFIG_PING6=y
-CONFIG_FEATURE_FANCY_PING=y
-# CONFIG_PSCAN is not set
+CONFIG_NTPD=y
+CONFIG_FEATURE_NTPD_SERVER=y
+CONFIG_PSCAN=y
 CONFIG_ROUTE=y
 # CONFIG_SLATTACH is not set
 # CONFIG_TCPSVD is not set
@@ -790,10 +819,10 @@ CONFIG_TELNET=y
 CONFIG_FEATURE_TELNET_TTYPE=y
 CONFIG_FEATURE_TELNET_AUTOLOGIN=y
 CONFIG_TELNETD=y
-# CONFIG_FEATURE_TELNETD_STANDALONE is not set
-# CONFIG_FEATURE_TELNETD_INETD_WAIT is not set
+CONFIG_FEATURE_TELNETD_STANDALONE=y
+CONFIG_FEATURE_TELNETD_INETD_WAIT=y
 CONFIG_TFTP=y
-# CONFIG_TFTPD is not set
+CONFIG_TFTPD=y
 
 #
 # Common options for tftp/tftpd
@@ -801,25 +830,28 @@ CONFIG_TFTP=y
 CONFIG_FEATURE_TFTP_GET=y
 CONFIG_FEATURE_TFTP_PUT=y
 CONFIG_FEATURE_TFTP_BLOCKSIZE=y
-# CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set
+CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
 # CONFIG_TFTP_DEBUG is not set
 CONFIG_TRACEROUTE=y
 CONFIG_TRACEROUTE6=y
 CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
-# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
-# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
+CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE=y
+CONFIG_FEATURE_TRACEROUTE_USE_ICMP=y
 CONFIG_TUNCTL=y
-# CONFIG_FEATURE_TUNCTL_UG is not set
+CONFIG_FEATURE_TUNCTL_UG=y
+# CONFIG_UDHCPC6 is not set
 CONFIG_UDHCPD=y
-# CONFIG_DHCPRELAY is not set
+CONFIG_DHCPRELAY=y
 CONFIG_DUMPLEASES=y
 # CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
-CONFIG_DHCPD_LEASES_FILE="/var/lib/misc/udhcpd.leases"
+# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set
+CONFIG_DHCPD_LEASES_FILE=""
 CONFIG_UDHCPC=y
 CONFIG_FEATURE_UDHCPC_ARPING=y
 # CONFIG_FEATURE_UDHCP_PORT is not set
-CONFIG_UDHCP_DEBUG=0
+CONFIG_UDHCP_DEBUG=9
 CONFIG_FEATURE_UDHCP_RFC3397=y
+CONFIG_FEATURE_UDHCP_8021Q=y
 CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script"
 CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80
 CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n"
@@ -829,6 +861,7 @@ CONFIG_WGET=y
 CONFIG_FEATURE_WGET_STATUSBAR=y
 CONFIG_FEATURE_WGET_AUTHENTICATION=y
 CONFIG_FEATURE_WGET_LONG_OPTIONS=y
+CONFIG_FEATURE_WGET_TIMEOUT=y
 # CONFIG_ZCIP is not set
 
 #
@@ -841,45 +874,54 @@ CONFIG_FEATURE_WGET_LONG_OPTIONS=y
 #
 # Mail Utilities
 #
-# CONFIG_MAKEMIME is not set
-CONFIG_FEATURE_MIME_CHARSET=""
-# CONFIG_POPMAILDIR is not set
-# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set
-# CONFIG_REFORMIME is not set
-# CONFIG_FEATURE_REFORMIME_COMPAT is not set
+CONFIG_MAKEMIME=y
+CONFIG_FEATURE_MIME_CHARSET="us-ascii"
+CONFIG_POPMAILDIR=y
+CONFIG_FEATURE_POPMAILDIR_DELIVERY=y
+CONFIG_REFORMIME=y
+CONFIG_FEATURE_REFORMIME_COMPAT=y
 # CONFIG_SENDMAIL is not set
 
 #
 # Process Utilities
 #
-# CONFIG_SMEMCAP is not set
+CONFIG_IOSTAT=y
+CONFIG_LSOF=y
+CONFIG_MPSTAT=y
+# CONFIG_NMETER is not set
+CONFIG_PMAP=y
+CONFIG_POWERTOP=y
+CONFIG_PSTREE=y
+CONFIG_PWDX=y
+CONFIG_SMEMCAP=y
+CONFIG_UPTIME=y
+CONFIG_FEATURE_UPTIME_UTMP_SUPPORT=y
 CONFIG_FREE=y
-# CONFIG_FUSER is not set
+CONFIG_FUSER=y
 CONFIG_KILL=y
 CONFIG_KILLALL=y
-# CONFIG_KILLALL5 is not set
-# CONFIG_NMETER is not set
+CONFIG_KILLALL5=y
 # CONFIG_PGREP is not set
 CONFIG_PIDOF=y
-# CONFIG_FEATURE_PIDOF_SINGLE is not set
-# CONFIG_FEATURE_PIDOF_OMIT is not set
+CONFIG_FEATURE_PIDOF_SINGLE=y
+CONFIG_FEATURE_PIDOF_OMIT=y
 # CONFIG_PKILL is not set
 CONFIG_PS=y
 # CONFIG_FEATURE_PS_WIDE is not set
+# CONFIG_FEATURE_PS_LONG is not set
 # CONFIG_FEATURE_PS_TIME is not set
 CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y
 # CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
 CONFIG_RENICE=y
-# CONFIG_BB_SYSCTL is not set
+CONFIG_BB_SYSCTL=y
 CONFIG_TOP=y
 CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y
 CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y
-# CONFIG_FEATURE_TOP_SMP_CPU is not set
+CONFIG_FEATURE_TOP_SMP_CPU=y
 # CONFIG_FEATURE_TOP_DECIMALS is not set
-# CONFIG_FEATURE_TOP_SMP_PROCESS is not set
-# CONFIG_FEATURE_TOPMEM is not set
+CONFIG_FEATURE_TOP_SMP_PROCESS=y
+CONFIG_FEATURE_TOPMEM=y
 CONFIG_FEATURE_SHOW_THREADS=y
-CONFIG_UPTIME=y
 CONFIG_WATCH=y
 
 #
@@ -890,7 +932,7 @@ CONFIG_WATCH=y
 # CONFIG_FEATURE_RUNSVDIR_LOG is not set
 # CONFIG_SV is not set
 CONFIG_SV_DEFAULT_SERVICE_DIR=""
-# CONFIG_SVLOGD is not set
+CONFIG_SVLOGD=y
 # CONFIG_CHPST is not set
 # CONFIG_SETUIDGID is not set
 # CONFIG_ENVUIDGID is not set
@@ -917,21 +959,25 @@ CONFIG_SV_DEFAULT_SERVICE_DIR=""
 #
 CONFIG_ASH=y
 CONFIG_ASH_BASH_COMPAT=y
+# CONFIG_ASH_IDLE_TIMEOUT is not set
 CONFIG_ASH_JOB_CONTROL=y
 CONFIG_ASH_ALIAS=y
 CONFIG_ASH_GETOPTS=y
 CONFIG_ASH_BUILTIN_ECHO=y
 CONFIG_ASH_BUILTIN_PRINTF=y
 CONFIG_ASH_BUILTIN_TEST=y
-CONFIG_ASH_CMDCMD=y
-CONFIG_ASH_MAIL=y
+# CONFIG_ASH_CMDCMD is not set
+# CONFIG_ASH_MAIL is not set
 CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
-CONFIG_ASH_RANDOM_SUPPORT=y
-CONFIG_ASH_EXPAND_PRMT=y
+# CONFIG_ASH_RANDOM_SUPPORT is not set
+# CONFIG_ASH_EXPAND_PRMT is not set
+CONFIG_CTTYHACK=y
 # CONFIG_HUSH is not set
 # CONFIG_HUSH_BASH_COMPAT is not set
+# CONFIG_HUSH_BRACE_EXPANSION is not set
 # CONFIG_HUSH_HELP is not set
 # CONFIG_HUSH_INTERACTIVE is not set
+# CONFIG_HUSH_SAVEHISTORY is not set
 # CONFIG_HUSH_JOB is not set
 # CONFIG_HUSH_TICK is not set
 # CONFIG_HUSH_IF is not set
@@ -939,34 +985,35 @@ CONFIG_ASH_EXPAND_PRMT=y
 # CONFIG_HUSH_CASE is not set
 # CONFIG_HUSH_FUNCTIONS is not set
 # CONFIG_HUSH_LOCAL is not set
-# CONFIG_HUSH_EXPORT_N is not set
 # CONFIG_HUSH_RANDOM_SUPPORT is not set
+# CONFIG_HUSH_EXPORT_N is not set
+# CONFIG_HUSH_MODE_X is not set
+# CONFIG_MSH is not set
 CONFIG_FEATURE_SH_IS_ASH=y
 # CONFIG_FEATURE_SH_IS_HUSH is not set
 # CONFIG_FEATURE_SH_IS_NONE is not set
 # CONFIG_FEATURE_BASH_IS_ASH is not set
 # CONFIG_FEATURE_BASH_IS_HUSH is not set
 CONFIG_FEATURE_BASH_IS_NONE=y
-# CONFIG_LASH is not set
-# CONFIG_MSH is not set
 CONFIG_SH_MATH_SUPPORT=y
 CONFIG_SH_MATH_SUPPORT_64=y
 # CONFIG_FEATURE_SH_EXTRA_QUIET is not set
-CONFIG_FEATURE_SH_STANDALONE=y
+# CONFIG_FEATURE_SH_STANDALONE is not set
 # CONFIG_FEATURE_SH_NOFORK is not set
-# CONFIG_CTTYHACK is not set
+CONFIG_FEATURE_SH_HISTFILESIZE=y
 
 #
 # System Logging Utilities
 #
 CONFIG_SYSLOGD=y
-# CONFIG_FEATURE_ROTATE_LOGFILE is not set
-CONFIG_FEATURE_REMOTE_LOG=y
+CONFIG_FEATURE_ROTATE_LOGFILE=y
+# CONFIG_FEATURE_REMOTE_LOG is not set
 # CONFIG_FEATURE_SYSLOGD_DUP is not set
+CONFIG_FEATURE_SYSLOGD_CFG=y
 CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256
-CONFIG_FEATURE_IPC_SYSLOG=y
-CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=64
-CONFIG_LOGREAD=y
+# CONFIG_FEATURE_IPC_SYSLOG is not set
+CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0
+# CONFIG_LOGREAD is not set
 # CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
 CONFIG_KLOGD=y
 CONFIG_FEATURE_KLOGD_KLOGCTL=y
diff --git a/packaging/cttyhack-serial-console-detection-is-Linux-specific.patch b/packaging/cttyhack-serial-console-detection-is-Linux-specific.patch
deleted file mode 100644 (file)
index 7228615..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-From 430ba79c39eeed4725c36e9c2ad61c438c8a5d3e Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Fri, 30 Jul 2010 06:21:21 +0200
-Subject: [PATCH 13/19] cttyhack: serial console detection is Linux-specific
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- shell/cttyhack.c |   15 ++++++++++++---
- 1 files changed, 12 insertions(+), 3 deletions(-)
-
-Index: busybox-1.17.1/shell/cttyhack.c
-===================================================================
---- busybox-1.17.1.orig/shell/cttyhack.c       2010-08-01 05:38:31.000000000 +0200
-+++ busybox-1.17.1/shell/cttyhack.c    2010-08-01 05:39:26.000000000 +0200
-@@ -6,6 +6,10 @@
-  */
- #include "libbb.h"
-+#if !defined(__linux__) && !defined(TIOCGSERIAL)
-+# warning cttyhack will not be able to detect a controlling tty on this system
-+#endif
-+
- /* From <linux/vt.h> */
- struct vt_stat {
-       unsigned short v_active;        /* active vt */
-@@ -59,13 +63,19 @@
-               close(fd);
-       } else {
-               /* We don't have ctty (or don't have "/dev/tty" node...) */
--              if (ioctl(0, TIOCGSERIAL, &u.sr) == 0) {
-+              if (0) {}
-+#ifdef TIOCGSERIAL
-+              else if (ioctl(0, TIOCGSERIAL, &u.sr) == 0) {
-                       /* this is a serial console */
-                       sprintf(console + 8, "S%d", u.sr.line);
--              } else if (ioctl(0, VT_GETSTATE, &u.vt) == 0) {
-+              }
-+#endif
-+#ifdef __linux__
-+              else if (ioctl(0, VT_GETSTATE, &u.vt) == 0) {
-                       /* this is linux virtual tty */
-                       sprintf(console + 8, "S%d" + 1, u.vt.v_active);
-               }
-+#endif
-               if (console[8]) {
-                       fd = xopen(console, O_RDWR);
-                       //bb_error_msg("switching to '%s'", console);
-Index: busybox-1.17.1/shell/Config.src
-===================================================================
---- busybox-1.17.1.orig/shell/Config.src       2010-08-01 05:39:44.000000000 +0200
-+++ busybox-1.17.1/shell/Config.src    2010-08-01 05:39:49.000000000 +0200
-@@ -370,7 +370,6 @@
- config CTTYHACK
-       bool "cttyhack"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         One common problem reported on the mailing list is "can't access tty;
-         job control turned off" error message which typically appears when
diff --git a/packaging/debian-changes-1.17.1-10 b/packaging/debian-changes-1.17.1-10
deleted file mode 100644 (file)
index 6bb43e4..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-Description: Upstream changes introduced in version 1:1.17.1-10
- This patch has been created by dpkg-source during the package build.
- Here's the last changelog entry, hopefully it gives details on why
- those changes were made:
- .
- busybox (1:1.17.1-10) unstable; urgency=low
- .
-   [ Michael Tokarev ]
-   * tiny build system changes: from main source to build directories:
-     don't copy .dotfiles, use ln instead of cp
- .
-   [ Joey Hess ]
-   * Enable sha256sum in udeb, needed by debootstrap to handle Release
-     files w/o md5sums.
-   * Did not disable md5sum in udeb because eg anna still uses them to verify
-     md5sum fields from d-i Packages files. (Those fields are still present..
-     for now.)
- .
- The person named in the Author field signed this changelog entry.
-Author: Joey Hess <joeyh@debian.org>
-
----
-The information above should follow the Patch Tagging Guidelines, please
-checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
-are templates for supplementary fields that you might want to add:
-
-Origin: <vendor|upstream|other>, <url of original patch>
-Bug: <url in upstream bugtracker>
-Bug-Debian: http://bugs.debian.org/<bugnumber>
-Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
-Forwarded: <no|not-needed|url proving that it has been forwarded>
-Reviewed-By: <name and email of someone who approved the patch>
-Last-Update: <YYYY-MM-DD>
-
---- /dev/null
-+++ busybox-1.17.1/busybox-1.17.1/.indent.pro
-@@ -0,0 +1,33 @@
-+--blank-lines-after-declarations
-+--blank-lines-after-procedures
-+--break-before-boolean-operator
-+--no-blank-lines-after-commas
-+--braces-on-if-line
-+--braces-on-struct-decl-line
-+--comment-indentation25
-+--declaration-comment-column25
-+--no-comment-delimiters-on-blank-lines
-+--cuddle-else
-+--continuation-indentation4
-+--case-indentation0
-+--else-endif-column33
-+--space-after-cast
-+--line-comments-indentation0
-+--declaration-indentation1
-+--dont-format-first-column-comments
-+--dont-format-comments
-+--honour-newlines
-+--indent-level4
-+/* changed from 0 to 4 */
-+--parameter-indentation4
-+--line-length78 /* changed from 75 */
-+--continue-at-parentheses
-+--no-space-after-function-call-names
-+--dont-break-procedure-type
-+--dont-star-comments
-+--leave-optional-blank-lines
-+--dont-space-special-semicolon
-+--tab-size4
-+/* additions by Mark */
-+--case-brace-indentation0
-+--leave-preprocessor-space
diff --git a/packaging/doc-man-name.patch b/packaging/doc-man-name.patch
deleted file mode 100644 (file)
index d793c35..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
---- a/Makefile.custom
-+++ b/Makefile.custom
-@@ -107,7 +107,7 @@
- # Documentation Targets
- .PHONY: doc
--doc: docs/busybox.pod docs/BusyBox.txt docs/BusyBox.1 docs/BusyBox.html
-+doc: docs/busybox.pod docs/BusyBox.txt docs/busybox.1 docs/BusyBox.html
- # FIXME: Doesn't belong here
-        cmd_doc =
-@@ -134,10 +134,10 @@
-       $(Q)-mkdir -p docs
-       $(Q)-pod2text $< > $@
--docs/BusyBox.1: docs/busybox.pod
-+docs/busybox.1: docs/busybox.pod
-       $(disp_doc)
-       $(Q)-mkdir -p docs
--      $(Q)-pod2man --center=BusyBox --release="version $(KERNELVERSION)" $< > $@
-+      $(Q)-pod2man --center=busybox --release="version $(KERNELVERSION)" $< > $@
- docs/BusyBox.html: docs/busybox.net/BusyBox.html
-       $(disp_doc)
diff --git a/packaging/init-console-CRTSCTS.patch b/packaging/init-console-CRTSCTS.patch
deleted file mode 100644 (file)
index ddc06fb..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
---- a/init/init.c.original     2010-10-17 20:12:02.000000000 +0200
-+++ b/init/init.c      2010-10-17 20:12:17.000000000 +0200
-@@ -232,7 +232,11 @@
- #endif
-       /* Make it be sane */
--      tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
-+      tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD
-+#ifdef CRTSCTS
-+              | CRTSCTS
-+#endif
-+              ;
-       tty.c_cflag |= CREAD | HUPCL | CLOCAL;
-       /* input modes */
diff --git a/packaging/init-console.patch b/packaging/init-console.patch
deleted file mode 100644 (file)
index 6845950..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/init/init.c
-+++ b/init/init.c
-@@ -441,6 +441,8 @@
-       for (a = init_action_list; a; a = a->next) {
-               if (!(a->action_type & action_type))
-                       continue;
-+              if (a->terminal[0] && access(a->terminal, R_OK | W_OK))
-+                      continue;
-               if (a->action_type & (SYSINIT | WAIT | ONCE | CTRLALTDEL | SHUTDOWN)) {
-                       pid_t pid = run(a);
diff --git a/packaging/init-halt-portability-improvements.patch b/packaging/init-halt-portability-improvements.patch
deleted file mode 100644 (file)
index 94618c3..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-From 714674e4da3d92c5dd14e00ab30794a895b91eb4 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:38:44 +0200
-Subject: [PATCH 4/9] init,halt: portability improvements
-
-* make init and halt use the same RB_* constants for reboot()
-* conditionalize the Linux-specific code
-
-Inspired by init.init.diff from the Debian kFreeBSD patches at:
-http://svn.debian.org/viewsvn/d-i/people/slackydeb/kfreebsd/busybox/1.14/debian
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- init/Config.src |    2 --
- init/halt.c     |   14 +-------------
- init/init.c     |   16 ++++++++--------
- init/reboot.h   |   31 +++++++++++++++++++++++++++++++
- 4 files changed, 40 insertions(+), 23 deletions(-)
- create mode 100644 init/reboot.h
-
-Index: busybox-1.17.1/init/Config.src
-===================================================================
---- busybox-1.17.1.orig/init/Config.src        2010-08-01 05:32:43.000000000 +0200
-+++ busybox-1.17.1/init/Config.src     2010-08-01 05:36:47.000000000 +0200
-@@ -10,7 +10,6 @@
- config INIT
-       bool "init"
-       default y
--      depends on PLATFORM_LINUX
-       select FEATURE_SYSLOG
-       help
-         init is the first program run when the system boots.
-@@ -93,7 +92,6 @@
- config HALT
-       bool "poweroff, halt, and reboot"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         Stop all processes and either halt, reboot, or power off the system.
-Index: busybox-1.17.1/init/halt.c
-===================================================================
---- busybox-1.17.1.orig/init/halt.c    2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/init/halt.c 2010-08-01 05:36:47.000000000 +0200
-@@ -8,7 +8,7 @@
-  */
- #include "libbb.h"
--#include <sys/reboot.h>
-+#include "reboot.h"
- #if ENABLE_FEATURE_WTMP
- #include <sys/utsname.h>
-@@ -36,18 +36,6 @@
- #define write_wtmp() ((void)0)
- #endif
--#ifndef RB_HALT_SYSTEM
--#define RB_HALT_SYSTEM RB_HALT
--#endif
--
--#ifndef RB_POWERDOWN
--/* Stop system and switch power off if possible.  */
--# define RB_POWERDOWN   0x4321fedc
--#endif
--#ifndef RB_POWER_OFF
--# define RB_POWER_OFF RB_POWERDOWN
--#endif
--
- int halt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
- int halt_main(int argc UNUSED_PARAM, char **argv)
-Index: busybox-1.17.1/init/init.c
-===================================================================
---- busybox-1.17.1.orig/init/init.c    2010-08-01 05:36:45.000000000 +0200
-+++ busybox-1.17.1/init/init.c 2010-08-01 05:36:47.000000000 +0200
-@@ -12,7 +12,6 @@
- #include "libbb.h"
- #include <syslog.h>
- #include <paths.h>
--#include <sys/reboot.h>
- #include <sys/resource.h>
- #ifdef __linux__
- #include <linux/vt.h>
-@@ -20,6 +19,7 @@
- #if ENABLE_FEATURE_UTMP
- # include <utmp.h> /* DEAD_PROCESS */
- #endif
-+#include "reboot.h" /* reboot() constants */
- /* Used only for sanitizing purposes in set_sane_term() below. On systems where
-  * the baud rate is stored in a separate field, we can safely disable them. */
-@@ -97,13 +97,6 @@
- enum {
-       L_LOG = 0x1,
-       L_CONSOLE = 0x2,
--#ifndef RB_HALT_SYSTEM
--      RB_HALT_SYSTEM = 0xcdef0123, /* FIXME: this overflows enum */
--      RB_ENABLE_CAD = 0x89abcdef,
--      RB_DISABLE_CAD = 0,
--      RB_POWER_OFF = 0x4321fedc,
--      RB_AUTOBOOT = 0x01234567,
--#endif
- };
- /* Print a message to the specified device.
-@@ -726,10 +719,12 @@
-               run_shutdown_and_kill_processes();
-+#ifdef RB_ENABLE_CAD
-               /* Allow Ctrl-Alt-Del to reboot the system.
-                * This is how kernel sets it up for init, we follow suit.
-                */
-               reboot(RB_ENABLE_CAD); /* misnomer */
-+#endif
-               if (open_stdio_to_tty(a->terminal)) {
-                       dbg_message(L_CONSOLE, "Trying to re-exec %s", a->command);
-@@ -872,9 +867,11 @@
-               ) {
-                       bb_show_usage();
-               }
-+#ifdef RB_DISABLE_CAD
-               /* Turn off rebooting via CTL-ALT-DEL - we get a
-                * SIGINT on CAD so we can shut things down gracefully... */
-               reboot(RB_DISABLE_CAD); /* misnomer */
-+#endif
-       }
-       /* Figure out where the default console should be */
-@@ -897,6 +894,8 @@
-       message(L_CONSOLE | L_LOG, "init started: %s", bb_banner);
- #endif
-+/* struct sysinfo is linux-specific */
-+#ifdef __linux__
-       /* Make sure there is enough memory to do something useful. */
-       if (ENABLE_SWAPONOFF) {
-               struct sysinfo info;
-@@ -912,6 +911,7 @@
-                       run_actions(SYSINIT);   /* wait and removing */
-               }
-       }
-+#endif
-       /* Check if we are supposed to be in single user mode */
-       if (argv[1]
-Index: busybox-1.17.1/init/reboot.h
-===================================================================
---- /dev/null  1970-01-01 00:00:00.000000000 +0000
-+++ busybox-1.17.1/init/reboot.h       2010-08-01 05:36:47.000000000 +0200
-@@ -0,0 +1,31 @@
-+/*
-+ * Definitions related to the reboot() system call,
-+ * shared between init.c and halt.c.
-+ */
-+
-+#include <sys/reboot.h>
-+
-+#ifndef RB_HALT_SYSTEM
-+# if defined(__linux__)
-+#  define RB_HALT_SYSTEM  0xcdef0123
-+#  define RB_ENABLE_CAD   0x89abcdef
-+#  define RB_DISABLE_CAD  0
-+#  define RB_POWER_OFF    0x4321fedc
-+#  define RB_AUTOBOOT     0x01234567
-+# elif defined(RB_HALT)
-+#  define RB_HALT_SYSTEM  RB_HALT
-+# endif
-+#endif
-+
-+/* Stop system and switch power off if possible.  */
-+#ifndef RB_POWER_OFF
-+# if defined(RB_POWERDOWN)
-+#  define RB_POWER_OFF  RB_POWERDOWN
-+# elif defined(__linux__)
-+#  define RB_POWER_OFF  0x4321fedc
-+# else
-+#  warning "poweroff unsupported, using halt as fallback"
-+#  define RB_POWER_OFF  RB_HALT_SYSTEM
-+# endif
-+#endif
-+
diff --git a/packaging/init-loginutils-termios-portability-fixes.patch b/packaging/init-loginutils-termios-portability-fixes.patch
deleted file mode 100644 (file)
index c612263..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-From f812eace1863feeac64dc8af27f4ab0f98119618 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:37:32 +0200
-Subject: [PATCH 3/9] init,loginutils: termios portability fixes
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- init/init.c           |   17 ++++++++++++++++-
- loginutils/Config.src |    2 --
- loginutils/getty.c    |   27 ++++++++++++++-------------
- loginutils/login.c    |    2 +-
- 4 files changed, 31 insertions(+), 17 deletions(-)
-
-diff --git a/init/init.c b/init/init.c
-index 2eb8f1a..1388c75 100644
---- a/init/init.c
-+++ b/init/init.c
-@@ -14,11 +14,19 @@
- #include <paths.h>
- #include <sys/reboot.h>
- #include <sys/resource.h>
-+#ifdef __linux__
- #include <linux/vt.h>
-+#endif
- #if ENABLE_FEATURE_UTMP
- # include <utmp.h> /* DEAD_PROCESS */
- #endif
-+/* Used only for sanitizing purposes in set_sane_term() below. On systems where
-+ * the baud rate is stored in a separate field, we can safely disable them. */
-+#ifndef CBAUD
-+# define CBAUD 0
-+# define CBAUDEX 0
-+#endif
- /* Was a CONFIG_xxx option. A lot of people were building
-  * not fully functional init by switching it on! */
-@@ -166,7 +174,9 @@ static void message(int where, const char *fmt, ...)
- static void console_init(void)
- {
-+#ifdef VT_OPENQRY
-       int vtno;
-+#endif
-       char *s;
-       s = getenv("CONSOLE");
-@@ -190,6 +200,7 @@ static void console_init(void)
-       }
-       s = getenv("TERM");
-+#ifdef VT_OPENQRY
-       if (ioctl(STDIN_FILENO, VT_OPENQRY, &vtno) != 0) {
-               /* Not a linux terminal, probably serial console.
-                * Force the TERM setting to vt102
-@@ -198,7 +209,9 @@ static void console_init(void)
-                       putenv((char*)"TERM=vt102");
-               if (!ENABLE_FEATURE_INIT_SYSLOG)
-                       log_console = NULL;
--      } else if (!s)
-+      } else
-+#endif
-+      if (!s)
-               putenv((char*)"TERM=linux");
- }
-@@ -220,8 +233,10 @@ static void set_sane_term(void)
-       tty.c_cc[VSTOP] = 19;   /* C-s */
-       tty.c_cc[VSUSP] = 26;   /* C-z */
-+#ifdef __linux__
-       /* use line discipline 0 */
-       tty.c_line = 0;
-+#endif
-       /* Make it be sane */
-       tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
-diff --git a/loginutils/Config.src b/loginutils/Config.src
-index 425d041..6ec2893 100644
---- a/loginutils/Config.src
-+++ b/loginutils/Config.src
-@@ -179,7 +179,6 @@ config DELUSER
- config GETTY
-       bool "getty"
-       default y
--      depends on PLATFORM_LINUX
-       select FEATURE_SYSLOG
-       help
-         getty lets you log in on a tty, it is normally invoked by init.
-@@ -187,7 +186,6 @@ config GETTY
- config LOGIN
-       bool "login"
-       default y
--      depends on PLATFORM_LINUX
-       select FEATURE_SUID
-       select FEATURE_SYSLOG
-       help
-diff --git a/loginutils/getty.c b/loginutils/getty.c
-index a5e8e90..7f04d33 100644
---- a/loginutils/getty.c
-+++ b/loginutils/getty.c
-@@ -282,10 +282,8 @@ static void termios_init(struct termios *tp, int speed, struct options *op)
-        * reads will be done in raw mode anyway. Errors will be dealt with
-        * later on.
-        */
--#ifdef __linux__
-       /* flush input and output queues, important for modems! */
--      ioctl(0, TCFLSH, TCIOFLUSH); /* tcflush(0, TCIOFLUSH)? - same */
--#endif
-+      tcflush(0, TCIOFLUSH);
-       ispeed = ospeed = speed;
-       if (speed == B0) {
-               /* Speed was specified as "0" on command line.
-@@ -299,10 +297,13 @@ static void termios_init(struct termios *tp, int speed, struct options *op)
-       cfsetispeed(tp, ispeed);
-       cfsetospeed(tp, ospeed);
--      tp->c_iflag = tp->c_lflag = tp->c_line = 0;
-+      tp->c_iflag = tp->c_lflag = 0;
-       tp->c_oflag = OPOST | ONLCR;
-       tp->c_cc[VMIN] = 1;
-       tp->c_cc[VTIME] = 0;
-+#ifdef __linux__
-+      tp->c_line = 0;
-+#endif
-       /* Optionally enable hardware flow control */
- #ifdef CRTSCTS
-@@ -360,10 +361,8 @@ static void auto_baud(char *buf, unsigned size_buf, struct termios *tp)
-               for (bp = buf; bp < buf + nread; bp++) {
-                       if (isdigit(*bp)) {
-                               speed = bcode(bp);
--                              if (speed > 0) {
--                                      tp->c_cflag &= ~CBAUD;
--                                      tp->c_cflag |= speed;
--                              }
-+                              if (speed > 0)
-+                                      cfsetspeed(tp, speed);
-                               break;
-                       }
-               }
-@@ -417,7 +416,7 @@ static char *get_logname(char *logname, unsigned size_logname,
-       /* Flush pending input (esp. after parsing or switching the baud rate). */
-       sleep(1);
--      ioctl(0, TCFLSH, TCIFLUSH); /* tcflush(0, TCIOFLUSH)? - same */
-+      tcflush(0, TCIOFLUSH);
-       /* Prompt for and read a login name. */
-       logname[0] = '\0';
-@@ -526,7 +525,9 @@ static void termios_final(struct options *op, struct termios *tp, struct chardat
-       tp->c_cc[VQUIT] = DEF_QUIT;     /* default quit */
-       tp->c_cc[VEOF] = DEF_EOF;       /* default EOF character */
-       tp->c_cc[VEOL] = DEF_EOL;
-+#ifdef VSWTC
-       tp->c_cc[VSWTC] = DEF_SWITCH;   /* default switch character */
-+#endif
-       /* Account for special characters seen in input. */
-       if (cp->eol == CR) {
-@@ -572,8 +573,8 @@ static void termios_final(struct options *op, struct termios *tp, struct chardat
- #endif
-       /* Finally, make the new settings effective */
--      /* It's tcsetattr_stdin_TCSANOW() + error check */
--      ioctl_or_perror_and_die(0, TCSETS, tp, "%s: TCSETS", op->tty);
-+      if (tcsetattr_stdin_TCSANOW(tp) < 0)
-+              bb_perror_msg_and_die("%s: tcsetattr", op->tty);
- }
- int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-@@ -650,8 +651,8 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
-        * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
-        * 5 seconds seems to be a good value.
-        */
--      /* tcgetattr() + error check */
--      ioctl_or_perror_and_die(0, TCGETS, &termios, "%s: TCGETS", options.tty);
-+      if (tcgetattr(0, &termios) < 0)
-+              bb_perror_msg_and_die("%s: tcgetattr", options.tty);
-       pid = getpid();
- #ifdef __linux__
-diff --git a/loginutils/login.c b/loginutils/login.c
-index 88ed0af..1001248 100644
---- a/loginutils/login.c
-+++ b/loginutils/login.c
-@@ -264,7 +264,7 @@ int login_main(int argc UNUSED_PARAM, char **argv)
-       while (1) {
-               /* flush away any type-ahead (as getty does) */
--              ioctl(0, TCFLSH, TCIFLUSH);
-+              tcflush(0, TCIFLUSH);
-               if (!username[0])
-                       get_username_or_die(username, sizeof(username));
--- 
-1.7.1
-
diff --git a/packaging/init-make-the-initial-TERM-value-configurable.patch b/packaging/init-make-the-initial-TERM-value-configurable.patch
deleted file mode 100644 (file)
index fb04a1e..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-From 1c05303fdc302725093294eb0305adc003d52bcb Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:46:07 +0200
-Subject: [PATCH 5/9] init: make the initial $TERM value configurable
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- init/Config.src |   12 ++++++++++++
- init/init.c     |    2 +-
- 2 files changed, 13 insertions(+), 1 deletions(-)
-
-diff --git a/init/Config.src b/init/Config.src
-index 590e298..2cac357 100644
---- a/init/Config.src
-+++ b/init/Config.src
-@@ -89,6 +89,18 @@ config FEATURE_INITRD
-         This does not apply to initramfs, which runs /init as PID 1 and
-         requires no special support.
-+config INIT_TERMINAL_TYPE
-+      string "Initial terminal type"
-+      default "linux"
-+      depends on INIT
-+      help
-+        This is the initial value set by init for the TERM environment
-+        variable. This variable is used by programs which make use of
-+        extended terminal capabilities.
-+
-+        Note that on Linux, init attempts to detect serial terminal and
-+        sets TERM to "vt102" if one is found.
-+
- config HALT
-       bool "poweroff, halt, and reboot"
-       default y
-diff --git a/init/init.c b/init/init.c
-index d8bf158..fa1af6d 100644
---- a/init/init.c
-+++ b/init/init.c
-@@ -205,7 +205,7 @@ static void console_init(void)
-       } else
- #endif
-       if (!s)
--              putenv((char*)"TERM=linux");
-+              putenv((char*)"TERM=" CONFIG_INIT_TERMINAL_TYPE);
- }
- /* Set terminal settings to reasonable defaults.
--- 
-1.7.1
-
diff --git a/packaging/klogd-make-it-work-on-non-linux-systems.patch b/packaging/klogd-make-it-work-on-non-linux-systems.patch
deleted file mode 100644 (file)
index 033617f..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-From 63c2e7ecc0c7a72b2ed35475a8d18d3052039ce4 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Sun, 1 Aug 2010 03:01:44 +0200
-Subject: [PATCH 1/2] klogd: make it work on non-linux systems
-
-The klogctl() interface allows changing the console loglevel, but is
-Linux-specific. The more portable method of reading from _PATH_KLOG is
-added as an alternative.
-
-Adapted from the Debian kFreeBSD patch at:
-http://svn.debian.org/viewsvn/d-i/people/slackydeb/kfreebsd/busybox/1.14/debian/klogd.diff
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- sysklogd/Config.src |   17 ++++++-
- sysklogd/klogd.c    |  128 +++++++++++++++++++++++++++++++++++++++++++-------
- 2 files changed, 126 insertions(+), 19 deletions(-)
-
-diff --git a/sysklogd/Config.src b/sysklogd/Config.src
-index 41c0d28..1e59872 100644
---- a/sysklogd/Config.src
-+++ b/sysklogd/Config.src
-@@ -109,7 +109,6 @@ config FEATURE_LOGREAD_REDUCED_LOCKING
- config KLOGD
-       bool "klogd"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         klogd is a utility which intercepts and logs all
-         messages from the Linux kernel and sends the messages
-@@ -117,6 +116,22 @@ config KLOGD
-         you wish to record the messages produced by the kernel,
-         you should enable this option.
-+config FEATURE_KLOGD_KLOGCTL
-+      bool "Use the klogctl() interface"
-+      default y
-+      depends on KLOGD && PLATFORM_LINUX
-+      help
-+        The klogd applet supports two interfaces for reading
-+        kernel messages. Linux provides the klogctl() interface
-+        which allows reading messages from the kernel ring buffer
-+        independently from the file system.
-+
-+        If you answer 'N' here, klogd will use the more portable
-+        approach of reading them from /proc or a device node.
-+        However, this method requires the file to be available.
-+
-+        If in doubt, say 'Y'.
-+
- config LOGGER
-       bool "logger"
-       default y
-diff --git a/sysklogd/klogd.c b/sysklogd/klogd.c
-index c54e80a..3468656 100644
---- a/sysklogd/klogd.c
-+++ b/sysklogd/klogd.c
-@@ -4,7 +4,7 @@
-  *
-  * Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>.
-  * Changes: Made this a standalone busybox module which uses standalone
-- *                                    syslog() client interface.
-+ * syslog() client interface.
-  *
-  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
-  *
-@@ -19,18 +19,93 @@
- #include "libbb.h"
- #include <syslog.h>
--#include <sys/klog.h>
--static void klogd_signal(int sig)
-+
-+/* The Linux-specific klogctl(3) interface does not rely on the filesystem and
-+ * allows us to change the console loglevel. Alternatively, we read the
-+ * messages from _PATH_KLOG. */
-+
-+#if ENABLE_FEATURE_KLOGD_KLOGCTL
-+
-+# include <sys/klog.h>
-+
-+static void klogd_open(void)
-+{
-+      /* "Open the log. Currently a NOP" */
-+      klogctl(1, NULL, 0);
-+}
-+
-+static void klogd_setloglevel(int lvl)
-+{
-+      /* "printk() prints a message on the console only if it has a loglevel
-+       * less than console_loglevel". Here we set console_loglevel = lvl. */
-+      klogctl(8, NULL, lvl);
-+}
-+
-+static int klogd_read(char *bufp, int len)
-+{
-+      return klogctl(2, bufp, len);
-+}
-+# define READ_ERROR "klogctl(2) error"
-+
-+static void klogd_close(void)
- {
-       /* FYI: cmd 7 is equivalent to setting console_loglevel to 7
-        * via klogctl(8, NULL, 7). */
-       klogctl(7, NULL, 0); /* "7 -- Enable printk's to console" */
-       klogctl(0, NULL, 0); /* "0 -- Close the log. Currently a NOP" */
--      syslog(LOG_NOTICE, "klogd: exiting");
--      kill_myself_with_sig(sig);
- }
-+#else
-+
-+# include <paths.h>
-+# ifndef _PATH_KLOG
-+#  ifdef __GNU__
-+#   define _PATH_KLOG "/dev/klog"
-+#  else
-+#   error "your system's _PATH_KLOG is unknown"
-+#  endif
-+# endif
-+# define PATH_PRINTK "/proc/sys/kernel/printk"
-+
-+enum { klogfd = 3 };
-+
-+static void klogd_open(void)
-+{
-+      int fd = xopen(_PATH_KLOG, O_RDONLY);
-+      xmove_fd(fd, klogfd);
-+}
-+
-+static void klogd_setloglevel(int lvl)
-+{
-+      FILE *fp = fopen_or_warn(PATH_PRINTK, "w");
-+      if (fp) {
-+              /* This changes only first value:
-+               * "messages with a higher priority than this
-+               * [that is, with numerically lower value]
-+               * will be printed to the console".
-+               * The other three values in this pseudo-file aren't changed.
-+               */
-+              fprintf(fp, "%u\n", lvl);
-+              fclose(fp);
-+      }
-+}
-+
-+static int klogd_read(char *bufp, int len)
-+{
-+      return read(klogfd, bufp, len);
-+}
-+# define READ_ERROR "read error"
-+
-+static void klogd_close(void)
-+{
-+      klogd_setloglevel(7);
-+      if (ENABLE_FEATURE_CLEAN_UP)
-+              close(klogfd);
-+}
-+
-+#endif
-+
- #define log_buffer bb_common_bufsiz1
- enum {
-       KLOGD_LOGBUF_SIZE = sizeof(log_buffer),
-@@ -38,6 +113,19 @@ enum {
-       OPT_FOREGROUND = (1 << 1),
- };
-+/* TODO: glibc openlog(LOG_KERN) reverts to LOG_USER instead,
-+ * because that's how they interpret word "default"
-+ * in the openlog() manpage:
-+ *      LOG_USER (default)
-+ *              generic user-level messages
-+ * and the fact that LOG_KERN is a constant 0.
-+ * glibc interprets it as "0 in openlog() call means 'use default'".
-+ * I think it means "if openlog wasn't called before syslog() is called,
-+ * use default".
-+ * Convincing glibc maintainers otherwise is, as usual, nearly impossible.
-+ * Should we open-code syslog() here to use correct facility?
-+ */
-+
- int klogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
- int klogd_main(int argc UNUSED_PARAM, char **argv)
- {
-@@ -55,34 +143,34 @@ int klogd_main(int argc UNUSED_PARAM, char **argv)
-               bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
-       }
--      openlog("kernel", 0, LOG_KERN);
--
--      bb_signals(BB_FATAL_SIGS, klogd_signal);
--      signal(SIGHUP, SIG_IGN);
-+      logmode = LOGMODE_SYSLOG;
--      /* "Open the log. Currently a NOP" */
--      klogctl(1, NULL, 0);
-+      /* klogd_open() before openlog(), since it might use fixed fd 3,
-+       * and openlog() also may use the same fd 3 if we swap them:
-+       */
-+      klogd_open();
-+      openlog("kernel", 0, LOG_KERN);
--      /* "printk() prints a message on the console only if it has a loglevel
--       * less than console_loglevel". Here we set console_loglevel = i. */
-       if (i)
--              klogctl(8, NULL, i);
-+              klogd_setloglevel(i);
-+
-+      bb_signals(BB_FATAL_SIGS, record_signo);
-+      signal(SIGHUP, SIG_IGN);
-       syslog(LOG_NOTICE, "klogd started: %s", bb_banner);
--      while (1) {
-+      while (!bb_got_signal) {
-               int n;
-               int priority;
-               char *start;
-               /* "2 -- Read from the log." */
-               start = log_buffer + used;
--              n = klogctl(2, start, KLOGD_LOGBUF_SIZE-1 - used);
-+              n = klogd_read(start, KLOGD_LOGBUF_SIZE-1 - used);
-               if (n < 0) {
-                       if (errno == EINTR)
-                               continue;
--                      syslog(LOG_ERR, "klogd: error %d in klogctl(2): %m",
--                                      errno);
-+                      bb_perror_msg(READ_ERROR);
-                       break;
-               }
-               start[n] = '\0';
-@@ -131,5 +219,9 @@ int klogd_main(int argc UNUSED_PARAM, char **argv)
-               }
-       }
-+      klogd_close();
-+      syslog(LOG_NOTICE, "klogd: exiting");
-+      if (bb_got_signal)
-+              kill_myself_with_sig(bb_got_signal);
-       return EXIT_FAILURE;
- }
--- 
-1.7.1
-
diff --git a/packaging/klogd.service b/packaging/klogd.service
deleted file mode 100644 (file)
index cce62ac..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-[Unit]
-Description=Run syslog
-After=syslogd.service
-
-[Service]
-Type=forking
-ExecStart=/sbin/klogd
-OOMScoreAdjust=-1000
-Restart=always
-RestartSec=0
-
-[Install]
-WantedBy=basic.target
diff --git a/packaging/less-remove-misguided-dependency-on-PLATFORM_LINUX.patch b/packaging/less-remove-misguided-dependency-on-PLATFORM_LINUX.patch
deleted file mode 100644 (file)
index 5bec32f..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-From 5a71fb82025d8bbb87a2d0a0851cb4c9cc31e888 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 29 Jul 2010 04:29:47 +0200
-Subject: [PATCH 10/12] less: remove misguided dependency on PLATFORM_LINUX
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- miscutils/Config.src |    1 -
- 1 files changed, 0 insertions(+), 1 deletions(-)
-
-Index: busybox-1.17.1/miscutils/Config.src
-===================================================================
---- busybox-1.17.1.orig/miscutils/Config.src   2010-08-01 07:33:11.000000000 +0200
-+++ busybox-1.17.1/miscutils/Config.src        2010-08-02 00:45:01.000000000 +0200
-@@ -351,11 +351,6 @@
- config LESS
-       bool "less"
-       default y
--      depends on PLATFORM_LINUX
--      depends on PLATFORM_LINUX
--      depends on PLATFORM_LINUX
--      depends on PLATFORM_LINUX
--      depends on PLATFORM_LINUX
-       help
-         'less' is a pager, meaning that it displays text files. It possesses
-         a wide array of features, and is an improvement over 'more'.
diff --git a/packaging/libbb-conditionalize-AF_-usage-in-error-reporting.patch b/packaging/libbb-conditionalize-AF_-usage-in-error-reporting.patch
deleted file mode 100644 (file)
index 73c9c32..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-From 29885114a5e3d22ee7aa3ab0e373e00e7cff443c Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:39:24 +0200
-Subject: [PATCH 8/9] libbb: conditionalize AF_* usage in error reporting
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- libbb/xfuncs_printf.c |    4 ++++
- networking/Config.src |    1 -
- 2 files changed, 4 insertions(+), 1 deletions(-)
-
-diff --git a/libbb/xfuncs_printf.c b/libbb/xfuncs_printf.c
-index 7069a7c..91f7ba2 100644
---- a/libbb/xfuncs_printf.c
-+++ b/libbb/xfuncs_printf.c
-@@ -387,8 +387,12 @@ int FAST_FUNC xsocket(int domain, int type, int protocol)
-               /* Hijack vaguely related config option */
- #if ENABLE_VERBOSE_RESOLUTION_ERRORS
-               const char *s = "INET";
-+# ifdef AF_PACKET
-               if (domain == AF_PACKET) s = "PACKET";
-+# endif
-+# ifdef AF_NETLINK
-               if (domain == AF_NETLINK) s = "NETLINK";
-+# endif
- IF_FEATURE_IPV6(if (domain == AF_INET6) s = "INET6";)
-               bb_perror_msg_and_die("socket(AF_%s,%d,%d)", s, type, protocol);
- #else
-diff --git a/networking/Config.src b/networking/Config.src
-index 26c59e7..fc613e8 100644
---- a/networking/Config.src
-+++ b/networking/Config.src
-@@ -43,7 +43,6 @@ config FEATURE_PREFER_IPV4_ADDRESS
- config VERBOSE_RESOLUTION_ERRORS
-       bool "Verbose resolution errors"
-       default n
--      depends on PLATFORM_LINUX #because of xsocket() in libbb/xfuncs_prinf.c
-       help
-         Enable if you are not satisfied with simplistic
-         "can't resolve 'hostname.com'" and want to know more.
--- 
-1.7.1
-
diff --git a/packaging/libbb.h-add-device-names-for-Hurd-and-FreeBSD.patch b/packaging/libbb.h-add-device-names-for-Hurd-and-FreeBSD.patch
deleted file mode 100644 (file)
index 91ac9bf..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-From 816ed971e4ce60564f7ecbdc016d268d8e936230 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:46:29 +0200
-Subject: [PATCH 6/9] libbb.h: add device names for Hurd and FreeBSD
-
-Adapted from include.libbb.diff from the Debian kFreeBSD people:
-http://svn.debian.org/viewsvn/d-i/people/slackydeb/kfreebsd/busybox/1.14/debian
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- include/libbb.h |   26 +++++++++++++++++++++-----
- 1 files changed, 21 insertions(+), 5 deletions(-)
-
-Index: busybox-1.17.1/include/libbb.h
-===================================================================
---- busybox-1.17.1.orig/include/libbb.h        2010-08-01 05:24:36.000000000 +0200
-+++ busybox-1.17.1/include/libbb.h     2010-08-01 05:36:59.000000000 +0200
-@@ -1614,7 +1614,27 @@
- /* "sh" */
- #define DEFAULT_SHELL_SHORT_NAME   (bb_default_login_shell+6)
--#if ENABLE_FEATURE_DEVFS
-+/* The following devices are the same on all systems.  */
-+#define CURRENT_TTY "/dev/tty"
-+#define DEV_CONSOLE "/dev/console"
-+
-+#if defined(__FreeBSD_kernel__)
-+# define CURRENT_VC CURRENT_TTY
-+# define VC_1 "/dev/ttyv0"
-+# define VC_2 "/dev/ttyv1"
-+# define VC_3 "/dev/ttyv2"
-+# define VC_4 "/dev/ttyv3"
-+# define VC_5 "/dev/ttyv4"
-+# define VC_FORMAT "/dev/ttyv%d"
-+#elif defined(__GNU__)
-+# define CURRENT_VC CURRENT_TTY
-+# define VC_1 "/dev/tty1"
-+# define VC_2 "/dev/tty2"
-+# define VC_3 "/dev/tty3"
-+# define VC_4 "/dev/tty4"
-+# define VC_5 "/dev/tty5"
-+# define VC_FORMAT "/dev/tty%d"
-+#elif ENABLE_FEATURE_DEVFS /* from now on, assume Linux naming */
- # define CURRENT_VC "/dev/vc/0"
- # define VC_1 "/dev/vc/1"
- # define VC_2 "/dev/vc/2"
-@@ -1661,10 +1681,6 @@
- # define FB_0 "/dev/fb0"
- #endif
--/* The following devices are the same on devfs and non-devfs systems.  */
--#define CURRENT_TTY "/dev/tty"
--#define DEV_CONSOLE "/dev/console"
--
- #define ARRAY_SIZE(x) ((unsigned)(sizeof(x) / sizeof((x)[0])))
diff --git a/packaging/make_gen_build_files_skip_quilt.patch b/packaging/make_gen_build_files_skip_quilt.patch
deleted file mode 100644 (file)
index d654468..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-Index: busybox-1.17.1/scripts/gen_build_files.sh
-===================================================================
---- busybox-1.17.1.orig/scripts/gen_build_files.sh     2010-08-01 07:28:13.000000000 +0200
-+++ busybox-1.17.1/scripts/gen_build_files.sh  2010-08-01 07:31:03.000000000 +0200
-@@ -48,7 +48,7 @@
- fi
- # (Re)generate */Kbuild and */Config.in
--{ cd -- "$srctree" && find -type d; } | while read -r d; do
-+{ cd -- "$srctree" && find -name .\?\* -prune -or -type d -print; } | while read -r d; do
-       d="${d#./}"
-       src="$srctree/$d/Kbuild.src"
diff --git a/packaging/make_unicode_printable.patch b/packaging/make_unicode_printable.patch
deleted file mode 100644 (file)
index b62ed6f..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-diff --git a/libbb/printable_string.c b/libbb/printable_string.c
-index 83a4821..8a62725 100644
---- a/libbb/printable_string.c
-+++ b/libbb/printable_string.c
-@@ -31,8 +31,8 @@ const char* FAST_FUNC printable_string(uni_stat_t *stats, const char *str)
-               }
-               if (c < ' ')
-                       break;
--              if (c >= 0x7f)
--                      break;
-+              /* if (c >= 0x7f) */
-+                      /* break; */
-               s++;
-       }
-@@ -45,7 +45,8 @@ const char* FAST_FUNC printable_string(uni_stat_t *stats, const char *str)
-                       unsigned char c = *d;
-                       if (c == '\0')
-                               break;
--                      if (c < ' ' || c >= 0x7f)
-+                      /* if (c < ' ' || c >= 0x7f) */
-+                      if (c < ' ')
-                               *d = '?';
-                       d++;
-               }
diff --git a/packaging/mark-Linux-specific-configuration-options.patch b/packaging/mark-Linux-specific-configuration-options.patch
deleted file mode 100644 (file)
index 8f89c3e..0000000
+++ /dev/null
@@ -1,915 +0,0 @@
-From 1d7266d3b59be361763dab61f680103bbb70f3e9 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Mon, 19 Jul 2010 00:44:56 +0200
-Subject: [PATCH 2/9] mark Linux-specific configuration options
-
-PLATFORM_LINUX is used as a dependency for applets or features
-which require Linux-specific interfaces.
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- Config.in                     |   12 ++++++++++++
- console-tools/Config.src      |   13 +++++++++++++
- coreutils/Config.src          |    2 ++
- coreutils/date.c              |    2 +-
- e2fsprogs/Config.src          |    1 +
- init/Config.src               |    2 ++
- init/bootchartd.c             |    1 +
- libbb/Config.src              |    1 +
- loginutils/Config.src         |    3 +++
- miscutils/Config.src          |   19 ++++++++++++++++++-
- miscutils/conspy.c            |    1 +
- miscutils/ubi_attach_detach.c |    2 ++
- modutils/Config.src           |    1 +
- networking/Config.src         |   23 ++++++++++++++++++++++-
- networking/udhcp/Config.src   |    2 ++
- procps/Config.src             |    4 +++-
- shell/cttyhack.c              |    1 +
- sysklogd/Config.src           |    1 +
- util-linux/Config.src         |   27 +++++++++++++++++++++++++++
- 19 files changed, 114 insertions(+), 4 deletions(-)
-
-Index: busybox-1.17.1/Config.in
-===================================================================
---- busybox-1.17.1.orig/Config.in      2010-08-01 05:24:36.000000000 +0200
-+++ busybox-1.17.1/Config.in   2010-08-01 05:32:43.000000000 +0200
-@@ -47,6 +47,17 @@
-         compiler other than gcc.
-         If you do use gcc, this option may needlessly increase code size.
-+config PLATFORM_LINUX
-+      bool "Enable Linux-specific applets and features"
-+      default y
-+      help
-+        For the most part, busybox requires only POSIX compatibility
-+        from the target system, but some applets and features use
-+        Linux-specific interfaces.
-+
-+        Answering 'N' here will disable such applets and hide the
-+        corresponding configuration options.
-+
- choice
-       prompt "Buffer allocation policy"
-       default FEATURE_BUFFERS_USE_MALLOC
-@@ -353,6 +364,7 @@
- config SELINUX
-       bool "Support NSA Security Enhanced Linux"
-       default n
-+      depends on PLATFORM_LINUX
-       help
-         Enable support for SELinux in applets ls, ps, and id. Also provide
-         the option of compiling in SELinux applets.
-Index: busybox-1.17.1/console-tools/Config.src
-===================================================================
---- busybox-1.17.1.orig/console-tools/Config.src       2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/console-tools/Config.src    2010-08-01 05:32:43.000000000 +0200
-@@ -10,6 +10,7 @@
- config CHVT
-       bool "chvt"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program is used to change to another terminal.
-         Example: chvt 4 (change to terminal /dev/tty4)
-@@ -17,6 +18,7 @@
- config FGCONSOLE
-       bool "fgconsole"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program prints active (foreground) console number.
-@@ -29,12 +31,14 @@
- config DEALLOCVT
-       bool "deallocvt"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program deallocates unused virtual consoles.
- config DUMPKMAP
-       bool "dumpkmap"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program dumps the kernel's keyboard translation table to
-         stdout, in binary format. You can then use loadkmap to load it.
-@@ -42,18 +46,21 @@
- config KBD_MODE
-       bool "kbd_mode"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program reports and sets keyboard mode.
- config LOADFONT
-       bool "loadfont"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program loads a console font from standard input.
- config LOADKMAP
-       bool "loadkmap"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program loads a keyboard translation table from
-         standard input.
-@@ -61,6 +68,7 @@
- config OPENVT
-       bool "openvt"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program is used to start a command on an unused
-         virtual terminal.
-@@ -92,6 +100,7 @@
- config SETCONSOLE
-       bool "setconsole"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program redirects the system console to another device,
-         like the current tty while logged in via telnet.
-@@ -106,6 +115,7 @@
- config SETFONT
-       bool "setfont"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Allows to load console screen map. Useful for i18n.
-@@ -127,6 +137,7 @@
- config SETKEYCODES
-       bool "setkeycodes"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program loads entries into the kernel's scancode-to-keycode
-         map, allowing unusual keyboards to generate usable keycodes.
-@@ -134,12 +145,14 @@
- config SETLOGCONS
-       bool "setlogcons"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This program redirects the output console of kernel messages.
- config SHOWKEY
-       bool "showkey"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Shows keys pressed.
-Index: busybox-1.17.1/coreutils/Config.src
-===================================================================
---- busybox-1.17.1.orig/coreutils/Config.src   2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/coreutils/Config.src        2010-08-01 05:32:43.000000000 +0200
-@@ -591,6 +591,7 @@
- config STAT
-       bool "stat"
-       default y
-+      depends on PLATFORM_LINUX # statfs()
-       help
-         display file or filesystem status.
-@@ -606,6 +607,7 @@
- config STTY
-       bool "stty"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         stty is used to change and print terminal line settings.
-Index: busybox-1.17.1/coreutils/date.c
-===================================================================
---- busybox-1.17.1.orig/coreutils/date.c       2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/coreutils/date.c    2010-08-01 05:32:43.000000000 +0200
-@@ -72,7 +72,7 @@
- //config:config FEATURE_DATE_NANO
- //config:     bool "Support %[num]N nanosecond format specifier"
- //config:     default n
--//config:     depends on DATE
-+//config:     depends on DATE && PLATFORM_LINUX # syscall(__NR_clock_gettime)
- //config:     help
- //config:       Support %[num]N format specifier. Adds ~250 bytes of code.
- //config:
-Index: busybox-1.17.1/e2fsprogs/Config.src
-===================================================================
---- busybox-1.17.1.orig/e2fsprogs/Config.src   2010-07-25 00:12:56.000000000 +0200
-+++ busybox-1.17.1/e2fsprogs/Config.src        2010-08-01 05:32:43.000000000 +0200
-@@ -33,6 +33,7 @@
- config LSATTR
-       bool "lsattr"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         lsattr lists the file attributes on a second extended file system.
-Index: busybox-1.17.1/init/Config.src
-===================================================================
---- busybox-1.17.1.orig/init/Config.src        2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/init/Config.src     2010-08-01 05:32:43.000000000 +0200
-@@ -10,6 +10,7 @@
- config INIT
-       bool "init"
-       default y
-+      depends on PLATFORM_LINUX
-       select FEATURE_SYSLOG
-       help
-         init is the first program run when the system boots.
-@@ -92,6 +93,7 @@
- config HALT
-       bool "poweroff, halt, and reboot"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Stop all processes and either halt, reboot, or power off the system.
-Index: busybox-1.17.1/init/bootchartd.c
-===================================================================
---- busybox-1.17.1.orig/init/bootchartd.c      2010-07-25 00:12:56.000000000 +0200
-+++ busybox-1.17.1/init/bootchartd.c   2010-08-01 05:32:43.000000000 +0200
-@@ -6,6 +6,7 @@
- //config:config BOOTCHARTD
- //config:     bool "bootchartd"
- //config:     default y
-+//config:     depends on PLATFORM_LINUX
- //config:     help
- //config:       bootchartd is commonly used to profile the boot process
- //config:       for the purpose of speeding it up. In this case, it is started
-Index: busybox-1.17.1/libbb/Config.src
-===================================================================
---- busybox-1.17.1.orig/libbb/Config.src       2010-07-25 00:12:56.000000000 +0200
-+++ busybox-1.17.1/libbb/Config.src    2010-08-01 05:32:43.000000000 +0200
-@@ -153,6 +153,7 @@
- config MONOTONIC_SYSCALL
-       bool "Use clock_gettime(CLOCK_MONOTONIC) syscall"
-       default n
-+      depends on PLATFORM_LINUX
-       help
-         Use clock_gettime(CLOCK_MONOTONIC) syscall for measuring
-         time intervals (time, ping, traceroute etc need this).
-Index: busybox-1.17.1/loginutils/Config.src
-===================================================================
---- busybox-1.17.1.orig/loginutils/Config.src  2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/loginutils/Config.src       2010-08-01 05:32:43.000000000 +0200
-@@ -179,6 +179,7 @@
- config GETTY
-       bool "getty"
-       default y
-+      depends on PLATFORM_LINUX
-       select FEATURE_SYSLOG
-       help
-         getty lets you log in on a tty, it is normally invoked by init.
-@@ -186,6 +187,7 @@
- config LOGIN
-       bool "login"
-       default y
-+      depends on PLATFORM_LINUX
-       select FEATURE_SUID
-       select FEATURE_SYSLOG
-       help
-@@ -295,6 +297,7 @@
- config VLOCK
-       bool "vlock"
-       default y
-+      depends on PLATFORM_LINUX
-       select FEATURE_SUID
-       help
-         Build the "vlock" applet which allows you to lock (virtual) terminals.
-Index: busybox-1.17.1/miscutils/Config.src
-===================================================================
---- busybox-1.17.1.orig/miscutils/Config.src   2010-07-25 00:12:56.000000000 +0200
-+++ busybox-1.17.1/miscutils/Config.src        2010-08-01 05:32:43.000000000 +0200
-@@ -10,6 +10,7 @@
- config ADJTIMEX
-       bool "adjtimex"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Adjtimex reads and optionally sets adjustment parameters for
-         the Linux clock adjustment algorithm.
-@@ -24,6 +25,7 @@
- config BEEP
-       bool "beep"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The beep applets beeps in a given freq/Hz.
-@@ -180,6 +182,7 @@
- config DEVFSD
-       bool "devfsd (obsolete)"
-       default n
-+      depends on PLATFORM_LINUX
-       select FEATURE_SYSLOG
-       help
-         This is deprecated and should NOT be used anymore.
-@@ -223,6 +226,7 @@
- config FEATURE_DEVFS
-       bool "Use devfs names for all devices (obsolete)"
-       default n
-+      depends on PLATFORM_LINUX
-       help
-         This is obsolete and should NOT be used anymore.
-         Use linux >= 2.6 (optionally with hotplug) and mdev instead!
-@@ -242,6 +246,7 @@
- config EJECT
-       bool "eject"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Used to eject cdroms. (defaults to /dev/cdrom)
-@@ -256,6 +261,7 @@
- config FBSPLASH
-       bool "fbsplash"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Shows splash image and progress bar on framebuffer device.
-         Can be used during boot phase of an embedded device. ~2kb.
-@@ -305,6 +311,7 @@
- config IONICE
-       bool "ionice"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Set/set program io scheduling class and priority
-         Requires kernel >= 2.6.13
-@@ -344,6 +351,11 @@
- config LESS
-       bool "less"
-       default y
-+      depends on PLATFORM_LINUX
-+      depends on PLATFORM_LINUX
-+      depends on PLATFORM_LINUX
-+      depends on PLATFORM_LINUX
-+      depends on PLATFORM_LINUX
-       help
-         'less' is a pager, meaning that it displays text files. It possesses
-         a wide array of features, and is an improvement over 'more'.
-@@ -410,6 +422,7 @@
- config HDPARM
-       bool "hdparm"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Get/Set hard drive parameters. Primarily intended for ATA
-         drives. Adds about 13k (or around 30k if you enable the
-@@ -526,6 +539,7 @@
- config RAIDAUTORUN
-       bool "raidautorun"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         raidautorun tells the kernel md driver to
-         search and start RAID arrays.
-@@ -533,7 +547,7 @@
- config READAHEAD
-       bool "readahead"
-       default y
--      depends on LFS
-+      depends on LFS && PLATFORM_LINUX
-       help
-         Preload the files listed on the command line into RAM cache so that
-         subsequent reads on these files will not block on disk I/O.
-@@ -550,6 +564,7 @@
- config RFKILL
-       bool "rfkill"
-       default n  # doesn't build on Ubuntu 9.04
-+      depends on PLATFORM_LINUX
-       help
-         Enable/disable wireless devices.
-@@ -570,6 +585,7 @@
- config RX
-       bool "rx"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Receive files using the Xmodem protocol.
-@@ -641,6 +657,7 @@
- config WATCHDOG
-       bool "watchdog"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The watchdog utility is used with hardware or software watchdog
-         device drivers. It opens the specified watchdog device special file
-Index: busybox-1.17.1/miscutils/conspy.c
-===================================================================
---- busybox-1.17.1.orig/miscutils/conspy.c     2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/miscutils/conspy.c  2010-08-01 05:32:43.000000000 +0200
-@@ -17,6 +17,7 @@
- //config:config CONSPY
- //config:     bool "conspy"
- //config:     default n
-+//config:     depends on PLATFORM_LINUX
- //config:     help
- //config:       A text-mode VNC like program for Linux virtual terminals.
- //config:       example:  conspy NUM      shared access to console num
-Index: busybox-1.17.1/miscutils/ubi_attach_detach.c
-===================================================================
---- busybox-1.17.1.orig/miscutils/ubi_attach_detach.c  2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/miscutils/ubi_attach_detach.c       2010-08-01 05:32:43.000000000 +0200
-@@ -12,12 +12,14 @@
- //config:config UBIATTACH
- //config:     bool "ubiattach"
- //config:     default n
-+//config:     depends on PLATFORM_LINUX
- //config:     help
- //config:       Attach MTD device to an UBI device.
- //config:
- //config:config UBIDETACH
- //config:     bool "ubidetach"
- //config:     default n
-+//config:     depends on PLATFORM_LINUX
- //config:     help
- //config:       Detach MTD device from an UBI device.
-Index: busybox-1.17.1/modutils/Config.src
-===================================================================
---- busybox-1.17.1.orig/modutils/Config.src    2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/modutils/Config.src 2010-08-01 05:32:43.000000000 +0200
-@@ -4,6 +4,7 @@
- #
- menu "Linux Module Utilities"
-+depends on PLATFORM_LINUX
- INSERT
-Index: busybox-1.17.1/networking/Config.src
-===================================================================
---- busybox-1.17.1.orig/networking/Config.src  2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/networking/Config.src       2010-08-01 05:32:43.000000000 +0200
-@@ -43,6 +43,7 @@
- config VERBOSE_RESOLUTION_ERRORS
-       bool "Verbose resolution errors"
-       default n
-+      depends on PLATFORM_LINUX #because of xsocket() in libbb/xfuncs_prinf.c
-       help
-         Enable if you are not satisfied with simplistic
-         "can't resolve 'hostname.com'" and want to know more.
-@@ -51,18 +52,21 @@
- config ARP
-       bool "arp"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Manipulate the system ARP cache.
- config ARPING
-       bool "arping"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Ping hosts by ARP packets.
- config BRCTL
-       bool "brctl"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Manage ethernet bridges.
-         Supports addbr/delbr and addif/delif.
-@@ -95,6 +99,7 @@
- config ETHER_WAKE
-       bool "ether-wake"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Send a magic packet to wake up sleeping machines.
-@@ -269,6 +274,7 @@
- config IFCONFIG
-       bool "ifconfig"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Ifconfig is used to configure the kernel-resident network interfaces.
-@@ -316,6 +322,7 @@
- config IFENSLAVE
-       bool "ifenslave"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Userspace application to bind several interfaces
-         to a logical interface (use with kernel bonding driver).
-@@ -323,6 +330,7 @@
- config IFPLUGD
-       bool "ifplugd"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Network interface plug detection daemon.
-@@ -364,7 +372,7 @@
- config FEATURE_IFUPDOWN_IP_BUILTIN
-       bool "Use busybox ip applet"
-       default y
--      depends on FEATURE_IFUPDOWN_IP
-+      depends on FEATURE_IFUPDOWN_IP && PLATFORM_LINUX
-       select IP
-       select FEATURE_IP_ADDRESS
-       select FEATURE_IP_LINK
-@@ -483,6 +491,7 @@
- config IP
-       bool "ip"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The "ip" applet is a TCP/IP interface configuration and routing
-         utility. You generally don't need "ip" to use busybox with
-@@ -598,6 +607,7 @@
- config NAMEIF
-       bool "nameif"
-       default y
-+      depends on PLATFORM_LINUX
-       select FEATURE_SYSLOG
-       help
-         nameif is used to rename network interface by its MAC address.
-@@ -626,6 +636,7 @@
- config NETSTAT
-       bool "netstat"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         netstat prints information about the Linux networking subsystem.
-@@ -654,6 +665,7 @@
- config NTPD
-       bool "ntpd"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The NTP client/server daemon.
-@@ -668,6 +680,7 @@
- config PING
-       bool "ping"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to
-         elicit an ICMP ECHO_RESPONSE from a host or gateway.
-@@ -696,12 +709,14 @@
- config ROUTE
-       bool "route"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Route displays or manipulates the kernel's IP routing tables.
- config SLATTACH
-       bool "slattach"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         slattach is a small utility to attach network interfaces to serial
-         lines.
-@@ -719,6 +734,7 @@
- config TCPSVD
-       bool "tcpsvd"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         tcpsvd listens on a TCP port and runs a program for each new
-         connection.
-@@ -888,6 +904,7 @@
- config TRACEROUTE
-       bool "traceroute"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Utility to trace the route of IP packets.
-@@ -924,6 +941,7 @@
- config TUNCTL
-       bool "tunctl"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         tunctl creates or deletes tun devices.
-@@ -949,6 +967,7 @@
- config UDPSVD
-       bool "udpsvd"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         udpsvd listens on an UDP port and runs a program for each new
-         connection.
-@@ -956,6 +975,7 @@
- config VCONFIG
-       bool "vconfig"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Creates, removes, and configures VLAN interfaces
-@@ -990,6 +1010,7 @@
- config ZCIP
-       bool "zcip"
-       default y
-+      depends on PLATFORM_LINUX
-       select FEATURE_SYSLOG
-       help
-         ZCIP provides ZeroConf IPv4 address selection, according to RFC 3927.
-Index: busybox-1.17.1/networking/udhcp/Config.src
-===================================================================
---- busybox-1.17.1.orig/networking/udhcp/Config.src    2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/networking/udhcp/Config.src 2010-08-01 05:32:43.000000000 +0200
-@@ -8,6 +8,7 @@
- config UDHCPD
-       bool "udhcp server (udhcpd)"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         udhcpd is a DHCP server geared primarily toward embedded systems,
-         while striving to be fully functional and RFC compliant.
-@@ -51,6 +52,7 @@
- config UDHCPC
-       bool "udhcp client (udhcpc)"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         udhcpc is a DHCP client geared primarily toward embedded systems,
-         while striving to be fully functional and RFC compliant.
-Index: busybox-1.17.1/procps/Config.src
-===================================================================
---- busybox-1.17.1.orig/procps/Config.src      2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/procps/Config.src   2010-08-01 05:32:43.000000000 +0200
-@@ -10,6 +10,7 @@
- config FREE
-       bool "free"
-       default y
-+      depends on PLATFORM_LINUX #sysinfo()
-       help
-         free displays the total amount of free and used physical and swap
-         memory in the system, as well as the buffers used by the kernel.
-@@ -104,7 +105,7 @@
- config FEATURE_PS_TIME
-       bool "Enable time and elapsed time output"
-       default y
--      depends on PS && DESKTOP
-+      depends on PS && DESKTOP && PLATFORM_LINUX #sysinfo()
-       help
-         Support -o time and -o etime output specifiers.
-@@ -200,6 +201,7 @@
- config UPTIME
-       bool "uptime"
-       default y
-+      depends on PLATFORM_LINUX #sysinfo()
-       help
-         uptime gives a one line display of the current time, how long
-         the system has been running, how many users are currently logged
-Index: busybox-1.17.1/sysklogd/Config.src
-===================================================================
---- busybox-1.17.1.orig/sysklogd/Config.src    2010-07-25 00:12:43.000000000 +0200
-+++ busybox-1.17.1/sysklogd/Config.src 2010-08-01 05:32:43.000000000 +0200
-@@ -109,6 +109,7 @@
- config KLOGD
-       bool "klogd"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         klogd is a utility which intercepts and logs all
-         messages from the Linux kernel and sends the messages
-Index: busybox-1.17.1/util-linux/Config.src
-===================================================================
---- busybox-1.17.1.orig/util-linux/Config.src  2010-07-25 00:12:56.000000000 +0200
-+++ busybox-1.17.1/util-linux/Config.src       2010-08-01 05:32:43.000000000 +0200
-@@ -10,6 +10,7 @@
- config ACPID
-       bool "acpid"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         acpid listens to ACPI events coming either in textual form from
-         /proc/acpi/event (though it is marked deprecated it is still widely
-@@ -32,6 +33,7 @@
- config BLKID
-       bool "blkid"
-       default y
-+      depends on PLATFORM_LINUX
-       select VOLUMEID
-       help
-         Lists labels and UUIDs of all filesystems.
-@@ -41,6 +43,7 @@
- config DMESG
-       bool "dmesg"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         dmesg is used to examine or control the kernel ring buffer. When the
-         Linux kernel prints messages to the system log, they are stored in
-@@ -74,6 +77,7 @@
- config FBSET
-       bool "fbset"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         fbset is used to show or change the settings of a Linux frame buffer
-         device. The frame buffer device provides a simple and unique
-@@ -102,6 +106,7 @@
- config FDFLUSH
-       bool "fdflush"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         fdflush is only needed when changing media on slightly-broken
-         removable media drives. It is used to make Linux believe that a
-@@ -114,12 +119,14 @@
- config FDFORMAT
-       bool "fdformat"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         fdformat is used to low-level format a floppy disk.
- config FDISK
-       bool "fdisk"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The fdisk utility is used to divide hard disks into one or more
-         logical disks, which are generally called partitions. This utility
-@@ -187,6 +194,7 @@
- config FINDFS
-       bool "findfs"
-       default y
-+      depends on PLATFORM_LINUX
-       select VOLUMEID
-       help
-         Prints the name of a filesystem with given label or UUID.
-@@ -202,6 +210,7 @@
- config FREERAMDISK
-       bool "freeramdisk"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Linux allows you to create ramdisks. This utility allows you to
-         delete them and completely free all memory that was used for the
-@@ -224,12 +233,14 @@
- config MKFS_EXT2
-       bool "mkfs_ext2"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Utility to create EXT2 filesystems.
- config MKFS_MINIX
-       bool "mkfs_minix"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The minix filesystem is a nice, small, compact, read-write filesystem
-         with little overhead. If you wish to be able to create minix
-@@ -247,6 +258,7 @@
- config MKFS_REISER
-       bool "mkfs_reiser"
-       default n
-+      depends on PLATFORM_LINUX
-       help
-         Utility to create ReiserFS filesystems.
-         Note: this applet needs a lot of testing and polishing.
-@@ -254,6 +266,7 @@
- config MKFS_VFAT
-       bool "mkfs_vfat"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Utility to create FAT32 filesystems.
-@@ -302,6 +315,7 @@
- config HWCLOCK
-       bool "hwclock"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The hwclock utility is used to read and set the hardware clock
-         on a system. This is primarily used to set the current time on
-@@ -341,6 +355,7 @@
- config IPCS
-       bool "ipcs"
-       default y
-+      depends on PLATFORM_LINUX
-       select FEATURE_SUID
-       help
-         The ipcs utility is used to provide information on the currently
-@@ -349,6 +364,7 @@
- config LOSETUP
-       bool "losetup"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         losetup is used to associate or detach a loop device with a regular
-         file or block device, and to query the status of a loop device. This
-@@ -357,6 +373,7 @@
- config LSPCI
-       bool "lspci"
-       default y
-+      #depends on PLATFORM_LINUX
-       help
-         lspci is a utility for displaying information about PCI buses in the
-         system and devices connected to them.
-@@ -366,6 +383,7 @@
- config LSUSB
-       bool "lsusb"
-       default y
-+      #depends on PLATFORM_LINUX
-       help
-         lsusb is a utility for displaying information about USB buses in the
-         system and devices connected to them.
-@@ -375,6 +393,7 @@
- config MDEV
-       bool "mdev"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         mdev is a mini-udev implementation for dynamically creating device
-         nodes in the /dev directory.
-@@ -473,6 +492,7 @@
- config MOUNT
-       bool "mount"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         All files and filesystems in Unix are arranged into one big directory
-         tree. The 'mount' utility is used to graft a filesystem onto a
-@@ -555,6 +575,7 @@
- config PIVOT_ROOT
-       bool "pivot_root"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The pivot_root utility swaps the mount points for the root filesystem
-         with some other mounted filesystem. This allows you to do all sorts
-@@ -582,12 +603,14 @@
- config READPROFILE
-       bool "readprofile"
-       default y
-+      #depends on PLATFORM_LINUX
-       help
-         This allows you to parse /proc/profile for basic profiling.
- config RTCWAKE
-       bool "rtcwake"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         Enter a system sleep state until specified wakeup time.
-@@ -607,6 +630,7 @@
- config SETARCH
-       bool "setarch"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The linux32 utility is used to create a 32bit environment for the
-         specified program (usually a shell). It only makes sense to have
-@@ -616,6 +640,7 @@
- config SWAPONOFF
-       bool "swaponoff"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         This option enables both the 'swapon' and the 'swapoff' utilities.
-         Once you have created some swap space using 'mkswap', you also need
-@@ -634,6 +659,7 @@
- config SWITCH_ROOT
-       bool "switch_root"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         The switch_root utility is used from initramfs to select a new
-         root device. Under initramfs, you have to use this instead of
-@@ -653,6 +679,7 @@
- config UMOUNT
-       bool "umount"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         When you want to remove a mounted filesystem from its current mount
-         point, for example when you are shutting down the system, the
-Index: busybox-1.17.1/shell/Config.src
-===================================================================
---- busybox-1.17.1.orig/shell/Config.src       2010-08-01 05:33:24.000000000 +0200
-+++ busybox-1.17.1/shell/Config.src    2010-08-01 05:33:34.000000000 +0200
-@@ -370,6 +370,7 @@
- config CTTYHACK
-       bool "cttyhack"
-       default y
-+      depends on PLATFORM_LINUX
-       help
-         One common problem reported on the mailing list is "can't access tty;
-         job control turned off" error message which typically appears when
diff --git a/packaging/mkdir-fix-p-on-FreeBSD.patch b/packaging/mkdir-fix-p-on-FreeBSD.patch
deleted file mode 100644 (file)
index fc5bf7f..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-From 84b01d5afc8230c79a1b8469c222d940c0d4e792 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:46:33 +0200
-Subject: [PATCH 7/9] mkdir: fix -p on FreeBSD
-
-This patch is libbb.make_directory.diff from Debian kFreeBSD at:
-http://svn.debian.org/viewsvn/d-i/people/slackydeb/kfreebsd/busybox/1.14/debian
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- libbb/make_directory.c |    2 +-
- 1 files changed, 1 insertions(+), 1 deletions(-)
-
-diff --git a/libbb/make_directory.c b/libbb/make_directory.c
-index 4486eb1..6dd04cf 100644
---- a/libbb/make_directory.c
-+++ b/libbb/make_directory.c
-@@ -86,7 +86,7 @@ int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
-               if (mkdir(path, 0777) < 0) {
-                       /* If we failed for any other reason than the directory
-                        * already exists, output a diagnostic and return -1 */
--                      if (errno != EEXIST
-+                      if ((errno != EEXIST && errno != EISDIR)
-                        || !(flags & FILEUTILS_RECUR)
-                        || ((stat(path, &st) < 0) || !S_ISDIR(st.st_mode))
-                       ) {
--- 
-1.7.1
-
diff --git a/packaging/readlink-use-xmalloc_realpath.patch b/packaging/readlink-use-xmalloc_realpath.patch
deleted file mode 100644 (file)
index e1103ec..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-From b175462422f02a159a14dc5561d8bef6f84b2b66 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:32:19 +0200
-Subject: [PATCH 1/9] readlink: use xmalloc_realpath()
-
-Using realpath() directly with a non-NULL output buffer is unsafe because its
-behavior is unspecified on systems which don't have PATH_MAX (ie. Hurd)
-
-I beleive this also fixes a small bug whereby 'buf' would not be freed
-on 'readlink -v' with ENABLE_FEATURE_CLEANUP.
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- coreutils/readlink.c |    5 ++---
- 1 files changed, 2 insertions(+), 3 deletions(-)
-
-diff --git a/coreutils/readlink.c b/coreutils/readlink.c
-index 20df38b..2ed5e2c 100644
---- a/coreutils/readlink.c
-+++ b/coreutils/readlink.c
-@@ -36,7 +36,6 @@ int readlink_main(int argc UNUSED_PARAM, char **argv)
- {
-       char *buf;
-       char *fname;
--      char pathbuf[PATH_MAX];
-       IF_FEATURE_READLINK_FOLLOW(
-               unsigned opt;
-@@ -56,7 +55,7 @@ int readlink_main(int argc UNUSED_PARAM, char **argv)
-               logmode = LOGMODE_NONE;
-       if (opt & 1) { /* -f */
--              buf = realpath(fname, pathbuf);
-+              buf = xmalloc_realpath(fname);
-       } else {
-               buf = xmalloc_readlink_or_warn(fname);
-       }
-@@ -65,7 +64,7 @@ int readlink_main(int argc UNUSED_PARAM, char **argv)
-               return EXIT_FAILURE;
-       printf((opt & 2) ? "%s" : "%s\n", buf);
--      if (ENABLE_FEATURE_CLEAN_UP && !opt)
-+      if (ENABLE_FEATURE_CLEAN_UP)
-               free(buf);
-       fflush_stdout_and_exit(EXIT_SUCCESS);
--- 
-1.7.1
-
diff --git a/packaging/sbin.links b/packaging/sbin.links
deleted file mode 100644 (file)
index a56bbea..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-blkid
-blockdev
-depmod
-fbsplash
-fdisk
-fsck.minix
-getty
-hdparm
-hwclock
-ifconfig
-ifdown
-ifenslave
-ifup
-inotifyd
-insmod
-ip
-iptunnel
-klogd
-loadkmap
-logread
-losetup
-lsmod
-makedevs
-mdev
-mkdosfs
-mkfs.minix
-mkfs.vfat
-mkswap
-modinfo
-modprobe
-nameif
-pivot_root
-raidautorun
-rmmod
-route
-setconsole
-slattach
-swapoff
-swapon
-switch_root
-sysctl
-syslogd
-vconfig
diff --git a/packaging/shell-ash-export-HOME.patch b/packaging/shell-ash-export-HOME.patch
deleted file mode 100644 (file)
index 0d3c5ab..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/shell/ash.c
-+++ b/shell/ash.c
-@@ -1774,7 +1774,7 @@
-       { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL"      , changemail      },
-       { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH"  , changemail      },
- #endif
--      { VSTRFIXED|VTEXTFIXED       , bb_PATH_root_path, changepath },
-+      { VSTRFIXED|VTEXTFIXED|VEXPORT, bb_PATH_root_path, changepath },
-       { VSTRFIXED|VTEXTFIXED       , "PS1=$ "    , NULL            },
-       { VSTRFIXED|VTEXTFIXED       , "PS2=> "    , NULL            },
-       { VSTRFIXED|VTEXTFIXED       , "PS4=+ "    , NULL            },
diff --git a/packaging/shell-hist.patch b/packaging/shell-hist.patch
deleted file mode 100644 (file)
index 889785a..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
---- a/libbb/lineedit.c 2009-03-30 17:15:10.000000000 +0300
-+++ b/libbb/lineedit.c 2009-03-31 17:52:48.000000000 +0300
-@@ -959,49 +959,48 @@
- static void load_history(const char *fromfile)
- {
-       FILE *fp;
--      int hi;
-+      int hi = 0;
--      /* cleanup old */
--      for (hi = state->cnt_history; hi > 0;) {
--              hi--;
--              free(state->history[hi]);
--      }
--
--      fp = fopen(fromfile, "r");
--      if (fp) {
--              for (hi = 0; hi < MAX_HISTORY;) {
--                      char *hl = xmalloc_getline(fp);
--                      int l;
--
--                      if (!hl)
--                              break;
--                      l = strlen(hl);
--                      if (l >= MAX_LINELEN)
--                              hl[MAX_LINELEN-1] = '\0';
--                      if (l == 0 || hl[0] == ' ') {
--                              free(hl);
--                              continue;
-+      if (!state->cnt_history) {
-+              fp = fopen(fromfile, "r");
-+              if (fp) {
-+                      for (hi = 0; hi < MAX_HISTORY;) {
-+                              char *hl = xmalloc_getline(fp);
-+                              int l;
-+
-+                              if (!hl)
-+                                      break;
-+                              l = strlen(hl);
-+                              if (l >= MAX_LINELEN)
-+                                      hl[MAX_LINELEN-1] = '\0';
-+                              if (l == 0 || hl[0] == ' ') {
-+                                      free(hl);
-+                                      continue;
-+                              }
-+                              state->history[hi++] = hl;
-                       }
--                      state->history[hi++] = hl;
-+                      fclose(fp);
-               }
--              fclose(fp);
-+              state->cur_history = state->cnt_history = hi;
-       }
--      state->cur_history = state->cnt_history = hi;
- }
- /* state->flags is already checked to be nonzero */
--static void save_history(const char *tofile)
-+void save_history(line_input_t *);
-+void save_history(line_input_t *st)
- {
-       FILE *fp;
--      fp = fopen(tofile, "w");
--      if (fp) {
--              int i;
-+      if (st->cnt_history) {
-+              fp = fopen(st->hist_file, "w");
-+              if (fp) {
-+                      int i;
--              for (i = 0; i < state->cnt_history; i++) {
--                      fprintf(fp, "%s\n", state->history[i]);
-+                      for (i = 0; i < st->cnt_history; i++) {
-+                              fprintf(fp, "%s\n", st->history[i]);
-+                      }
-+                      fclose(fp);
-               }
--              fclose(fp);
-       }
- }
- #else
-@@ -1030,10 +1029,6 @@
-       state->history[i++] = xstrdup(str);
-       state->cur_history = i;
-       state->cnt_history = i;
--#if ENABLE_FEATURE_EDITING_SAVEHISTORY
--      if ((state->flags & SAVE_HISTORY) && state->hist_file)
--              save_history(state->hist_file);
--#endif
-       USE_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;)
- }
-diff -r -u busybox-1.10.2.legal.orig/shell/ash.c busybox-1.10.2.legal/shell/ash.c
---- busybox-1.10.2.legal.orig/shell/ash.c      2008-07-24 10:16:00.000000000 +0100
-+++ busybox-1.10.2.legal/shell/ash.c   2009-03-23 17:09:11.000000000 +0000
-@@ -1733,6 +1733,9 @@
- #if ENABLE_ASH_RANDOM_SUPPORT
- static void change_random(const char *);
- #endif
-+#if ENABLE_FEATURE_EDITING_SAVEHISTORY
-+void save_history(line_input_t *);
-+#endif
- static const struct {
-       int flags;
-@@ -12815,6 +12818,10 @@
-       char *p;
-       int status;
-+      if (iflag && (line_input_state->flags & SAVE_HISTORY)
-+          && line_input_state->hist_file && !shlvl) {
-+              save_history(line_input_state);
-+      }
-       status = exitstatus;
-       TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
-       if (setjmp(loc.loc)) {
diff --git a/packaging/smack-busybox-1.17.1.patch b/packaging/smack-busybox-1.17.1.patch
deleted file mode 100644 (file)
index a95697f..0000000
+++ /dev/null
@@ -1,1604 +0,0 @@
-
-Smack updates for busybox-1.17.1
-
----
- Config.in             |    9 ++
- Makefile              |    1 
- coreutils/id.c        |   26 +++++-
- coreutils/ls.c        |   28 ++++++
- coreutils/stat.c      |   87 ++++++++++++++++----
- findutils/find.c      |   31 +++++++
- include/applets.src.h |    4 
- include/libbb.h       |   21 ++++
- include/usage.src.h   |   30 ++++++-
- libbb/Kbuild.src      |    1 
- libbb/messages.c      |    3 
- libbb/procps.c        |    6 +
- libbb/smack_common.c  |  178 ++++++++++++++++++++++++++++++++++++++++++
- loginutils/adduser.c  |   13 ++-
- loginutils/login.c    |    8 +
- loginutils/su.c       |    8 +
- loginutils/sulogin.c  |    6 +
- miscutils/crond.c     |   11 ++
- miscutils/crontab.c   |   13 +++
- procps/ps.c           |   48 +++++++++--
- smack/Config.src      |   40 +++++++++
- smack/Kbuild.src      |   14 +++
- smack/newsmack.c      |   61 ++++++++++++++
- smack/smackcipso.c    |  119 ++++++++++++++++++++++++++++
- smack/smackenabled.c  |   17 ++++
- smack/smackload.c     |   93 +++++++++++++++++++++
- 26 files changed, 844 insertions(+), 32 deletions(-)
-
-diff -uprN busybox-1.17.1/Config.in busybox-1.17.1-smack/Config.in
---- busybox-1.17.1/Config.in   2010-07-24 15:12:56.000000000 -0700
-+++ busybox-1.17.1-smack/Config.in     2011-08-17 15:27:12.889107702 -0700
-@@ -370,6 +370,14 @@ config SELINUX
-         Most people will leave this set to 'N'.
-+config SMACK
-+      bool "Support Smack"
-+      default n
-+      help
-+        Enable support for Smack in applets ls, ps, and id.  Also provide
-+        the option of compiling in Smack applets.
-+
-+
- config FEATURE_PREFER_APPLETS
-       bool "exec prefers applets"
-       default n
-@@ -739,4 +747,5 @@ source procps/Config.in
- source runit/Config.in
- source selinux/Config.in
- source shell/Config.in
-+source smack/Config.in
- source sysklogd/Config.in
-diff -uprN busybox-1.17.1/coreutils/id.c busybox-1.17.1-smack/coreutils/id.c
---- busybox-1.17.1/coreutils/id.c      2010-07-05 19:25:53.000000000 -0700
-+++ busybox-1.17.1-smack/coreutils/id.c        2011-08-17 15:27:12.889107702 -0700
-@@ -34,6 +34,9 @@ enum {
- #if ENABLE_SELINUX
-       JUST_CONTEXT    = (1 << 5),
- #endif
-+#if ENABLE_SMACK
-+      JUST_LABEL      = (1 << 5),
-+#endif
- };
- static int print_common(unsigned id, const char *name, const char *prefix)
-@@ -116,11 +119,15 @@ int id_main(int argc UNUSED_PARAM, char
- #if ENABLE_SELINUX
-       security_context_t scontext = NULL;
- #endif
-+#if ENABLE_SMACK
-+      char smack[SMACKBUFFSIZE];
-+#endif
-       /* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/
-       /* Don't allow more than one username */
-       opt_complementary = "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG"
--                       IF_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G");
--      opt = getopt32(argv, "rnugG" IF_SELINUX("Z"));
-+                       IF_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G")
-+                       IF_SMACK(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G");
-+      opt = getopt32(argv, "rnugG" IF_SELINUX("Z") IF_SMACK("Z"));
-       username = argv[optind];
-       if (username) {
-@@ -187,6 +194,10 @@ int id_main(int argc UNUSED_PARAM, char
-                               printf(" context=%s", scontext);
-               }
- #endif
-+#if ENABLE_SMACK
-+              if (smack_from_proc(-1, smack, sizeof(smack)) == 0)
-+                      printf(" label=%s", smack);
-+#endif
-       } else if (opt & PRINT_REAL) {
-               euid = ruid;
-               egid = rgid;
-@@ -205,6 +216,17 @@ int id_main(int argc UNUSED_PARAM, char
-               }
-               fputs(scontext, stdout);
-       }
-+#endif
-+#if ENABLE_SMACK
-+      else if (opt & JUST_LABEL) {
-+              if (username || smack_from_proc(-1, smack, sizeof(smack)) < 0) {
-+                      bb_error_msg_and_die("can't get process label%s",
-+                              username ? " for a different user" : "");
-+              }
-+              fputs(smack, stdout);
-+      }
-+#endif
-+#if ENABLE_SELINUX
-       /* freecon(NULL) seems to be harmless */
-       if (ENABLE_FEATURE_CLEAN_UP)
-               freecon(scontext);
-diff -uprN busybox-1.17.1/coreutils/ls.c busybox-1.17.1-smack/coreutils/ls.c
---- busybox-1.17.1/coreutils/ls.c      2010-07-05 19:25:53.000000000 -0700
-+++ busybox-1.17.1-smack/coreutils/ls.c        2011-08-17 16:23:44.839147638 -0700
-@@ -141,6 +141,7 @@ static const char ls_options[] ALIGN1 =
-       IF_FEATURE_LS_RECURSIVE("R")     /* 1, 25 */
-       IF_FEATURE_HUMAN_READABLE("h")   /* 1, 26 */
-       IF_SELINUX("KZ") /* 2, 28 */
-+      IF_SMACK("KZ") /* 2, 28 */
-       IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */
-       ;
- enum {
-@@ -165,6 +166,7 @@ enum {
-               + 1 * ENABLE_FEATURE_LS_RECURSIVE
-               + 1 * ENABLE_FEATURE_HUMAN_READABLE
-               + 2 * ENABLE_SELINUX
-+              + 2 * ENABLE_SMACK
-               + 2 * ENABLE_FEATURE_AUTOWIDTH,
-       OPT_color = 1 << OPTBIT_color,
- };
-@@ -191,6 +193,7 @@ static const unsigned opt_flags[] = {
-       0,                          /* Q (quote filename) - handled via OPT_Q */
-       DISP_HIDDEN,                /* A */
-       ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
-+      ENABLE_SMACK * LIST_CONTEXT, /* k (ignored if !SMACK) */
- #if ENABLE_FEATURE_LS_TIMESTAMPS
-       TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME),   /* c */
-       LIST_FULLTIME,              /* e */
-@@ -216,10 +219,10 @@ static const unsigned opt_flags[] = {
- #if ENABLE_FEATURE_HUMAN_READABLE
-       LS_DISP_HR,                 /* h */
- #endif
--#if ENABLE_SELINUX
-+#if ENABLE_SELINUX || ENABLE_SMACK
-       LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
- #endif
--#if ENABLE_SELINUX
-+#if ENABLE_SELINUX || ENABLE_SMACK
-       LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
- #endif
-       (1U<<31)
-@@ -239,6 +242,7 @@ struct dnode {
-       smallint fname_allocated;
-       struct stat dstat;      /* the file stat info */
-       IF_SELINUX(security_context_t sid;)
-+      IF_SMACK(char xattr[SMACKBUFFSIZE];)
- };
- struct globals {
-@@ -288,6 +292,10 @@ static struct dnode *my_stat(const char
-       struct stat dstat;
-       struct dnode *cur;
-       IF_SELINUX(security_context_t sid = NULL;)
-+#if ENABLE_SMACK
-+      char xattr[SMACKBUFFSIZE];
-+      int i;
-+#endif
-       if ((all_fmt & FOLLOW_LINKS) || force_follow) {
- #if ENABLE_SELINUX
-@@ -295,6 +303,11 @@ static struct dnode *my_stat(const char
-                        getfilecon(fullname, &sid);
-               }
- #endif
-+#if ENABLE_SMACK
-+              i = smack_from_file(fullname, xattr, sizeof(xattr), 1);
-+              if (i < 0)
-+                      xattr[0] = '\0';
-+#endif
-               if (stat(fullname, &dstat)) {
-                       bb_simple_perror_msg(fullname);
-                       exit_code = EXIT_FAILURE;
-@@ -306,6 +319,11 @@ static struct dnode *my_stat(const char
-                       lgetfilecon(fullname, &sid);
-               }
- #endif
-+#if ENABLE_SMACK
-+              i = smack_from_file(fullname, xattr, sizeof(xattr), 0);
-+              if (i < 0)
-+                      xattr[0] = '\0';
-+#endif
-               if (lstat(fullname, &dstat)) {
-                       bb_simple_perror_msg(fullname);
-                       exit_code = EXIT_FAILURE;
-@@ -318,6 +336,7 @@ static struct dnode *my_stat(const char
-       cur->name = name;
-       cur->dstat = dstat;
-       IF_SELINUX(cur->sid = sid;)
-+      IF_SMACK(strncpy(cur->xattr, xattr, sizeof(xattr));)
-       return cur;
- }
-@@ -685,6 +704,10 @@ static NOINLINE unsigned list_single(con
-               freecon(dn->sid);
-       }
- #endif
-+#if ENABLE_SMACK
-+      if (all_fmt & LIST_CONTEXT)
-+              column += printf("%-23s ", dn->xattr);
-+#endif
-       if (all_fmt & LIST_FILENAME) {
- #if ENABLE_FEATURE_LS_COLOR
-               if (show_color) {
-@@ -753,6 +776,7 @@ static void showfiles(struct dnode **dn,
-               }
-               column_width += tabstops +
-                       IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
-+                      IF_SMACK( ((all_fmt & LIST_CONTEXT) ? 24 : 0) + )
-                               ((all_fmt & LIST_INO) ? 8 : 0) +
-                               ((all_fmt & LIST_BLOCKS) ? 5 : 0);
-               ncols = (int) (terminal_width / column_width);
-diff -uprN busybox-1.17.1/coreutils/stat.c busybox-1.17.1-smack/coreutils/stat.c
---- busybox-1.17.1/coreutils/stat.c    2010-07-05 19:25:53.000000000 -0700
-+++ busybox-1.17.1-smack/coreutils/stat.c      2011-08-18 17:01:04.060191320 -0700
-@@ -18,6 +18,7 @@
- #define OPT_TERSE       (1 << 1)
- #define OPT_DEREFERENCE (1 << 2)
- #define OPT_SELINUX     (1 << 3)
-+#define OPT_SMACK       (1 << 4)
- #if ENABLE_FEATURE_STAT_FORMAT
- typedef bool (*statfunc_ptr)(const char *, const char *);
-@@ -154,7 +155,8 @@ static void printfs(char *pformat, const
- /* print statfs info */
- static void FAST_FUNC print_statfs(char *pformat, const char m,
-               const char *const filename, const void *data
--              IF_SELINUX(, security_context_t scontext))
-+              IF_SELINUX(, security_context_t scontext)
-+              IF_SMACK(, char *smack))
- {
-       const struct statfs *statfsbuf = data;
-       if (m == 'n') {
-@@ -192,6 +194,10 @@ static void FAST_FUNC print_statfs(char
-       } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
-               printfs(pformat, scontext);
- # endif
-+#if ENABLE_SMACK
-+      } else if (m == 'C' && (option_mask32 & OPT_SMACK)) {
-+              printfs(pformat, smack);
-+#endif
-       } else {
-               strcatc(pformat, 'c');
-               printf(pformat, m);
-@@ -201,7 +207,8 @@ static void FAST_FUNC print_statfs(char
- /* print stat info */
- static void FAST_FUNC print_stat(char *pformat, const char m,
-               const char *const filename, const void *data
--              IF_SELINUX(, security_context_t scontext))
-+              IF_SELINUX(, security_context_t scontext)
-+              IF_SMACK(, char *smack))
- {
- #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
-       struct stat *statbuf = (struct stat *) data;
-@@ -296,6 +303,10 @@ static void FAST_FUNC print_stat(char *p
-       } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
-               printfs(pformat, scontext);
- # endif
-+#if ENABLE_SMACK
-+      } else if (m == 'C' && (option_mask32 & OPT_SMACK)) {
-+              printfs(pformat, smack);
-+#endif
-       } else {
-               strcatc(pformat, 'c');
-               printf(pformat, m);
-@@ -304,9 +315,10 @@ static void FAST_FUNC print_stat(char *p
- static void print_it(const char *masterformat,
-               const char *filename,
--              void FAST_FUNC (*print_func)(char*, char, const char*, const void* IF_SELINUX(, security_context_t scontext)),
-+              void FAST_FUNC (*print_func)(char*, char, const char*, const void* IF_SELINUX(, security_context_t scontext) IF_SMACK(, char *smack)),
-               const void *data
--              IF_SELINUX(, security_context_t scontext))
-+              IF_SELINUX(, security_context_t scontext)
-+              IF_SMACK(, char *smack) )
- {
-       /* Create a working copy of the format string */
-       char *format = xstrdup(masterformat);
-@@ -349,7 +361,7 @@ static void print_it(const char *masterf
-                       break;
-               default:
-                       /* Completes "%<modifiers>" with specifier and printfs */
--                      print_func(dest, *p, filename, data IF_SELINUX(,scontext));
-+                      print_func(dest, *p, filename, data IF_SELINUX(,scontext) IF_SMACK(,smack) );
-                       break;
-               }
-       }
-@@ -383,6 +395,18 @@ static bool do_statfs(const char *filena
-               }
-       }
- #endif
-+#if ENABLE_SMACK
-+      char smack[SMACKBUFFSIZE];
-+
-+      if (option_mask32 & OPT_SMACK) {
-+              int i = option_mask32 & OPT_DEREFERENCE;
-+
-+              if (smack_from_file(filename, smack, sizeof(smack), i) < 0) {
-+                      bb_perror_msg("%s", filename);
-+                      return 0;
-+              }
-+      }
-+#endif
-       if (statfs(filename, &statfsbuf) != 0) {
-               bb_perror_msg("can't read file system information for '%s'", filename);
-               return 0;
-@@ -390,7 +414,7 @@ static bool do_statfs(const char *filena
- #if ENABLE_FEATURE_STAT_FORMAT
-       if (format == NULL) {
--# if !ENABLE_SELINUX
-+# if !ENABLE_SELINUX && !ENABLE_SMACK
-               format = (option_mask32 & OPT_TERSE
-                       ? "%n %i %l %t %s %b %f %a %c %d\n"
-                       : "  File: \"%n\"\n"
-@@ -400,9 +424,9 @@ static bool do_statfs(const char *filena
-                         "Inodes: Total: %-10c Free: %d");
- # else
-               format = (option_mask32 & OPT_TERSE
--                      ? (option_mask32 & OPT_SELINUX ? "%n %i %l %t %s %b %f %a %c %d %C\n":
-+                      ? (option_mask32 & (OPT_SELINUX | OPT_SMACK) ? "%n %i %l %t %s %b %f %a %c %d %C\n":
-                       "%n %i %l %t %s %b %f %a %c %d\n")
--                      : (option_mask32 & OPT_SELINUX ?
-+                      : (option_mask32 & (OPT_SELINUX | OPT_SMACK) ?
-                       "  File: \"%n\"\n"
-                       "    ID: %-8i Namelen: %-7l Type: %T\n"
-                       "Block size: %-10s\n"
-@@ -415,9 +439,9 @@ static bool do_statfs(const char *filena
-                       "Blocks: Total: %-10b Free: %-10f Available: %a\n"
-                       "Inodes: Total: %-10c Free: %d\n")
-                       );
--# endif /* SELINUX */
-+# endif /* SELINUX or Smack */
-       }
--      print_it(format, filename, print_statfs, &statfsbuf IF_SELINUX(, scontext));
-+      print_it(format, filename, print_statfs, &statfsbuf IF_SELINUX(, scontext) IF_SMACK(, smack) );
- #else /* FEATURE_STAT_FORMAT */
-       format = (option_mask32 & OPT_TERSE
-               ? "%s %llx %lu "
-@@ -433,7 +457,7 @@ static bool do_statfs(const char *filena
-       else
-               printf("Type: %s\n", human_fstype(statfsbuf.f_type));
--# if !ENABLE_SELINUX
-+# if !ENABLE_SELINUX && !ENABLE_SMACK
-       format = (option_mask32 & OPT_TERSE
-               ? "%lu %llu %llu %llu %llu %llu\n"
-               : "Block size: %-10lu\n"
-@@ -448,8 +472,8 @@ static bool do_statfs(const char *filena
-              (unsigned long long) statfsbuf.f_ffree);
- # else
-       format = (option_mask32 & OPT_TERSE
--              ? (option_mask32 & OPT_SELINUX ? "%lu %llu %llu %llu %llu %llu %C\n" : "%lu %llu %llu %llu %llu %llu\n")
--              : (option_mask32 & OPT_SELINUX
-+              ? (option_mask32 & (OPT_SELINUX | OPT_SMACK) ? "%lu %llu %llu %llu %llu %llu %C\n" : "%lu %llu %llu %llu %llu %llu\n")
-+              : (option_mask32 & (OPT_SELINUX | OPT_SMACK)
-                       ?       "Block size: %-10lu\n"
-                               "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n"
-                               "Inodes: Total: %-10llu Free: %llu"
-@@ -496,6 +520,18 @@ static bool do_stat(const char *filename
-               }
-       }
- #endif
-+#if ENABLE_SMACK
-+      char smack[SMACKBUFFSIZE];
-+
-+      if (option_mask32 & OPT_SMACK) {
-+              int i = option_mask32 & OPT_DEREFERENCE;
-+
-+              if (smack_from_file(filename, smack, sizeof(smack), i) < 0) {
-+                      bb_perror_msg("%s", filename);
-+                      return 0;
-+              }
-+      }
-+#endif
-       if ((option_mask32 & OPT_DEREFERENCE ? stat : lstat) (filename, &statbuf) != 0) {
-               bb_perror_msg("can't stat '%s'", filename);
-               return 0;
-@@ -503,7 +539,7 @@ static bool do_stat(const char *filename
- #if ENABLE_FEATURE_STAT_FORMAT
-       if (format == NULL) {
--# if !ENABLE_SELINUX
-+#if !ENABLE_SELINUX && !ENABLE_SMACK
-               if (option_mask32 & OPT_TERSE) {
-                       format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o";
-               } else {
-@@ -526,12 +562,12 @@ static bool do_stat(const char *filename
-               }
- # else
-               if (option_mask32 & OPT_TERSE) {
--                      format = (option_mask32 & OPT_SELINUX ?
-+                      format = (option_mask32 & (OPT_SELINUX | OPT_SMACK) ?
-                                 "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C\n":
-                                 "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n");
-               } else {
-                       if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
--                              format = (option_mask32 & OPT_SELINUX ?
-+                              format = (option_mask32 & (OPT_SELINUX | OPT_SMACK) ?
-                                         "  File: %N\n"
-                                         "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
-                                         "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
-@@ -546,7 +582,7 @@ static bool do_stat(const char *filename
-                                         "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
-                                         "Access: %x\n" "Modify: %y\n" "Change: %z\n");
-                       } else {
--                              format = (option_mask32 & OPT_SELINUX ?
-+                              format = (option_mask32 & (OPT_SELINUX | OPT_SMACK) ?
-                                         "  File: %N\n"
-                                         "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
-                                         "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
-@@ -562,11 +598,14 @@ static bool do_stat(const char *filename
-               }
- # endif
-       }
--      print_it(format, filename, print_stat, &statbuf IF_SELINUX(, scontext));
-+      print_it(format, filename, print_stat, &statbuf IF_SELINUX(, scontext) IF_SMACK(, smack) );
- #else /* FEATURE_STAT_FORMAT */
-       if (option_mask32 & OPT_TERSE) {
-+#if !ENABLE_SELINUX && !ENABLE_SMACK
-+              printf("%s %llu %llu %lx %lu %lu %llx %llu %lu %lx %lx %lu %lu %lu %lu\n"
-+#else
-               printf("%s %llu %llu %lx %lu %lu %llx %llu %lu %lx %lx %lu %lu %lu %lu"
--                     IF_NOT_SELINUX("\n"),
-+#endif
-                      filename,
-                      (unsigned long long) statbuf.st_size,
-                      (unsigned long long) statbuf.st_blocks,
-@@ -589,6 +628,12 @@ static bool do_stat(const char *filename
-               else
-                       bb_putchar('\n');
- # endif
-+#if ENABLE_SMACK
-+              if (option_mask32 & OPT_SMACK)
-+                      printf(" %lc\n", smack);
-+              else
-+                      bb_putchar('\n');
-+#endif
-       } else {
-               char *linkname = NULL;
-@@ -632,6 +677,9 @@ static bool do_stat(const char *filename
- # if ENABLE_SELINUX
-               printf("   S_Context: %lc\n", *scontext);
- # endif
-+#if ENABLE_SMACK
-+              printf("   Smack: %lc\n", smack);
-+#endif
-               printf("Access: %s\n" "Modify: %s\n" "Change: %s\n",
-                      human_time(statbuf.st_atime),
-                      human_time(statbuf.st_mtime),
-@@ -653,6 +701,7 @@ int stat_main(int argc UNUSED_PARAM, cha
-       opt_complementary = "-1"; /* min one arg */
-       opts = getopt32(argv, "ftL"
-               IF_SELINUX("Z")
-+              IF_SMACK("Z")
-               IF_FEATURE_STAT_FORMAT("c:", &format)
-       );
-       if (opts & OPT_FILESYS) /* -f */
-diff -uprN busybox-1.17.1/findutils/find.c busybox-1.17.1-smack/findutils/find.c
---- busybox-1.17.1/findutils/find.c    2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/findutils/find.c      2011-08-18 17:39:38.790218576 -0700
-@@ -223,6 +223,13 @@
- //config:     help
- //config:       Support the 'find -context' option for matching security context.
- //config:
-+//config:config FEATURE_FIND_SMACK
-+//config:     bool "Enable -smack: Smack label matching"
-+//config:     default n
-+//config:     depends on FIND && SMACK
-+//config:     help
-+//config:       Support the 'find -smack' option for matching Smack label.
-+//config:
- //config:config FEATURE_FIND_LINKS
- //config:     bool "Enable -links: link count matching"
- //config:     default y
-@@ -268,6 +275,7 @@ IF_FEATURE_FIND_INUM(   ACTS(inum,  ino_
- IF_FEATURE_FIND_USER(   ACTS(user,  uid_t uid;))
- IF_FEATURE_FIND_SIZE(   ACTS(size,  char size_char; off_t size;))
- IF_FEATURE_FIND_CONTEXT(ACTS(context, security_context_t context;))
-+IF_FEATURE_FIND_SMACK(  ACTS(smack,  const char *smack;))
- IF_FEATURE_FIND_PAREN(  ACTS(paren, action ***subexpr;))
- IF_FEATURE_FIND_PRUNE(  ACTS(prune))
- IF_FEATURE_FIND_DELETE( ACTS(delete))
-@@ -573,6 +581,17 @@ ACTF(context)
-       return rc == 0;
- }
- #endif
-+#if ENABLE_FEATURE_FIND_SMACK
-+ACTF(smack)
-+{
-+      char smack[SMACKBUFFSIZE];
-+
-+      if (smack_from_file(fileName, smack, SMACKBUFFSIZE,
-+              (G.recurse_flags & ACTION_FOLLOWLINKS) ? 1 : 0) < 0)
-+              return FALSE;
-+      return strcmp(ap->smack, smack) == 0;
-+}
-+#endif
- #if ENABLE_FEATURE_FIND_LINKS
- ACTF(links)
- {
-@@ -704,6 +723,7 @@ static action*** parse_params(char **arg
-       IF_FEATURE_FIND_GROUP(  PARM_group     ,)
-       IF_FEATURE_FIND_SIZE(   PARM_size      ,)
-       IF_FEATURE_FIND_CONTEXT(PARM_context   ,)
-+      IF_FEATURE_FIND_SMACK(  PARM_smack     ,)
-       IF_FEATURE_FIND_LINKS(  PARM_links     ,)
-       };
-@@ -738,6 +758,7 @@ static action*** parse_params(char **arg
-       IF_FEATURE_FIND_GROUP(  "-group\0"  )
-       IF_FEATURE_FIND_SIZE(   "-size\0"   )
-       IF_FEATURE_FIND_CONTEXT("-context\0")
-+      IF_FEATURE_FIND_SMACK(  "-smack\0"  )
-       IF_FEATURE_FIND_LINKS(  "-links\0"  )
-                                ;
-@@ -1028,6 +1049,13 @@ static action*** parse_params(char **arg
-                               bb_simple_perror_msg(arg1);
-               }
- #endif
-+#if ENABLE_FEATURE_FIND_SMACK
-+              else if (parm == PARM_smack) {
-+                      action_smack *ap;
-+                      ap = ALLOC_ACTION(smack);
-+                      ap->smack = arg1;
-+              }
-+#endif
- #if ENABLE_FEATURE_FIND_LINKS
-               else if (parm == PARM_links) {
-                       action_links *ap;
-@@ -1115,6 +1143,9 @@ static action*** parse_params(char **arg
- //usage:      IF_FEATURE_FIND_CONTEXT(
- //usage:     "\n      -context        File has specified security context"
- //usage:      )
-+//usage:      IF_FEATURE_FIND_CONTEXT(
-+//usage:     "\n      -smack          File has specified Smack label"
-+//usage:      )
- //usage:      IF_FEATURE_FIND_EXEC(
- //usage:     "\n      -exec CMD ARG ; Run CMD with all instances of {} replaced by the"
- //usage:     "\n                      matching files"
-diff -uprN busybox-1.17.1/include/applets.src.h busybox-1.17.1-smack/include/applets.src.h
---- busybox-1.17.1/include/applets.src.h       2010-07-24 15:12:43.000000000 -0700
-+++ busybox-1.17.1-smack/include/applets.src.h 2011-08-18 11:40:15.809964682 -0700
-@@ -279,6 +279,7 @@ IF_MV(APPLET(mv, _BB_DIR_BIN, _BB_SUID_D
- IF_NAMEIF(APPLET(nameif, _BB_DIR_SBIN, _BB_SUID_DROP))
- IF_NC(APPLET(nc, _BB_DIR_USR_BIN, _BB_SUID_DROP))
- IF_NETSTAT(APPLET(netstat, _BB_DIR_BIN, _BB_SUID_DROP))
-+IF_NEWSMACK(APPLET(newsmack, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
- IF_NICE(APPLET(nice, _BB_DIR_BIN, _BB_SUID_DROP))
- IF_NMETER(APPLET(nmeter, _BB_DIR_USR_BIN, _BB_SUID_DROP))
- IF_NOHUP(APPLET(nohup, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-@@ -356,6 +357,9 @@ IF_SHA512SUM(APPLET_ODDNAME(sha512sum, m
- IF_SHOWKEY(APPLET(showkey, _BB_DIR_USR_BIN, _BB_SUID_DROP))
- IF_SLATTACH(APPLET(slattach, _BB_DIR_SBIN, _BB_SUID_DROP))
- IF_SLEEP(APPLET_NOFORK(sleep, sleep, _BB_DIR_BIN, _BB_SUID_DROP, sleep))
-+IF_SMACKCIPSO(APPLET(smackcipso, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-+IF_SMACKENABLED(APPLET(smackenabled, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
-+IF_SMACKLOAD(APPLET(smackload, _BB_DIR_USR_SBIN, _BB_SUID_DROP))
- IF_SOFTLIMIT(APPLET_ODDNAME(softlimit, chpst, _BB_DIR_USR_BIN, _BB_SUID_DROP, softlimit))
- IF_SORT(APPLET_NOEXEC(sort, sort, _BB_DIR_USR_BIN, _BB_SUID_DROP, sort))
- IF_SPLIT(APPLET(split, _BB_DIR_USR_BIN, _BB_SUID_DROP))
-diff -uprN busybox-1.17.1/include/libbb.h busybox-1.17.1-smack/include/libbb.h
---- busybox-1.17.1/include/libbb.h     2010-07-24 15:12:43.000000000 -0700
-+++ busybox-1.17.1-smack/include/libbb.h       2011-08-18 11:54:34.569974792 -0700
-@@ -52,6 +52,12 @@
- #include <selinux/flask.h>
- #include <selinux/av_permissions.h>
- #endif
-+#if ENABLE_SMACK
-+#define       SMACKBUFFSIZE   24
-+#define       SMACKATTR               "security.SMACK64"
-+#define       SMACKFLOOR              "_"
-+#define       SMACKSTAR               "*"
-+#endif
- #if ENABLE_LOCALE_SUPPORT
- # include <locale.h>
- #else
-@@ -1183,6 +1189,16 @@ extern void selinux_preserve_fcontext(in
- #endif
- extern void selinux_or_die(void) FAST_FUNC;
-+#if ENABLE_SMACK
-+extern int smack_from_file(const char *path, char *result, int len, int follow);
-+extern int smack_from_proc(const int pid, char *result, int len);
-+extern int smack_to_file(const char *path, const char *smack, int follow);
-+extern int smack_to_proc(const int pid, const char *smack);
-+extern int smack_user_default(const char *user, char *result, int len);
-+extern int smack_user_allowed(const char *user, const char *smack);
-+extern int smack_user_add(const char *user, const char *smack);
-+#endif
-+
- /* setup_environment:
-  * if chdir pw->pw_dir: ok: else if to_tmp == 1: goto /tmp else: goto / or die
-  * if clear_env = 1: cd(pw->pw_dir), clear environment, then set
-@@ -1391,6 +1407,7 @@ typedef struct procps_status_t {
-       char *argv0;
-       char *exe;
-       IF_SELINUX(char *context;)
-+      IF_SMACK(char smack[SMACKBUFFSIZE];)
-       /* Everything below must contain no ptrs to malloc'ed data:
-        * it is memset(0) for each process in procps_scan() */
-       unsigned long vsz, rss; /* we round it to kbytes */
-@@ -1453,6 +1470,7 @@ enum {
-                               || ENABLE_SESTATUS
-                               ),
-       PSSCAN_CONTEXT  = (1 << 17) * ENABLE_SELINUX,
-+      PSSCAN_SMACK    = (1 << 17) * ENABLE_SMACK,
-       PSSCAN_START_TIME = 1 << 18,
-       PSSCAN_CPU      = (1 << 19) * ENABLE_FEATURE_TOP_SMP_PROCESS,
-       PSSCAN_NICE     = (1 << 20) * ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS,
-@@ -1574,6 +1592,9 @@ extern const char bb_path_group_file[];
- extern const char bb_path_motd_file[];
- extern const char bb_path_wtmp_file[];
- extern const char bb_dev_null[];
-+#if ENABLE_SMACK
-+extern const char bb_path_smack_user[];
-+#endif
- extern const char bb_busybox_exec_path[];
- extern const char *bb_busybox_exec_paths[];
- /* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin,
-diff -uprN busybox-1.17.1/include/usage.src.h busybox-1.17.1-smack/include/usage.src.h
---- busybox-1.17.1/include/usage.src.h 2010-07-24 15:12:43.000000000 -0700
-+++ busybox-1.17.1-smack/include/usage.src.h   2011-08-18 12:10:12.649985839 -0700
-@@ -1764,6 +1764,9 @@ INSERT
-       IF_SELINUX( \
-      "\n      -Z      Print the security context" \
-       ) \
-+      IF_SMACK( \
-+     "\n      -Z      Print the Smack label" \
-+      ) \
-      "\n      -u      Print user ID" \
-      "\n      -g      Print group ID" \
-      "\n      -G      Print supplementary group IDs" \
-@@ -2982,6 +2985,11 @@ INSERT
-      "\n      -p      Display PID/Program name for sockets" \
-       )
-+#define newsmack_trivial_usage \
-+     "label [COMMAND [ARG] ...]"
-+#define newsmack_full_usage \
-+     "Run a program or shell with the specified Smack label" \
-+
- #define nmeter_trivial_usage \
-        "format_string"
- #define nmeter_full_usage "\n\n" \
-@@ -3303,7 +3311,7 @@ INSERT
- #else /* !ENABLE_DESKTOP */
--#if !ENABLE_SELINUX && !ENABLE_FEATURE_PS_WIDE
-+#if !ENABLE_SELINUX && !ENABLE_FEATURE_PS_WIDE && !ENABLE_SMACK
- #define USAGE_PS "\nThis version of ps accepts no options"
- #else
- #define USAGE_PS "\nOptions:"
-@@ -3317,6 +3325,9 @@ INSERT
-       IF_SELINUX( \
-      "\n      -Z      Show selinux context" \
-       ) \
-+      IF_SMACK( \
-+     "\n      -Z      Show Smack label" \
-+      ) \
-       IF_FEATURE_PS_WIDE( \
-      "\n      w       Wide output" \
-       )
-@@ -3845,6 +3856,17 @@ INSERT
-        "$ sleep 1d 3h 22m 8s\n" \
-        "[98528 second delay results]\n")
-+#define smackcipso_trivial_usage
-+#define smackcipso_full_usage \
-+       "Read and set Smack CIPSO mappings from the standard input"
-+
-+#define smackenabled_trivial_usage
-+#define smackenabled_full_usage
-+
-+#define smackload_trivial_usage
-+#define smackload_full_usage \
-+       "Read and set Smack access rules from the standard input"
-+
- #define sort_trivial_usage \
-        "[-nru" \
-       IF_FEATURE_SORT_BIG("gMcszbdfimSTokt] [-o FILE] [-k start[.offset][opts][,end[.offset][opts]] [-t CHAR") \
-@@ -3982,6 +4004,9 @@ INSERT
-       IF_SELINUX( \
-      "\n      -Z      Print security context" \
-       ) \
-+      IF_SMACK( \
-+     "\n      -Z      Print Smack label" \
-+      ) \
-       IF_FEATURE_STAT_FORMAT( \
-        "\n\nValid format sequences for files:\n" \
-        " %a   Access rights in octal\n" \
-@@ -4019,6 +4044,9 @@ INSERT
-       IF_SELINUX( \
-        " %C   Security context in selinux\n" \
-       ) \
-+      IF_SMACK( \
-+       " %C   Smack label\n" \
-+      ) \
-        " %i   File System ID in hex\n" \
-        " %l   Maximum length of filenames\n" \
-        " %n   File name\n" \
-diff -uprN busybox-1.17.1/libbb/Kbuild.src busybox-1.17.1-smack/libbb/Kbuild.src
---- busybox-1.17.1/libbb/Kbuild.src    2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/libbb/Kbuild.src      2011-08-18 15:10:58.330113541 -0700
-@@ -126,6 +126,7 @@ lib-$(CONFIG_FEATURE_UTMP) += utmp.o
- # A mix of optimizations (why build stuff we know won't be used)
- # and objects which may fail to build (SELinux on selinux-less system)
- lib-$(CONFIG_SELINUX) += selinux_common.o
-+lib-$(CONFIG_SMACK) += smack_common.o
- lib-$(CONFIG_FEATURE_MTAB_SUPPORT) += mtab.o
- lib-$(CONFIG_UNICODE_SUPPORT) += unicode.o
- lib-$(CONFIG_FEATURE_CHECK_NAMES) += die_if_bad_username.o
-diff -uprN busybox-1.17.1/libbb/messages.c busybox-1.17.1-smack/libbb/messages.c
---- busybox-1.17.1/libbb/messages.c    2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/libbb/messages.c      2011-08-17 15:27:12.889107702 -0700
-@@ -43,6 +43,9 @@ const char bb_path_shadow_file[] ALIGN1
- const char bb_path_group_file[] ALIGN1 = "/etc/group";
- const char bb_path_gshadow_file[] ALIGN1 = "/etc/gshadow";
- const char bb_path_motd_file[] ALIGN1 = "/etc/motd";
-+#if ENABLE_SMACK
-+const char bb_path_smack_user[] ALIGN1 = "/etc/smack/user";
-+#endif
- const char bb_dev_null[] ALIGN1 = "/dev/null";
- const char bb_busybox_exec_path[] ALIGN1 = CONFIG_BUSYBOX_EXEC_PATH;
- const char *bb_busybox_exec_paths[] ALIGN1 = {
-diff -uprN busybox-1.17.1/libbb/procps.c busybox-1.17.1-smack/libbb/procps.c
---- busybox-1.17.1/libbb/procps.c      2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/libbb/procps.c        2011-08-17 15:27:12.909107701 -0700
-@@ -240,6 +240,12 @@ procps_status_t* FAST_FUNC procps_scan(p
-                               sp->context = NULL;
-               }
- #endif
-+#if ENABLE_SMACK
-+              if (flags & PSSCAN_SMACK) {
-+                      if (smack_from_proc(sp->pid, sp->smack, SMACKBUFFSIZE) < 0)
-+                              strcpy(sp->smack, "?");
-+              }
-+#endif
-               filename_tail = filename + sprintf(filename, "/proc/%u/", pid);
-diff -uprN busybox-1.17.1/libbb/smack_common.c busybox-1.17.1-smack/libbb/smack_common.c
---- busybox-1.17.1/libbb/smack_common.c        1969-12-31 16:00:00.000000000 -0800
-+++ busybox-1.17.1-smack/libbb/smack_common.c  2011-08-18 17:17:53.730203208 -0700
-@@ -0,0 +1,178 @@
-+/*
-+ * libbb/smack_common.c
-+ *   -- common Smack utility functions
-+ *
-+ * Copyright 2007 Casey Schaufler <casey@schaufler-ca.com>
-+ */
-+#include "libbb.h"
-+#include <sys/xattr.h>
-+
-+#define       SMACKPROCPATH   "/proc/%d/attr/current"
-+#define SMACKUSERFILE "/etc/smack/user"
-+#define SMACKFILELINE 256
-+
-+int smack_from_file(const char *path, char *result, int len, int follow)
-+{
-+      char buffer[SMACKBUFFSIZE];
-+      int rc;
-+
-+      if (follow)
-+              rc = getxattr(path, SMACKATTR, buffer, SMACKBUFFSIZE - 1);
-+      else
-+              rc = lgetxattr(path, SMACKATTR, buffer, SMACKBUFFSIZE - 1);
-+
-+      if (rc < 0)
-+              return rc;
-+
-+      buffer[rc] = '\0';
-+      if (strlen(buffer) > len)
-+              return -EINVAL;
-+
-+      strcpy(result, buffer);
-+      return rc;
-+}
-+
-+int smack_from_proc(const int pid, char *result, int len)
-+{
-+      char path[SMACKFILELINE];
-+      char buffer[SMACKFILELINE];
-+      int fd;
-+      int i;
-+
-+      sprintf(path, SMACKPROCPATH, (pid < 0) ? getpid() : pid);
-+
-+      fd = open(path, O_RDONLY);
-+      if (fd < 0)
-+              return -EACCES;
-+
-+      i = read(fd, buffer, SMACKFILELINE);
-+      close(fd);
-+
-+      if (i <= 0 || i >= len)
-+              return -ERANGE;
-+      buffer[i] = '\0';
-+      strcpy(result, buffer);
-+      return 0;
-+}
-+
-+int smack_to_file(const char *path, const char *smack, int follow)
-+{
-+      if (follow)
-+              return setxattr(path, SMACKATTR, smack, strlen(smack)+1, 0);
-+      return lsetxattr(path, SMACKATTR, smack, strlen(smack)+1, 0);
-+}
-+
-+int smack_to_proc(const int pid, const char *smack)
-+{
-+      char path[SMACKFILELINE];
-+      int fd;
-+      int i;
-+      int slen;
-+
-+      slen = strlen(smack) + 1;
-+      if (slen >= SMACKBUFFSIZE)
-+              return -ERANGE;
-+
-+      sprintf(path, SMACKPROCPATH, (pid < 0) ? getpid() : pid);
-+
-+      fd = open(path, O_RDWR);
-+      if (fd < 0)
-+              return -EACCES;
-+
-+      i = write(fd, smack, slen);
-+      close(fd);
-+
-+      if (i != slen)
-+              return -EINVAL;
-+      return 0;
-+}
-+
-+int smack_user_default(const char *user, char *result, int len)
-+{
-+      char line[SMACKFILELINE];
-+      char ruser[SMACKFILELINE];
-+      char rsmack[SMACKFILELINE];
-+      FILE *fp;
-+
-+      fp = fopen(bb_path_smack_user, "r");
-+      if (fp == NULL)
-+              return -ENOENT;
-+
-+      while (fgets(line, SMACKFILELINE, fp) != NULL) {
-+              if (line[0] == '#' || line[0] == '\n')
-+                      continue;
-+              if (sscanf(line, "%s %s", ruser, rsmack) != 2)
-+                      continue;
-+              if (strcmp(ruser, user) != 0)
-+                      continue;
-+              if (strlen(rsmack) >= len)
-+                      return -ERANGE;
-+              strcpy(result, rsmack);
-+              return 0;
-+      }
-+      return -ENOENT;
-+}
-+
-+int smack_user_allowed(const char *user, const char *smack)
-+{
-+      char line[SMACKFILELINE];
-+      char *ruser;
-+      char *rsmack;
-+      FILE *fp;
-+      int rc = 1;
-+
-+      fp = fopen(SMACKUSERFILE, "r");
-+      if (fp == NULL)
-+              return -ENOENT;
-+
-+      while (rc == 1 && fgets(line, SMACKFILELINE, fp) != NULL) {
-+              if (line[0] == '#' || line[0] == '\n')
-+                      continue;
-+
-+              ruser = strtok(line, " \t\n");
-+              if (ruser == NULL)
-+                      continue;
-+
-+              if (strcmp(ruser, user) != 0)
-+                      continue;
-+
-+              while (rc == 1 && (rsmack = strtok(NULL, " \t\n")) != NULL) {
-+                      if (strcmp(rsmack, smack) == 0 || strcmp(rsmack, "+") == 0)
-+                              rc = 0;
-+              }
-+      }
-+      fclose(fp);
-+      return (rc == 1) ? -ENOENT : rc;
-+}
-+
-+int smack_user_add(const char *user, const char *smack)
-+{
-+      char line[SMACKFILELINE];
-+      char *ruser;
-+      FILE *fp;
-+      int rc = 0;
-+
-+      fp = fopen(SMACKUSERFILE, "a+");
-+      if (fp == NULL)
-+              return -ENOENT;
-+
-+      while (fgets(line, SMACKFILELINE, fp) != NULL) {
-+              if (line[0] == '#' || line[0] == '\n')
-+                      continue;
-+
-+              ruser = strtok(line, " \t\n");
-+              if (ruser == NULL)
-+                      continue;
-+
-+              if (strcmp(ruser, user) == 0) {
-+                      rc = -EEXIST;
-+                      break;
-+              }
-+      }
-+
-+      if (rc == 0)
-+              fprintf(fp, "%s %s\n", user, smack);
-+
-+      fclose(fp);
-+      return rc;
-+}
-diff -uprN busybox-1.17.1/loginutils/adduser.c busybox-1.17.1-smack/loginutils/adduser.c
---- busybox-1.17.1/loginutils/adduser.c        2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/loginutils/adduser.c  2011-08-17 15:27:12.909107701 -0700
-@@ -112,6 +112,7 @@ int adduser_main(int argc UNUSED_PARAM,
-       const char *usegroup = NULL;
-       char *p;
-       unsigned opts;
-+      IF_SMACK(const char *smack = SMACKSTAR;)
- #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
-       applet_long_options = adduser_longopts;
-@@ -130,10 +131,10 @@ int adduser_main(int argc UNUSED_PARAM,
-       /* disable interactive passwd for system accounts */
-       opt_complementary = "=1:SD:u+";
-       if (sizeof(pw.pw_uid) == sizeof(int)) {
--              opts = getopt32(argv, "h:g:s:G:DSHu:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &pw.pw_uid);
-+              opts = getopt32(argv, "h:g:s:G:DSHu:" IF_SMACK("L:"), &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &pw.pw_uid IF_SMACK(, &smack) );
-       } else {
-               unsigned uid;
--              opts = getopt32(argv, "h:g:s:G:DSHu:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &uid);
-+              opts = getopt32(argv, "h:g:s:G:DSHu:" IF_SMACK("L:"), &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &uid IF_SMACK(, &smack) );
-               if (opts & OPT_UID) {
-                       pw.pw_uid = uid;
-               }
-@@ -220,6 +221,14 @@ int adduser_main(int argc UNUSED_PARAM,
-                       bb_simple_perror_msg(pw.pw_dir);
-               }
-       }
-+#if ENABLE_SMACK
-+      if (smack_user_add(pw.pw_name, smack) < 0)
-+              bb_error_msg("cannot add user smack entry");
-+      if (strtok(smack, " \t") != NULL)
-+              if (smack_to_file(pw.pw_dir, smack, 0) != 0)
-+                      bb_error_msg("cannot set smack on home directory");
-+#endif
-+
-       if (!(opts & OPT_DONT_SET_PASS)) {
-               /* interactively set passwd */
-diff -uprN busybox-1.17.1/loginutils/login.c busybox-1.17.1-smack/loginutils/login.c
---- busybox-1.17.1/loginutils/login.c  2010-07-24 15:12:43.000000000 -0700
-+++ busybox-1.17.1-smack/loginutils/login.c    2011-08-17 15:27:12.909107701 -0700
-@@ -218,6 +218,7 @@ int login_main(int argc UNUSED_PARAM, ch
-       struct passwd pwdstruct;
-       char pwdbuf[256];
- #endif
-+      IF_SMACK(char smack[SMACKBUFFSIZE];)
-       username[0] = '\0';
-       signal(SIGALRM, alarm_handler);
-@@ -376,6 +377,13 @@ int login_main(int argc UNUSED_PARAM, ch
-               die_if_nologin();
-       IF_SELINUX(initselinux(username, full_tty, &user_sid));
-+#if ENABLE_SMACK
-+      if (smack_user_default(pw->pw_name, smack, SMACKBUFFSIZE) < 0)
-+              strcpy(smack, SMACKFLOOR);
-+      if (smack_to_proc(-1, smack) < 0)
-+              bb_perror_msg_and_die("cannot set smack");
-+#endif
-+
-       /* Try these, but don't complain if they fail.
-        * _f_chown is safe wrt race t=ttyname(0);...;chown(t); */
-diff -uprN busybox-1.17.1/loginutils/su.c busybox-1.17.1-smack/loginutils/su.c
---- busybox-1.17.1/loginutils/su.c     2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/loginutils/su.c       2011-08-18 15:13:07.830115065 -0700
-@@ -44,6 +44,7 @@ int su_main(int argc UNUSED_PARAM, char
-       const char *tty;
-       char user_buf[64];
-       const char *old_user;
-+      IF_SMACK(char smack[SMACKBUFFSIZE];)
-       flags = getopt32(argv, "mplc:s:", &opt_command, &opt_shell);
-       //argc -= optind;
-@@ -129,6 +130,13 @@ int su_main(int argc UNUSED_PARAM, char
-                       + (!(flags & SU_OPT_mp) * SETUP_ENV_CHANGEENV),
-                       pw);
-       IF_SELINUX(set_current_security_context(NULL);)
-+#if ENABLE_SMACK
-+      if (smack_from_proc(-1, smack, SMACKBUFFSIZE) < 0)
-+              bb_error_msg_and_die("cannot identify process smack");
-+      if (smack_user_allowed(pw->pw_name, smack) < 0)
-+              bb_error_msg_and_die("new usr not allowed current smack");
-+#endif
-+
-       /* Never returns */
-       run_shell(opt_shell, flags & SU_OPT_l, opt_command, (const char**)argv);
-diff -uprN busybox-1.17.1/loginutils/sulogin.c busybox-1.17.1-smack/loginutils/sulogin.c
---- busybox-1.17.1/loginutils/sulogin.c        2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/loginutils/sulogin.c  2011-08-17 15:27:12.909107701 -0700
-@@ -26,6 +26,7 @@ int sulogin_main(int argc UNUSED_PARAM,
-       char buffer[256];
-       struct spwd spw;
- #endif
-+      IF_SMACK(char smack[SMACKBUFFSIZE];)
-       logmode = LOGMODE_BOTH;
-       openlog(applet_name, 0, LOG_AUTH);
-@@ -97,6 +98,11 @@ int sulogin_main(int argc UNUSED_PARAM,
-       bb_info_msg("System Maintenance Mode");
-       IF_SELINUX(renew_current_security_context());
-+#if ENABLE_SMACK
-+      if (smack_to_proc(-1, SMACKFLOOR) < 0)
-+              bb_error_msg("login incorrect");
-+#endif
-+
-       shell = getenv("SUSHELL");
-       if (!shell)
-diff -uprN busybox-1.17.1/Makefile busybox-1.17.1-smack/Makefile
---- busybox-1.17.1/Makefile    2010-07-24 15:13:44.000000000 -0700
-+++ busybox-1.17.1-smack/Makefile      2011-08-17 15:27:12.909107701 -0700
-@@ -482,6 +482,7 @@ libs-y             := \
-               runit/ \
-               selinux/ \
-               shell/ \
-+              smack/ \
-               sysklogd/ \
-               util-linux/ \
-               util-linux/volume_id/ \
-diff -uprN busybox-1.17.1/miscutils/crond.c busybox-1.17.1-smack/miscutils/crond.c
---- busybox-1.17.1/miscutils/crond.c   2010-07-24 15:12:43.000000000 -0700
-+++ busybox-1.17.1-smack/miscutils/crond.c     2011-08-17 15:27:12.909107701 -0700
-@@ -906,6 +906,7 @@ static void RunJob(const char *user, Cro
- {
-       struct passwd *pas;
-       pid_t pid;
-+      IF_SMACK(char smack[SMACKBUFFSIZE];)
-       /* prepare things before vfork */
-       pas = getpwnam(user);
-@@ -919,6 +920,16 @@ static void RunJob(const char *user, Cro
-       pid = vfork();
-       if (pid == 0) {
-               /* CHILD */
-+#if ENABLE_SMACK
-+              if (smack_user_default(user, smack, SMACKBUFFSIZE) < 0) {
-+                      crondlog("\024user %s default smack unknown\n", user);
-+                      exit(0);
-+              }
-+              if (smack_to_proc(-1, smack) < 0) {
-+                      crondlog("\024user %s smack unsettable\n", user);
-+                      exit(0);
-+              }
-+#endif
-               /* change running state to the user in question */
-               ChangeUser(pas);
-               if (DebugOpt) {
-diff -uprN busybox-1.17.1/miscutils/crontab.c busybox-1.17.1-smack/miscutils/crontab.c
---- busybox-1.17.1/miscutils/crontab.c 2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/miscutils/crontab.c   2011-08-17 15:27:12.909107701 -0700
-@@ -77,6 +77,10 @@ int crontab_main(int argc UNUSED_PARAM,
-       int fd;
-       int src_fd;
-       int opt_ler;
-+#if ENABLE_SMACK
-+      char psmack[SMACKBUFFSIZE];
-+      char dsmack[SMACKBUFFSIZE];
-+#endif
-       /* file [opts]     Replace crontab from file
-        * - [opts]        Replace crontab from stdin
-@@ -120,6 +124,15 @@ int crontab_main(int argc UNUSED_PARAM,
-       if ((opt_ler - 1) & opt_ler) /* more than one bit set? */
-               bb_show_usage();
-+#if ENABLE_SMACK
-+      if (smack_user_default(pas->pw_name, dsmack, SMACKBUFFSIZE) < 0)
-+              bb_perror_msg_and_die("user default smack unknown");
-+      if (smack_from_proc(-1, psmack, SMACKBUFFSIZE) < 0)
-+              bb_perror_msg_and_die("user default smack unknown");
-+      if (strcmp(dsmack, psmack) != 0)
-+              bb_perror_msg_and_die("current smack is not default smack");
-+#endif
-+
-       /* Read replacement file under user's UID/GID/group vector */
-       src_fd = STDIN_FILENO;
-       if (!opt_ler) { /* Replace? */
-diff -uprN busybox-1.17.1/procps/ps.c busybox-1.17.1-smack/procps/ps.c
---- busybox-1.17.1/procps/ps.c 2010-07-05 19:25:54.000000000 -0700
-+++ busybox-1.17.1-smack/procps/ps.c   2011-08-18 17:28:10.070210464 -0700
-@@ -25,7 +25,12 @@ enum { MAX_WIDTH = 2*1024 };
- #if ENABLE_SELINUX
- #define SELINUX_O_PREFIX "label,"
- #define DEFAULT_O_STR    (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args")
--#else
-+#endif
-+#if ENABLE_SMACK
-+#define SMACK_O_PREFIX "label,"
-+#define DEFAULT_O_STR    (SMACK_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args")
-+#endif
-+#if !ENABLE_SELINUX && !ENABLE_SMACK
- #define DEFAULT_O_STR    ("pid,user" IF_FEATURE_PS_TIME(",time") ",args")
- #endif
-@@ -288,6 +293,13 @@ static void func_label(char *buf, int si
- }
- #endif
-+#if ENABLE_SMACK
-+static void func_label(char *buf, int size, const procps_status_t *ps)
-+{
-+      safe_strncpy(buf, ps->smack[0] ? ps->smack : "?", size+1);
-+}
-+#endif
-+
- /*
- static void func_nice(char *buf, int size, const procps_status_t *ps)
- {
-@@ -327,6 +339,9 @@ static const ps_out_t out_spec[] = {
- #if ENABLE_SELINUX
-       { 35                 , "label" ,"LABEL"  ,func_label ,PSSCAN_CONTEXT },
- #endif
-+#if ENABLE_SMACK
-+      { 24                 , "label" ,"LABEL"  ,func_label ,PSSCAN_SMACK },
-+#endif
- };
- static ps_out_t* new_out_t(void)
-@@ -516,6 +531,12 @@ int ps_main(int argc UNUSED_PARAM, char
-                       strcpy(default_o, DEFAULT_O_STR + sizeof(SELINUX_O_PREFIX)-1);
-               } else
- #endif
-+#if ENABLE_SMACK
-+              if (!(opt & 1)) {
-+                      /* no -Z : do not show LABEL */
-+                      strcpy(default_o, DEFAULT_O_STR + sizeof(SMACK_O_PREFIX) -1);
-+              } else
-+#endif
-               {
-                       strcpy(default_o, DEFAULT_O_STR);
-               }
-@@ -557,17 +578,17 @@ int ps_main(int argc UNUSED_PARAM, char
-                       | PSSCAN_STATE | PSSCAN_VSZ | PSSCAN_COMM;
-       unsigned terminal_width IF_NOT_FEATURE_PS_WIDE(= 79);
-       enum {
--              OPT_Z = (1 << 0) * ENABLE_SELINUX,
--              OPT_T = (1 << ENABLE_SELINUX) * ENABLE_FEATURE_SHOW_THREADS,
-+              OPT_Z = (1 << 0) * (ENABLE_SELINUX | ENABLE_SMACK),
-+              OPT_T = (1 << (ENABLE_SELINUX | ENABLE_SMACK)) * ENABLE_FEATURE_SHOW_THREADS,
-       };
-       int opts = 0;
-       /* If we support any options, parse argv */
--#if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_PS_WIDE
-+#if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_PS_WIDE || ENABLE_SMACK
- # if ENABLE_FEATURE_PS_WIDE
-       /* -w is a bit complicated */
-       int w_count = 0;
-       opt_complementary = "-:ww";
--      opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")"w", &w_count);
-+      opts = getopt32(argv, IF_SELINUX("Z")IF_SMACK("Z")IF_FEATURE_SHOW_THREADS("T")"w", &w_count);
-       /* if w is given once, GNU ps sets the width to 132,
-        * if w is given more than once, it is "unlimited"
-        */
-@@ -582,7 +603,7 @@ int ps_main(int argc UNUSED_PARAM, char
- # else
-       /* -w is not supported, only -Z and/or -T */
-       opt_complementary = "-";
--      opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T"));
-+      opts = getopt32(argv, IF_SELINUX("Z")IF_SMACK("Z")IF_FEATURE_SHOW_THREADS("T"));
- # endif
- #endif
-@@ -593,6 +614,13 @@ int ps_main(int argc UNUSED_PARAM, char
-               puts("  PID CONTEXT                          STAT COMMAND");
-       } else
- #endif
-+#if ENABLE_SMACK
-+      if (opts & OPT_Z) {
-+              psscan_flags = PSSCAN_PID | PSSCAN_SMACK
-+                              | PSSCAN_STATE | PSSCAN_COMM;
-+              puts("  PID CONTEXT                          STAT COMMAND");
-+      } else
-+#endif
-       {
-               puts("  PID USER       VSZ STAT COMMAND");
-       }
-@@ -611,6 +639,14 @@ int ps_main(int argc UNUSED_PARAM, char
-                                       p->state);
-               } else
- #endif
-+#if ENABLE_SMACK
-+              if (psscan_flags & PSSCAN_SMACK) {
-+                      len = printf("%5u %-24.24s %s  ",
-+                                      p->pid,
-+                                      p->smack ? p->smack : "unknown",
-+                                      p->state);
-+              } else
-+#endif
-               {
-                       const char *user = get_cached_username(p->uid);
-                       //if (p->vsz == 0)
-diff -uprN busybox-1.17.1/smack/Config.src busybox-1.17.1-smack/smack/Config.src
---- busybox-1.17.1/smack/Config.src    1969-12-31 16:00:00.000000000 -0800
-+++ busybox-1.17.1-smack/smack/Config.src      2011-08-18 11:19:44.669950186 -0700
-@@ -0,0 +1,40 @@
-+#
-+# For a description of the syntax of this configuration file,
-+# see scripts/kbuild/config-language.txt.
-+#
-+
-+menu "Smack Utilities"
-+      depends on SMACK
-+
-+INSERT
-+
-+config SMACKLOAD
-+      bool "smackload"
-+      default n
-+      depends on SMACK
-+      help
-+        Enable support to load Smack access rules.
-+
-+config SMACKCIPSO
-+      bool "smackcipso"
-+      default n
-+      depends on SMACK
-+      help
-+        Enable support to specify Smack CIPSO mappings.
-+
-+config NEWSMACK
-+      bool "newsmack"
-+      default n
-+      depends on SMACK
-+      help
-+        Enable support to run command with specified Smack label.
-+
-+config SMACKENABLED
-+      bool "smackenabled"
-+      default n
-+      depends on SMACK
-+      help
-+        Enable support for this command to be used within shell scripts
-+        to determine if smack is enabled.
-+
-+endmenu
-diff -uprN busybox-1.17.1/smack/Kbuild.src busybox-1.17.1-smack/smack/Kbuild.src
---- busybox-1.17.1/smack/Kbuild.src    1969-12-31 16:00:00.000000000 -0800
-+++ busybox-1.17.1-smack/smack/Kbuild.src      2011-08-19 13:22:11.301054005 -0700
-@@ -0,0 +1,14 @@
-+# Makefile for busybox
-+#
-+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
-+# Copyright (C) 2007 by KaiGai Kohei <kaigai@kaigai.gr.jp>
-+#
-+# Licensed under the GPL v2, see the file LICENSE in this tarball.
-+
-+INSERT
-+
-+lib-y:=
-+lib-$(CONFIG_NEWSMACK)                += newsmack.o
-+lib-$(CONFIG_SMACKLOAD)               += smackload.o
-+lib-$(CONFIG_SMACKCIPSO)      += smackcipso.o
-+lib-$(CONFIG_SMACKENABLED)    += smackenabled.o
-diff -uprN busybox-1.17.1/smack/newsmack.c busybox-1.17.1-smack/smack/newsmack.c
---- busybox-1.17.1/smack/newsmack.c    1969-12-31 16:00:00.000000000 -0800
-+++ busybox-1.17.1-smack/smack/newsmack.c      2011-08-17 15:27:12.909107701 -0700
-@@ -0,0 +1,61 @@
-+/*
-+ * newsmack label [ command [ arg ] ... ]
-+ *
-+ * Port to busybox: Casey Schaufler <casey@schaufler-ca.com>
-+ *
-+ * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
-+ *
-+ */
-+
-+#include "libbb.h"
-+
-+int newsmack_main(int argc, char **argv);
-+int newsmack_main(int argc, char **argv)
-+{
-+      struct passwd *pwd;
-+      char *newargv[256]; /* yes, I know */
-+      char *smack;
-+      int i;
-+      int newargc;
-+
-+      if (argc <= 1)
-+              bb_error_msg_and_die("No smack value specified");
-+
-+      smack = argv[1];
-+
-+      /*
-+       * Start a shell if no command is specified
-+       */
-+      if (argc == 2) {
-+              fprintf(stderr, "%s: start a shell at \"%s\"\n",
-+                      argv[0], smack);
-+              newargv[0] = strdup("sh");
-+              newargv[1] = NULL;
-+      } else {
-+              for (newargc = 0, i = 2; i < argc; newargc++, i++)
-+                      newargv[newargc] = argv[i];
-+              newargv[newargc] = NULL;
-+      }
-+
-+      /*
-+       * Verify the user is allowed the Smack label
-+       */
-+      pwd = getpwuid(getuid());
-+      if (pwd == NULL)
-+              bb_error_msg_and_die("User name not obtained");
-+      if (smack_user_allowed(pwd->pw_name, smack) != 0)
-+              bb_error_msg_and_die("User not allowed this smack label");
-+      /*
-+       * Set the process label.
-+       */
-+      i = smack_to_proc(-1, smack);
-+      if (i < 0)
-+              bb_error_msg_and_die("Cannot set smack");
-+
-+      /*
-+       * Do the exec
-+       */
-+      execvp(newargv[0], newargv);
-+
-+      bb_error_msg_and_die("%s: exec failure.", newargv[0]);
-+}
-diff -uprN busybox-1.17.1/smack/smackcipso.c busybox-1.17.1-smack/smack/smackcipso.c
---- busybox-1.17.1/smack/smackcipso.c  1969-12-31 16:00:00.000000000 -0800
-+++ busybox-1.17.1-smack/smack/smackcipso.c    2011-08-17 15:27:12.909107701 -0700
-@@ -0,0 +1,119 @@
-+/*
-+ * smackcipso - properly format smack access cipsos for
-+ * loading into the kernel by writing to /smack/cipso.
-+ *
-+ * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
-+ *
-+ *    This program is free software; you can redistribute it and/or modify
-+ *    it under the terms of the GNU General Public License as published by
-+ *    the Free Software Foundation, version 2.
-+ *
-+ * Authors:
-+ *    Casey Schaufler <casey@schaufler-ca.com>
-+ *    Ahmed S. Darwish <darwish.07@gmail.com>
-+ *
-+ */
-+/*
-+#include <sys/types.h>
-+#include <sys/stat.h>
-+#include <fcntl.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <ctype.h>
-+*/
-+#include "libbb.h"
-+
-+#define LSIZE 23
-+#define NSIZE 4
-+#define MAXCATNUM 239
-+#define MAXCATVAL 63
-+#define MAXLEVEL 255
-+
-+int smackcipso_main(int argc, char **argv);
-+int smackcipso_main(int argc, char **argv)
-+{
-+      int status = EXIT_SUCCESS;
-+      int cipsofd;
-+      char line[512];
-+      char cipso[LSIZE + NSIZE + NSIZE + (NSIZE * MAXCATNUM)];
-+      char cats[MAXCATNUM+1][NSIZE+1];
-+      char *cp;
-+      int level;
-+      int cat;
-+      int catcount;
-+      int i;
-+      int err;
-+
-+      cipsofd = open("/smack/cipso", O_RDWR);
-+      if (cipsofd < 0)
-+              bb_error_msg_and_die("failed opening /smack/cipso");
-+
-+      while (fgets(line, sizeof(line), stdin) != NULL) {
-+              catcount = 0;
-+              err = 0;
-+
-+              if ((cp = strchr(line, '\n')) == NULL) {
-+                      fprintf(stderr, "missing newline \"%s\"\n", line);
-+                      continue;
-+              }
-+              *cp = '\0';
-+              cp = strtok(line, " \t");
-+              if (cp == NULL) {
-+                      fprintf(stderr, "Empty line: \"%s\"\n", line);
-+                      continue;
-+              }
-+              sprintf(cipso, "%-23s ", line);
-+              if (strlen(cipso) != 24) {
-+                      fprintf(stderr, "Bad label starting: \"%s\"\n", line);
-+                      continue;
-+              }
-+              cp = strtok(NULL, " \t");
-+              if (cp == NULL) {
-+                      fprintf(stderr, "Missing level: \"%s\"\n", line);
-+                      continue;
-+              }
-+              if (!isdigit(*cp)) {
-+                      fprintf(stderr, "Bad level: \"%s\"\n", cp);
-+                      continue;
-+              }
-+              level = atoi(cp);
-+              if (level > MAXLEVEL) {
-+                      fprintf(stderr, "Bad level: \"%s\"\n", cp);
-+                      continue;
-+              }
-+              sprintf(cipso+LSIZE+1, "%-4d", level);
-+
-+              cp = strtok(NULL, " \t");
-+              for (i = 0; cp != NULL; cp = strtok(NULL, " \t"), i++) {
-+                      if (!isdigit(*cp)) {
-+                              fprintf(stderr, "Bad category \"%s\"\n", cp);
-+                              err = 1;
-+                              break;
-+                      }
-+                      cat = atoi(cp);
-+                      if (i >= MAXCATNUM) {
-+                              fprintf(stderr, "Maximum number of categories"
-+                                      "exceeded \"%d\"\n", i);
-+                              err = 1;
-+                              break;
-+                      }
-+                      if (cat > MAXCATVAL) {
-+                              fprintf(stderr, "Bad category \"%s\"\n", cp);
-+                              err = 1;
-+                              break;
-+                      }
-+                      sprintf(cats[i], "%-4d", cat);
-+              }
-+              if (err)
-+                      continue;
-+
-+              sprintf(cipso+LSIZE+1+NSIZE, "%-4d", i);
-+              while (i > 0)
-+                      strcat(cipso, cats[--i]);
-+              err = write(cipsofd, cipso, strlen(cipso));
-+              if (err < 0)
-+                      perror("writing /smack/cipso");
-+      }
-+      return status;
-+}
-diff -uprN busybox-1.17.1/smack/smackenabled.c busybox-1.17.1-smack/smack/smackenabled.c
---- busybox-1.17.1/smack/smackenabled.c        1969-12-31 16:00:00.000000000 -0800
-+++ busybox-1.17.1-smack/smack/smackenabled.c  2011-08-17 15:27:12.909107701 -0700
-@@ -0,0 +1,17 @@
-+/*
-+ * smackenabled
-+ *
-+ * Port to BusyBox  Casey Schaufler <casey@schaufler-ca.com
-+ *
-+ */
-+#include "libbb.h"
-+
-+int smackenabled_main(int argc, char **argv);
-+int smackenabled_main(int argc, char **argv)
-+{
-+      char buffer[80];
-+
-+      if (smack_from_file("/", buffer, sizeof(buffer), 0) < 0)
-+              return 0;
-+      return 1;
-+}
-diff -uprN busybox-1.17.1/smack/smackload.c busybox-1.17.1-smack/smack/smackload.c
---- busybox-1.17.1/smack/smackload.c   1969-12-31 16:00:00.000000000 -0800
-+++ busybox-1.17.1-smack/smack/smackload.c     2011-08-17 15:27:12.909107701 -0700
-@@ -0,0 +1,93 @@
-+/*
-+ * smackload - properly format smack access rules for
-+ * loading into the kernel by writing to /smack/load.
-+ *
-+ * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
-+ *
-+ *    This program is free software; you can redistribute it and/or modify
-+ *    it under the terms of the GNU General Public License as published by
-+ *    the Free Software Foundation, version 2.
-+ *
-+ * Author:
-+ *    Casey Schaufler <casey@schaufler-ca.com>
-+ */
-+/*
-+#include <sys/types.h>
-+#include <sys/stat.h>
-+#include <fcntl.h>
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <string.h>
-+*/
-+#include "libbb.h"
-+
-+#define LSIZE 23
-+#define ASIZE 4
-+
-+int smackload_main(int argc, char **argv);
-+int smackload_main(int argc, char **argv)
-+{
-+      int status = EXIT_SUCCESS;
-+      int loadfd;
-+      char line[80];
-+      char rule[LSIZE + LSIZE + ASIZE + 3];
-+      char subject[LSIZE+1];
-+      char object[LSIZE+1];
-+      char accesses[ASIZE+1];
-+      char real[ASIZE+1];
-+      char *cp;
-+      int i;
-+      int err;
-+
-+      loadfd = open("/smack/load", O_RDWR);
-+      if (loadfd < 0)
-+              bb_error_msg_and_die("failed opening /smack/load");
-+
-+      while (fgets(line, 80, stdin) != NULL) {
-+              err = 0;
-+              if ((cp = strchr(line, '\n')) != NULL)
-+                      *cp = '\0';
-+              if (sscanf(line,"%23s %23s %4s",subject,object,accesses) != 3) 
-+                      err = 1;
-+              else {
-+                      strcpy(real, "----");
-+                      for (i = 0;
-+                           i < ASIZE && accesses[i] != '\0' && err == 0;
-+                           i++) {
-+                              switch (accesses[i]) {
-+                              case 'r':
-+                              case 'R':
-+                                      real[0] = 'r';
-+                                      break;
-+                              case 'w':
-+                              case 'W':
-+                                      real[1] = 'w';
-+                                      break;
-+                              case 'x':
-+                              case 'X':
-+                                      real[2] = 'x';
-+                                      break;
-+                              case 'a':
-+                              case 'A':
-+                                      real[3] = 'a';
-+                                      break;
-+                              case '\0':
-+                              case '-':
-+                                      break;
-+                              default:
-+                                      err = 1;
-+                                      break;
-+                              }
-+                      }
-+              }
-+              if (err == 0) {
-+                      sprintf(rule, "%-23s %-23s %4s", subject,object,real);
-+                      err = write(loadfd, rule, LSIZE + LSIZE + ASIZE + 2);
-+                      if (err < 0)
-+                              perror("writing /smack/load");
-+              }
-+              else
-+                      fprintf(stderr, "Bad input line \"%s\"\n", line);
-+      }
-+      return status;
-+}
diff --git a/packaging/smack-conflict-with-selinux.patch b/packaging/smack-conflict-with-selinux.patch
deleted file mode 100644 (file)
index 8e9d425..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-From 0bfd7de40919494aa4a2cada5ca4c475d1a87b66 Mon Sep 17 00:00:00 2001
-From: Karol Lewandowski <k.lewandowsk@samsung.com>
-Date: Wed, 11 Jan 2012 11:21:15 +0100
-Subject: [PATCH] Don't allow SELINUX and SMACK to be enabled at the same time
-
-Source code simply doesn't support it.
----
- Config.in |    1 +
- 1 files changed, 1 insertions(+), 0 deletions(-)
-
-diff --git a/Config.in b/Config.in
-index 76c2b68..cac552c 100644
---- a/Config.in
-+++ b/Config.in
-@@ -385,6 +385,7 @@ config SELINUX
- config SMACK
-       bool "Support Smack"
-       default n
-+      depends on !SELINUX
-       help
-         Enable support for Smack in applets ls, ps, and id.  Also provide
-         the option of compiling in Smack applets.
--- 
-1.7.7.3
-
diff --git a/packaging/strip.patch b/packaging/strip.patch
deleted file mode 100644 (file)
index 1188e67..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
---- a/Makefile
-+++ b/Makefile
-@@ -690,20 +690,10 @@
- endif # ifdef CONFIG_KALLSYMS
- # busybox image - including updated kernel symbols
--busybox_unstripped: $(busybox-all) FORCE
-+busybox: $(busybox-all) FORCE
-       $(call if_changed_rule,busybox__)
-       $(Q)rm -f .old_version
--busybox: busybox_unstripped
--ifeq ($(SKIP_STRIP),y)
--      $(Q)cp $< $@
--else
--      $(Q)$(STRIP) -s --remove-section=.note --remove-section=.comment \
--              busybox_unstripped -o $@
--# strip is confused by PIE executable and does not set exec bits
--      $(Q)chmod a+x $@
--endif
--
- # The actual objects are generated when descending,
- # make sure no implicit rule kicks in
- $(sort $(busybox-all)): $(busybox-dirs) ;
diff --git a/packaging/stty-sort-out-preprocessor-conditionals.patch b/packaging/stty-sort-out-preprocessor-conditionals.patch
deleted file mode 100644 (file)
index e078f3d..0000000
+++ /dev/null
@@ -1,705 +0,0 @@
-From 138ce54c9c1930348bc842be781accd7c50c2cef Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Fri, 30 Jul 2010 06:01:37 +0200
-Subject: [PATCH 2/2] stty: sort out preprocessor conditionals
-
-* Move the definitions of missing constants to the top of the file.
-* Fix undefined IDX_xxx on missing termios constants.
-* FreeBSD has TABDLY, TAB0 and TAB3, but no TAB1 or TAB2
-* Omit the definition of set_window_size() if TIOCGWINSZ is not available.
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- coreutils/Config.src |    1 -
- coreutils/stty.c     |  339 +++++++++++++++++++++++++++++++------------------
- 2 files changed, 214 insertions(+), 126 deletions(-)
-
-diff --git a/coreutils/Config.src b/coreutils/Config.src
-index 780b73f..0eb70af 100644
---- a/coreutils/Config.src
-+++ b/coreutils/Config.src
-@@ -607,7 +607,6 @@ config FEATURE_STAT_FORMAT
- config STTY
-       bool "stty"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         stty is used to change and print terminal line settings.
-diff --git a/coreutils/stty.c b/coreutils/stty.c
-index c40d718..33f7b21 100644
---- a/coreutils/stty.c
-+++ b/coreutils/stty.c
-@@ -115,6 +115,113 @@
- # define CSTATUS Control('t')
- #endif
-+/* Save us from #ifdef forest plague */
-+#ifndef BSDLY
-+# define BSDLY 0
-+#endif
-+#ifndef CIBAUD
-+# define CIBAUD 0
-+#endif
-+#ifndef CRDLY
-+# define CRDLY 0
-+#endif
-+#ifndef CRTSCTS
-+# define CRTSCTS 0
-+#endif
-+#ifndef ECHOCTL
-+# define ECHOCTL 0
-+#endif
-+#ifndef ECHOKE
-+# define ECHOKE 0
-+#endif
-+#ifndef ECHOPRT
-+# define ECHOPRT 0
-+#endif
-+#ifndef FFDLY
-+# define FFDLY 0
-+#endif
-+#ifndef IEXTEN
-+# define IEXTEN 0
-+#endif
-+#ifndef IMAXBEL
-+# define IMAXBEL 0
-+#endif
-+#ifndef IUCLC
-+# define IUCLC 0
-+#endif
-+#ifndef IXANY
-+# define IXANY 0
-+#endif
-+#ifndef NLDLY
-+# define NLDLY 0
-+#endif
-+#ifndef OCRNL
-+# define OCRNL 0
-+#endif
-+#ifndef OFDEL
-+# define OFDEL 0
-+#endif
-+#ifndef OFILL
-+# define OFILL 0
-+#endif
-+#ifndef OLCUC
-+# define OLCUC 0
-+#endif
-+#ifndef ONLCR
-+# define ONLCR 0
-+#endif
-+#ifndef ONLRET
-+# define ONLRET 0
-+#endif
-+#ifndef ONOCR
-+# define ONOCR 0
-+#endif
-+#ifndef OXTABS
-+# define OXTABS 0
-+#endif
-+#ifndef TABDLY
-+# define TABDLY 0
-+#endif
-+#ifndef TAB1
-+# define TAB1 0
-+#endif
-+#ifndef TAB2
-+# define TAB2 0
-+#endif
-+#ifndef TOSTOP
-+# define TOSTOP 0
-+#endif
-+#ifndef VDSUSP
-+# define VDSUSP 0
-+#endif
-+#ifndef VEOL2
-+# define VEOL2 0
-+#endif
-+#ifndef VFLUSHO
-+# define VFLUSHO 0
-+#endif
-+#ifndef VLNEXT
-+# define VLNEXT 0
-+#endif
-+#ifndef VREPRINT
-+# define VREPRINT 0
-+#endif
-+#ifndef VSTATUS
-+# define VSTATUS 0
-+#endif
-+#ifndef VSWTCH
-+# define VSWTCH 0
-+#endif
-+#ifndef VTDLY
-+# define VTDLY 0
-+#endif
-+#ifndef VWERASE
-+# define VWERASE 0
-+#endif
-+#ifndef XCASE
-+# define XCASE 0
-+#endif
-+
- /* Which speeds to set */
- enum speed_setting {
-       input_speed, output_speed, both_speeds
-@@ -167,13 +274,13 @@ enum {
-       IDX_cbreak,
-       IDX_crt,
-       IDX_dec,
--#ifdef IXANY
-+#if IXANY
-       IDX_decctlq,
- #endif
--#if defined(TABDLY) || defined(OXTABS)
-+#if TABDLY || OXTABS
-       IDX_tabs,
- #endif
--#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
-+#if XCASE && IUCLC && OLCUC
-       IDX_lcase,
-       IDX_LCASE,
- #endif
-@@ -196,13 +303,13 @@ static const char mode_name[] =
-       MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
-       MI_ENTRY("crt",      combination, OMIT,              0,          0 )
-       MI_ENTRY("dec",      combination, OMIT,              0,          0 )
--#ifdef IXANY
-+#if IXANY
-       MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
- #endif
--#if defined(TABDLY) || defined(OXTABS)
-+#if TABDLY || OXTABS
-       MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
- #endif
--#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
-+#if XCASE && IUCLC && OLCUC
-       MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
-       MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
- #endif
-@@ -217,7 +324,7 @@ static const char mode_name[] =
-       MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
-       MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
-       MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
--#ifdef CRTSCTS
-+#if CRTSCTS
-       MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
- #endif
-       MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
-@@ -232,74 +339,78 @@ static const char mode_name[] =
-       MI_ENTRY("ixon",     input,       REV,               IXON,       0 )
-       MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 )
-       MI_ENTRY("tandem",   input,       REV        | OMIT, IXOFF,      0 )
--#ifdef IUCLC
-+#if IUCLC
-       MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
- #endif
--#ifdef IXANY
-+#if IXANY
-       MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
- #endif
--#ifdef IMAXBEL
-+#if IMAXBEL
-       MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 )
- #endif
-       MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 )
--#ifdef OLCUC
-+#if OLCUC
-       MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
- #endif
--#ifdef OCRNL
-+#if OCRNL
-       MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
- #endif
--#ifdef ONLCR
-+#if ONLCR
-       MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
- #endif
--#ifdef ONOCR
-+#if ONOCR
-       MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
- #endif
--#ifdef ONLRET
-+#if ONLRET
-       MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
- #endif
--#ifdef OFILL
-+#if OFILL
-       MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
- #endif
--#ifdef OFDEL
-+#if OFDEL
-       MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
- #endif
--#ifdef NLDLY
-+#if NLDLY
-       MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
-       MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
- #endif
--#ifdef CRDLY
-+#if CRDLY
-       MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY)
-       MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY)
-       MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY)
-       MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY)
- #endif
--#ifdef TABDLY
-+#if TABDLY
-       MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY)
-+# if TAB2
-       MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY)
-+# endif
-+# if TAB1
-       MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY)
-+# endif
-       MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY)
- #else
--# ifdef OXTABS
-+# if OXTABS
-       MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
- # endif
- #endif
--#ifdef BSDLY
-+#if BSDLY
-       MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
-       MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
- #endif
--#ifdef VTDLY
-+#if VTDLY
-       MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
-       MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
- #endif
--#ifdef FFDLY
-+#if FFDLY
-       MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY)
-       MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY)
- #endif
-       MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 )
-       MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 )
--#ifdef IEXTEN
-+#if IEXTEN
-       MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 )
- #endif
-       MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 )
-@@ -308,21 +419,21 @@ static const char mode_name[] =
-       MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 )
-       MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 )
-       MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 )
--#ifdef XCASE
-+#if XCASE
-       MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
- #endif
--#ifdef TOSTOP
-+#if TOSTOP
-       MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
- #endif
--#ifdef ECHOPRT
-+#if ECHOPRT
-       MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
-       MI_ENTRY("prterase", local,       REV | OMIT,        ECHOPRT,    0 )
- #endif
--#ifdef ECHOCTL
-+#if ECHOCTL
-       MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
-       MI_ENTRY("ctlecho",  local,       REV        | OMIT, ECHOCTL,    0 )
- #endif
--#ifdef ECHOKE
-+#if ECHOKE
-       MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
-       MI_ENTRY("crtkill",  local,       REV        | OMIT, ECHOKE,     0 )
- #endif
-@@ -346,13 +457,13 @@ static const struct mode_info mode_info[] = {
-       MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
-       MI_ENTRY("crt",      combination, OMIT,              0,          0 )
-       MI_ENTRY("dec",      combination, OMIT,              0,          0 )
--#ifdef IXANY
-+#if IXANY
-       MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
- #endif
--#if defined(TABDLY) || defined(OXTABS)
-+#if TABDLY || OXTABS
-       MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
- #endif
--#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
-+#if XCASE && IUCLC && OLCUC
-       MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
-       MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
- #endif
-@@ -367,7 +478,7 @@ static const struct mode_info mode_info[] = {
-       MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
-       MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
-       MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
--#ifdef CRTSCTS
-+#if CRTSCTS
-       MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
- #endif
-       MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
-@@ -382,74 +493,78 @@ static const struct mode_info mode_info[] = {
-       MI_ENTRY("ixon",     input,       REV,               IXON,       0 )
-       MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 )
-       MI_ENTRY("tandem",   input,       REV        | OMIT, IXOFF,      0 )
--#ifdef IUCLC
-+#if IUCLC
-       MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
- #endif
--#ifdef IXANY
-+#if IXANY
-       MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
- #endif
--#ifdef IMAXBEL
-+#if IMAXBEL
-       MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 )
- #endif
-       MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 )
--#ifdef OLCUC
-+#if OLCUC
-       MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
- #endif
--#ifdef OCRNL
-+#if OCRNL
-       MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
- #endif
--#ifdef ONLCR
-+#if ONLCR
-       MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
- #endif
--#ifdef ONOCR
-+#if ONOCR
-       MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
- #endif
--#ifdef ONLRET
-+#if ONLRET
-       MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
- #endif
--#ifdef OFILL
-+#if OFILL
-       MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
- #endif
--#ifdef OFDEL
-+#if OFDEL
-       MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
- #endif
--#ifdef NLDLY
-+#if NLDLY
-       MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
-       MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
- #endif
--#ifdef CRDLY
-+#if CRDLY
-       MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY)
-       MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY)
-       MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY)
-       MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY)
- #endif
--#ifdef TABDLY
-+#if TABDLY
-       MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY)
-+# if TAB2
-       MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY)
-+# endif
-+# if TAB1
-       MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY)
-+# endif
-       MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY)
- #else
--# ifdef OXTABS
-+# if OXTABS
-       MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
- # endif
- #endif
--#ifdef BSDLY
-+#if BSDLY
-       MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
-       MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
- #endif
--#ifdef VTDLY
-+#if VTDLY
-       MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
-       MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
- #endif
--#ifdef FFDLY
-+#if FFDLY
-       MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY)
-       MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY)
- #endif
-       MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 )
-       MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 )
--#ifdef IEXTEN
-+#if IEXTEN
-       MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 )
- #endif
-       MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 )
-@@ -458,21 +573,21 @@ static const struct mode_info mode_info[] = {
-       MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 )
-       MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 )
-       MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 )
--#ifdef XCASE
-+#if XCASE
-       MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
- #endif
--#ifdef TOSTOP
-+#if TOSTOP
-       MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
- #endif
--#ifdef ECHOPRT
-+#if ECHOPRT
-       MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
-       MI_ENTRY("prterase", local,       REV | OMIT,        ECHOPRT,    0 )
- #endif
--#ifdef ECHOCTL
-+#if ECHOCTL
-       MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
-       MI_ENTRY("ctlecho",  local,       REV        | OMIT, ECHOCTL,    0 )
- #endif
--#ifdef ECHOKE
-+#if ECHOKE
-       MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
-       MI_ENTRY("crtkill",  local,       REV        | OMIT, ECHOKE,     0 )
- #endif
-@@ -497,31 +612,31 @@ enum {
-       CIDX_kill,
-       CIDX_eof,
-       CIDX_eol,
--#ifdef VEOL2
-+#if VEOL2
-       CIDX_eol2,
- #endif
--#ifdef VSWTCH
-+#if VSWTCH
-       CIDX_swtch,
- #endif
-       CIDX_start,
-       CIDX_stop,
-       CIDX_susp,
--#ifdef VDSUSP
-+#if VDSUSP
-       CIDX_dsusp,
- #endif
--#ifdef VREPRINT
-+#if VREPRINT
-       CIDX_rprnt,
- #endif
--#ifdef VWERASE
-+#if VWERASE
-       CIDX_werase,
- #endif
--#ifdef VLNEXT
-+#if VLNEXT
-       CIDX_lnext,
- #endif
--#ifdef VFLUSHO
-+#if VFLUSHO
-       CIDX_flush,
- #endif
--#ifdef VSTATUS
-+#if VSTATUS
-       CIDX_status,
- #endif
-       CIDX_min,
-@@ -538,31 +653,31 @@ static const char control_name[] =
-       CI_ENTRY("kill",     CKILL,   VKILL   )
-       CI_ENTRY("eof",      CEOF,    VEOF    )
-       CI_ENTRY("eol",      CEOL,    VEOL    )
--#ifdef VEOL2
-+#if VEOL2
-       CI_ENTRY("eol2",     CEOL2,   VEOL2   )
- #endif
--#ifdef VSWTCH
-+#if VSWTCH
-       CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
- #endif
-       CI_ENTRY("start",    CSTART,  VSTART  )
-       CI_ENTRY("stop",     CSTOP,   VSTOP   )
-       CI_ENTRY("susp",     CSUSP,   VSUSP   )
--#ifdef VDSUSP
-+#if VDSUSP
-       CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
- #endif
--#ifdef VREPRINT
-+#if VREPRINT
-       CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
- #endif
--#ifdef VWERASE
-+#if VWERASE
-       CI_ENTRY("werase",   CWERASE, VWERASE )
- #endif
--#ifdef VLNEXT
-+#if VLNEXT
-       CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
- #endif
--#ifdef VFLUSHO
-+#if VFLUSHO
-       CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
- #endif
--#ifdef VSTATUS
-+#if VSTATUS
-       CI_ENTRY("status",   CSTATUS, VSTATUS )
- #endif
-       /* These must be last because of the display routines */
-@@ -581,31 +696,31 @@ static const struct control_info control_info[] = {
-       CI_ENTRY("kill",     CKILL,   VKILL   )
-       CI_ENTRY("eof",      CEOF,    VEOF    )
-       CI_ENTRY("eol",      CEOL,    VEOL    )
--#ifdef VEOL2
-+#if VEOL2
-       CI_ENTRY("eol2",     CEOL2,   VEOL2   )
- #endif
--#ifdef VSWTCH
-+#if VSWTCH
-       CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
- #endif
-       CI_ENTRY("start",    CSTART,  VSTART  )
-       CI_ENTRY("stop",     CSTOP,   VSTOP   )
-       CI_ENTRY("susp",     CSUSP,   VSUSP   )
--#ifdef VDSUSP
-+#if VDSUSP
-       CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
- #endif
--#ifdef VREPRINT
-+#if VREPRINT
-       CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
- #endif
--#ifdef VWERASE
-+#if VWERASE
-       CI_ENTRY("werase",   CWERASE, VWERASE )
- #endif
--#ifdef VLNEXT
-+#if VLNEXT
-       CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
- #endif
--#ifdef VFLUSHO
-+#if VFLUSHO
-       CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
- #endif
--#ifdef VSTATUS
-+#if VSTATUS
-       CI_ENTRY("status",   CSTATUS, VSTATUS )
- #endif
-       /* These must be last because of the display routines */
-@@ -740,6 +855,7 @@ static void newline(void)
-               wrapf("\n");
- }
-+#ifdef TIOCGWINSZ
- static void set_window_size(int rows, int cols)
- {
-       struct winsize win = { 0, 0, 0, 0 };
-@@ -760,6 +876,7 @@ static void set_window_size(int rows, int cols)
- bail:
-               perror_on_device("%s");
- }
-+#endif
- static void display_window_size(int fancy)
- {
-@@ -973,41 +1090,6 @@ static void sane_mode(struct termios *mode)
-       }
- }
--/* Save set_mode from #ifdef forest plague */
--#ifndef ONLCR
--#define ONLCR 0
--#endif
--#ifndef OCRNL
--#define OCRNL 0
--#endif
--#ifndef ONLRET
--#define ONLRET 0
--#endif
--#ifndef XCASE
--#define XCASE 0
--#endif
--#ifndef IXANY
--#define IXANY 0
--#endif
--#ifndef TABDLY
--#define TABDLY 0
--#endif
--#ifndef OXTABS
--#define OXTABS 0
--#endif
--#ifndef IUCLC
--#define IUCLC 0
--#endif
--#ifndef OLCUC
--#define OLCUC 0
--#endif
--#ifndef ECHOCTL
--#define ECHOCTL 0
--#endif
--#ifndef ECHOKE
--#define ECHOKE 0
--#endif
--
- static void set_mode(const struct mode_info *info, int reversed,
-                                       struct termios *mode)
- {
-@@ -1093,27 +1175,32 @@ static void set_mode(const struct mode_info *info, int reversed,
-                       mode->c_cc[VTIME] = 0;
-               }
-       }
--      else if (IXANY && info == &mode_info[IDX_decctlq]) {
-+#if IXANY
-+      else if (info == &mode_info[IDX_decctlq]) {
-               if (reversed)
-                       mode->c_iflag |= IXANY;
-               else
-                       mode->c_iflag &= ~IXANY;
-       }
--      else if (TABDLY && info == &mode_info[IDX_tabs]) {
-+#endif
-+#if TABDLY
-+      else if (info == &mode_info[IDX_tabs]) {
-               if (reversed)
-                       mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
-               else
-                       mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
-       }
--      else if (OXTABS && info == &mode_info[IDX_tabs]) {
-+#endif
-+#if OXTABS
-+      else if (info == &mode_info[IDX_tabs]) {
-               if (reversed)
-                       mode->c_oflag |= OXTABS;
-               else
-                       mode->c_oflag &= ~OXTABS;
--      } else
--      if (XCASE && IUCLC && OLCUC
--       && (info == &mode_info[IDX_lcase] || info == &mode_info[IDX_LCASE])
--      ) {
-+      }
-+#endif
-+#if XCASE && IUCLC && OLCUC
-+      else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) {
-               if (reversed) {
-                       mode->c_lflag &= ~XCASE;
-                       mode->c_iflag &= ~IUCLC;
-@@ -1123,7 +1210,9 @@ static void set_mode(const struct mode_info *info, int reversed,
-                       mode->c_iflag |= IUCLC;
-                       mode->c_oflag |= OLCUC;
-               }
--      } else if (info == &mode_info[IDX_crt]) {
-+      }
-+#endif
-+      else if (info == &mode_info[IDX_crt]) {
-               mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
-       } else if (info == &mode_info[IDX_dec]) {
-               mode->c_cc[VINTR] = 3; /* ^C */
-@@ -1419,7 +1508,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
-                       perror_on_device_and_die("%s");
-               if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
--#ifdef CIBAUD
-+#if CIBAUD
-                       /* SunOS 4.1.3 (at least) has the problem that after this sequence,
-                          tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
-                          sometimes (m1 != m2).  The only difference is in the four bits
--- 
-1.7.1
-
diff --git a/packaging/swaponoff-FreeBSD-support.patch b/packaging/swaponoff-FreeBSD-support.patch
deleted file mode 100644 (file)
index da273f5..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-From a5b837c34a96bdbb53151af455912b691c9aaa52 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 29 Jul 2010 21:59:54 +0200
-Subject: [PATCH 19/19] swaponoff: FreeBSD support
-
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
----
- util-linux/Config.src  |    3 +--
- util-linux/swaponoff.c |    6 +++---
- util-linux/xmount.c    |   10 ++++++++++
- util-linux/xmount.h    |   16 ++++++++++------
- 4 files changed, 24 insertions(+), 11 deletions(-)
-
-diff --git a/util-linux/Config.src b/util-linux/Config.src
-index 99a6fbe..cb4de95 100644
---- a/util-linux/Config.src
-+++ b/util-linux/Config.src
-@@ -639,7 +639,6 @@ config SETARCH
- config SWAPONOFF
-       bool "swaponoff"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         This option enables both the 'swapon' and the 'swapoff' utilities.
-         Once you have created some swap space using 'mkswap', you also need
-@@ -651,7 +650,7 @@ config SWAPONOFF
- config FEATURE_SWAPON_PRI
-       bool "Support priority option -p"
-       default y
--      depends on SWAPONOFF
-+      depends on SWAPONOFF && PLATFORM_LINUX
-       help
-         Enable support for setting swap device priority in swapon.
-diff --git a/util-linux/swaponoff.c b/util-linux/swaponoff.c
-index f2f52fb..d13c37e 100644
---- a/util-linux/swaponoff.c
-+++ b/util-linux/swaponoff.c
-@@ -8,8 +8,8 @@
-  */
- #include "libbb.h"
-+#include "xmount.h"
- #include <mntent.h>
--#include <sys/swap.h>
- #if ENABLE_FEATURE_MOUNT_LABEL
- # include "volume_id.h"
-@@ -43,9 +43,9 @@ static int swap_enable_disable(char *device)
- #endif
-       if (applet_name[5] == 'n')
--              status = swapon(device, g_flags);
-+              status = xswapon(device, g_flags);
-       else
--              status = swapoff(device);
-+              status = xswapoff(device);
-       if (status != 0) {
-               bb_simple_perror_msg(device);
-diff --git a/util-linux/xmount.c b/util-linux/xmount.c
-index 3f322b8..16543f1 100644
---- a/util-linux/xmount.c
-+++ b/util-linux/xmount.c
-@@ -63,4 +63,14 @@ int FAST_FUNC xumount(const char *target, int flags)
-       return unmount(target, flags);
- }
-+int FAST_FUNC xswapon(const char *path, int swapflags UNUSED_PARAM)
-+{
-+      return swapon(path);
-+}
-+
-+int FAST_FUNC xswapoff(const char *path)
-+{
-+      return swapoff(path);
-+}
-+
- #endif
-diff --git a/util-linux/xmount.h b/util-linux/xmount.h
-index caef564..bcd6d18 100644
---- a/util-linux/xmount.h
-+++ b/util-linux/xmount.h
-@@ -5,9 +5,9 @@
-  * Copyright (C) 2010 by Jeremie Koenig <jk@jk.fr.eu.org>
-  * Copyright (C) 2010 by Luca Favatella <slackydeb@gmail.com>
-  *
-- * The Linux prototypes for mount() and umount2() are used as a reference for
-- * our xmount() and xumount(), which should be implemented as a compatibility
-- * wrappers for non-Linux systems (see xmount.c).
-+ * The Linux prototypes for mount(), umount2(), swapon() and swapoff()  are
-+ * used as a reference for our versions of them. On non-Linux system those
-+ * should be implemented as compatibility wrappers (see xmount.c).
-  */
- /*
-@@ -17,6 +17,7 @@
- #ifdef __linux__
- # include <sys/mount.h>
-+# include <sys/swap.h>
- /* Make sure we have all the new mount flags we actually try to use
-  * (grab more as needed from util-linux's mount/mount_constants.h). */
- # ifndef MS_DIRSYNC
-@@ -56,6 +57,7 @@
- #elif defined(__FreeBSD_kernel__)
- # include <sys/mount.h>
-+# include <sys/swap.h>
- # define MS_NOSUID      MNT_NOSUID
- # define MS_NODEV       MNT_NODEV
- # define MS_NOEXEC      MNT_NOEXEC
-@@ -82,16 +84,18 @@
- #endif
- /*
-- * Prototypes for xmount() and xumount(): on Linux we use the system calls
-- * directly, otherwise xmount() and xumount() should be implemented as
-- * compatibility wrappers (see xmount.c).
-+ * Prototypes for the compatibility wrappers
-  */
- #ifdef __linux__
- # define xmount mount
- # define xumount umount2
-+# define xswapon swapon
-+# define xswapoff swapoff
- #else
- int xmount(const char *source, const char *target, const char *filesystemtype,
-               unsigned long mountflags, const void *data) FAST_FUNC;
- int xumount(const char *target, int flags) FAST_FUNC;
-+int xswapon(const char *path, int swapflags) FAST_FUNC;
-+int xswapoff(const char *path) FAST_FUNC;
- #endif
--- 
-1.7.1
-
diff --git a/packaging/sysinfo-multiple-define-error-fix.patch b/packaging/sysinfo-multiple-define-error-fix.patch
deleted file mode 100644 (file)
index 79edab1..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-diff --git a/include/libbb.h b/include/libbb.h
-index e2a8322..18bed75 100644
---- a/include/libbb.h
-+++ b/include/libbb.h
-@@ -104,6 +104,10 @@ char *dirname(char *path);
- #endif
- /* Include our own copy of struct sysinfo to avoid binary compatibility
-  * problems with Linux 2.4, which changed things.  Grumble, grumble. */
-+/* kernel-header-3.x provide sysinfo struct. So then include header which kernel provide
-+ *    - walyong.cho <walyong.cho@samsung.com> */
-+#include <linux/version.h>
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
- struct sysinfo {
-       long uptime;                    /* Seconds since boot */
-       unsigned long loads[3];         /* 1, 5, and 15 minute load averages */
-@@ -120,6 +124,9 @@ struct sysinfo {
-       unsigned int mem_unit;          /* Memory unit size in bytes */
-       char _f[20 - 2 * sizeof(long) - sizeof(int)]; /* Padding: libc5 uses this.. */
- };
-+#else
-+#include <sys/sysinfo.h>
-+#endif
- int sysinfo(struct sysinfo* info);
- #ifndef PATH_MAX
- # define PATH_MAX 256
--- 
-1.7.9.5
-
diff --git a/packaging/syslogd-disable-systemd-sa.patch b/packaging/syslogd-disable-systemd-sa.patch
deleted file mode 100644 (file)
index 63c1c95..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-diff -Nru busybox-1.17.1.ORIG//Makefile.flags busybox-1.17.1/Makefile.flags
---- busybox-1.17.1.ORIG//Makefile.flags        2012-08-03 15:39:32.345043691 +0900
-+++ busybox-1.17.1/Makefile.flags      2012-08-03 15:44:37.017043654 +0900
-@@ -115,10 +115,6 @@
- LDLIBS += dmalloc
- endif
--ifeq ($(CONFIG_SYSLOGD),y)
--LDLIBS += systemd-daemon
--endif
--
- # If a flat binary should be built, CFLAGS_busybox="-elf2flt"
- # env var should be set for make invocation.
- # Here we check whether CFLAGS_busybox indeed contains that flag.
-diff -Nru busybox-1.17.1.ORIG//sysklogd/syslogd.c busybox-1.17.1/sysklogd/syslogd.c
---- busybox-1.17.1.ORIG//sysklogd/syslogd.c    2012-08-03 15:39:32.349043691 +0900
-+++ busybox-1.17.1/sysklogd/syslogd.c  2012-08-03 15:40:05.013043687 +0900
-@@ -34,8 +34,6 @@
- #include <sys/shm.h>
- #endif
--#include "sd-daemon.h"
--
- #define DEBUG 0
- /* MARK code is not very useful, is bloat, and broken:
-@@ -513,9 +511,6 @@
-       int sock_fd;
-       char *dev_log_name;
--      if (sd_listen_fds(1) == 1 && sd_is_socket_unix(SD_LISTEN_FDS_START, SOCK_DGRAM, -1, "/dev/log", 0) > 0)
--              return SD_LISTEN_FDS_START;
--
-       memset(&sunx, 0, sizeof(sunx));
-       sunx.sun_family = AF_UNIX;
diff --git a/packaging/syslogd.manifest b/packaging/syslogd.manifest
deleted file mode 100644 (file)
index 70bcf4e..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<manifest>
-       <define>
-               <domain name="syslogd"/>
-               <permit>
-                       <smack permit="_" type="w"/>
-               </permit>
-       </define>
-        <request>
-                <domain name="_"/>
-        </request>
-        <assign>
-               <filesystem path="/sbin/syslogd" exec_label="syslogd" />
-        </assign>
-</manifest>
diff --git a/packaging/syslogd.service b/packaging/syslogd.service
deleted file mode 100644 (file)
index 23cf06c..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-[Unit]
-Description=Run syslog
-After=var.mount
-
-[Service]
-Type=forking
-ExecStart=/sbin/syslogd -b 5
-OOMScoreAdjust=-1000
-Restart=always
-RestartSec=0
-
-[Install]
-WantedBy=basic.target
diff --git a/packaging/tcpsvd-udpsvd-conditionalize-usage-of-SO_ORIGINAL_DS.patch b/packaging/tcpsvd-udpsvd-conditionalize-usage-of-SO_ORIGINAL_DS.patch
deleted file mode 100644 (file)
index eb797bc..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-From 2ea12d8b6d2a36c5d49df1ae97b86ba287835249 Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 27 May 2010 15:46:25 +0200
-Subject: [PATCH 9/9] tcpsvd,udpsvd: conditionalize usage of SO_ORIGINAL_DST
-
-On systems without this call, $TCPORIGDSTADDR is not set.
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- networking/Config.src |    2 --
- networking/tcpudp.c   |    5 +++++
- 2 files changed, 5 insertions(+), 2 deletions(-)
-
-diff --git a/networking/Config.src b/networking/Config.src
-index fc613e8..2d29c42 100644
---- a/networking/Config.src
-+++ b/networking/Config.src
-@@ -733,7 +733,6 @@ config SLATTACH
- config TCPSVD
-       bool "tcpsvd"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         tcpsvd listens on a TCP port and runs a program for each new
-         connection.
-@@ -966,7 +965,6 @@ config IFUPDOWN_UDHCPC_CMD_OPTIONS
- config UDPSVD
-       bool "udpsvd"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         udpsvd listens on an UDP port and runs a program for each new
-         connection.
-diff --git a/networking/tcpudp.c b/networking/tcpudp.c
-index 53e622b..40f6825 100644
---- a/networking/tcpudp.c
-+++ b/networking/tcpudp.c
-@@ -30,9 +30,12 @@
-  */
- #include "libbb.h"
-+
- /* Wants <limits.h> etc, thus included after libbb.h: */
-+#ifdef __linux__
- #include <linux/types.h> /* for __be32 etc */
- #include <linux/netfilter_ipv4.h>
-+#endif
- // TODO: move into this file:
- #include "tcpudp_perhost.h"
-@@ -464,6 +467,7 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
-                       /* setup ucspi env */
-                       const char *proto = tcp ? "TCP" : "UDP";
-+#ifdef SO_ORIGINAL_DST
-                       /* Extract "original" destination addr:port
-                        * from Linux firewall. Useful when you redirect
-                        * an outbond connection to local handler, and it needs
-@@ -473,6 +477,7 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
-                               xsetenv_plain("TCPORIGDSTADDR", addr);
-                               free(addr);
-                       }
-+#endif
-                       xsetenv_plain("PROTO", proto);
-                       xsetenv_proto(proto, "LOCALADDR", local_addr);
-                       xsetenv_proto(proto, "REMOTEADDR", remote_addr);
--- 
-1.7.1
-
diff --git a/packaging/tizen.config b/packaging/tizen.config
deleted file mode 100644 (file)
index cede9d2..0000000
+++ /dev/null
@@ -1,946 +0,0 @@
-#
-# Automatically generated make config: don't edit
-# Busybox version: 1.17.1
-# Wed Feb 13 20:10:00 2013
-#
-CONFIG_HAVE_DOT_CONFIG=y
-
-#
-# Busybox Settings
-#
-
-#
-# General Configuration
-#
-# CONFIG_DESKTOP is not set
-# CONFIG_EXTRA_COMPAT is not set
-# CONFIG_INCLUDE_SUSv2 is not set
-# CONFIG_USE_PORTABLE_CODE is not set
-CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
-# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
-# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
-# CONFIG_SHOW_USAGE is not set
-# CONFIG_FEATURE_VERBOSE_USAGE is not set
-# CONFIG_FEATURE_COMPRESS_USAGE is not set
-# CONFIG_FEATURE_INSTALLER is not set
-# CONFIG_LOCALE_SUPPORT is not set
-# CONFIG_UNICODE_SUPPORT is not set
-# CONFIG_UNICODE_USING_LOCALE is not set
-# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
-CONFIG_SUBST_WCHAR=0
-CONFIG_LAST_SUPPORTED_WCHAR=0
-# CONFIG_UNICODE_COMBINING_WCHARS is not set
-# CONFIG_UNICODE_WIDE_WCHARS is not set
-# CONFIG_UNICODE_BIDI_SUPPORT is not set
-# CONFIG_UNICODE_NEUTRAL_TABLE is not set
-# CONFIG_UNICODE_PRESERVE_BROKEN is not set
-# CONFIG_LONG_OPTS is not set
-# CONFIG_FEATURE_DEVPTS is not set
-# CONFIG_FEATURE_CLEAN_UP is not set
-CONFIG_FEATURE_UTMP=y
-# CONFIG_FEATURE_WTMP is not set
-# CONFIG_FEATURE_PIDFILE is not set
-CONFIG_FEATURE_SUID=y
-# CONFIG_FEATURE_SUID_CONFIG is not set
-# CONFIG_FEATURE_SUID_CONFIG_QUIET is not set
-# CONFIG_SELINUX is not set
-# CONFIG_FEATURE_PREFER_APPLETS is not set
-CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
-CONFIG_FEATURE_SYSLOG=y
-CONFIG_FEATURE_HAVE_RPC=y
-
-#
-# Build Options
-#
-# CONFIG_STATIC is not set
-# CONFIG_PIE is not set
-# CONFIG_NOMMU is not set
-# CONFIG_BUILD_LIBBUSYBOX is not set
-# CONFIG_FEATURE_INDIVIDUAL is not set
-# CONFIG_FEATURE_SHARED_BUSYBOX is not set
-# CONFIG_LFS is not set
-CONFIG_CROSS_COMPILER_PREFIX=""
-CONFIG_EXTRA_CFLAGS=""
-
-#
-# Debugging Options
-#
-# CONFIG_DEBUG is not set
-# CONFIG_DEBUG_PESSIMIZE is not set
-# CONFIG_WERROR is not set
-CONFIG_NO_DEBUG_LIB=y
-# CONFIG_DMALLOC is not set
-# CONFIG_EFENCE is not set
-
-#
-# Installation Options
-#
-# CONFIG_INSTALL_NO_USR is not set
-CONFIG_INSTALL_APPLET_SYMLINKS=y
-# CONFIG_INSTALL_APPLET_HARDLINKS is not set
-# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
-# CONFIG_INSTALL_APPLET_DONT is not set
-# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
-# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
-# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
-CONFIG_PREFIX="./_install"
-
-#
-# Busybox Library Tuning
-#
-CONFIG_PASSWORD_MINLEN=6
-CONFIG_MD5_SIZE_VS_SPEED=2
-# CONFIG_FEATURE_FAST_TOP is not set
-# CONFIG_FEATURE_ETC_NETWORKS is not set
-# CONFIG_FEATURE_EDITING is not set
-CONFIG_FEATURE_EDITING_MAX_LEN=0
-# CONFIG_FEATURE_EDITING_VI is not set
-CONFIG_FEATURE_EDITING_HISTORY=0
-# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set
-# CONFIG_FEATURE_TAB_COMPLETION is not set
-# CONFIG_FEATURE_USERNAME_COMPLETION is not set
-# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set
-# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
-# CONFIG_FEATURE_NON_POSIX_CP is not set
-# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
-CONFIG_FEATURE_COPYBUF_KB=4
-# CONFIG_MONOTONIC_SYSCALL is not set
-# CONFIG_IOCTL_HEX2STR_ERROR is not set
-# CONFIG_FEATURE_HWIB is not set
-
-#
-# Applets
-#
-
-#
-# Archival Utilities
-#
-# CONFIG_FEATURE_SEAMLESS_XZ is not set
-# CONFIG_FEATURE_SEAMLESS_LZMA is not set
-# CONFIG_FEATURE_SEAMLESS_BZ2 is not set
-# CONFIG_FEATURE_SEAMLESS_GZ is not set
-# CONFIG_FEATURE_SEAMLESS_Z is not set
-# CONFIG_AR is not set
-# CONFIG_FEATURE_AR_LONG_FILENAMES is not set
-# CONFIG_FEATURE_AR_CREATE is not set
-# CONFIG_BUNZIP2 is not set
-# CONFIG_BZIP2 is not set
-# CONFIG_CPIO is not set
-# CONFIG_FEATURE_CPIO_O is not set
-# CONFIG_FEATURE_CPIO_P is not set
-# CONFIG_DPKG is not set
-# CONFIG_DPKG_DEB is not set
-# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
-# CONFIG_GUNZIP is not set
-# CONFIG_GZIP is not set
-# CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set
-# CONFIG_LZOP is not set
-# CONFIG_LZOP_COMPR_HIGH is not set
-# CONFIG_RPM2CPIO is not set
-# CONFIG_RPM is not set
-# CONFIG_TAR is not set
-# CONFIG_FEATURE_TAR_CREATE is not set
-# CONFIG_FEATURE_TAR_AUTODETECT is not set
-# CONFIG_FEATURE_TAR_FROM is not set
-# CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY is not set
-# CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set
-# CONFIG_FEATURE_TAR_GNU_EXTENSIONS is not set
-# CONFIG_FEATURE_TAR_LONG_OPTIONS is not set
-# CONFIG_FEATURE_TAR_TO_COMMAND is not set
-# CONFIG_FEATURE_TAR_UNAME_GNAME is not set
-# CONFIG_FEATURE_TAR_NOPRESERVE_TIME is not set
-# CONFIG_FEATURE_TAR_SELINUX is not set
-# CONFIG_UNCOMPRESS is not set
-# CONFIG_UNLZMA is not set
-# CONFIG_FEATURE_LZMA_FAST is not set
-# CONFIG_LZMA is not set
-# CONFIG_UNXZ is not set
-# CONFIG_XZ is not set
-# CONFIG_UNZIP is not set
-
-#
-# Coreutils
-#
-# CONFIG_BASENAME is not set
-# CONFIG_CAT is not set
-# CONFIG_DATE is not set
-# CONFIG_FEATURE_DATE_ISOFMT is not set
-# CONFIG_FEATURE_DATE_NANO is not set
-# CONFIG_FEATURE_DATE_COMPAT is not set
-# CONFIG_TEST is not set
-# CONFIG_FEATURE_TEST_64 is not set
-# CONFIG_TR is not set
-# CONFIG_FEATURE_TR_CLASSES is not set
-# CONFIG_FEATURE_TR_EQUIV is not set
-# CONFIG_CAL is not set
-# CONFIG_CATV is not set
-# CONFIG_CHGRP is not set
-# CONFIG_CHMOD is not set
-# CONFIG_CHOWN is not set
-# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set
-# CONFIG_CHROOT is not set
-# CONFIG_CKSUM is not set
-# CONFIG_COMM is not set
-# CONFIG_CP is not set
-# CONFIG_FEATURE_CP_LONG_OPTIONS is not set
-# CONFIG_CUT is not set
-# CONFIG_DD is not set
-# CONFIG_FEATURE_DD_SIGNAL_HANDLING is not set
-# CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set
-# CONFIG_FEATURE_DD_IBS_OBS is not set
-# CONFIG_DF is not set
-# CONFIG_FEATURE_DF_FANCY is not set
-# CONFIG_DIRNAME is not set
-# CONFIG_DOS2UNIX is not set
-# CONFIG_UNIX2DOS is not set
-# CONFIG_DU is not set
-# CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K is not set
-# CONFIG_ECHO is not set
-# CONFIG_FEATURE_FANCY_ECHO is not set
-# CONFIG_ENV is not set
-# CONFIG_FEATURE_ENV_LONG_OPTIONS is not set
-# CONFIG_EXPAND is not set
-# CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set
-# CONFIG_EXPR is not set
-# CONFIG_EXPR_MATH_SUPPORT_64 is not set
-# CONFIG_FALSE is not set
-# CONFIG_FOLD is not set
-# CONFIG_FSYNC is not set
-# CONFIG_HEAD is not set
-# CONFIG_FEATURE_FANCY_HEAD is not set
-# CONFIG_HOSTID is not set
-# CONFIG_ID is not set
-# CONFIG_INSTALL is not set
-# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set
-# CONFIG_LENGTH is not set
-# CONFIG_LN is not set
-# CONFIG_LOGNAME is not set
-# CONFIG_LS is not set
-# CONFIG_FEATURE_LS_FILETYPES is not set
-# CONFIG_FEATURE_LS_FOLLOWLINKS is not set
-# CONFIG_FEATURE_LS_RECURSIVE is not set
-# CONFIG_FEATURE_LS_SORTFILES is not set
-# CONFIG_FEATURE_LS_TIMESTAMPS is not set
-# CONFIG_FEATURE_LS_USERNAME is not set
-# CONFIG_FEATURE_LS_COLOR is not set
-# CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set
-# CONFIG_MD5SUM is not set
-# CONFIG_MKDIR is not set
-# CONFIG_FEATURE_MKDIR_LONG_OPTIONS is not set
-# CONFIG_MKFIFO is not set
-# CONFIG_MKNOD is not set
-# CONFIG_MV is not set
-# CONFIG_FEATURE_MV_LONG_OPTIONS is not set
-# CONFIG_NICE is not set
-# CONFIG_NOHUP is not set
-# CONFIG_OD is not set
-# CONFIG_PRINTENV is not set
-# CONFIG_PRINTF is not set
-# CONFIG_PWD is not set
-# CONFIG_READLINK is not set
-# CONFIG_FEATURE_READLINK_FOLLOW is not set
-# CONFIG_REALPATH is not set
-# CONFIG_RM is not set
-# CONFIG_RMDIR is not set
-# CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set
-# CONFIG_SEQ is not set
-# CONFIG_SHA1SUM is not set
-# CONFIG_SHA256SUM is not set
-# CONFIG_SHA512SUM is not set
-# CONFIG_SLEEP is not set
-# CONFIG_FEATURE_FANCY_SLEEP is not set
-# CONFIG_FEATURE_FLOAT_SLEEP is not set
-# CONFIG_SORT is not set
-# CONFIG_FEATURE_SORT_BIG is not set
-# CONFIG_SPLIT is not set
-# CONFIG_FEATURE_SPLIT_FANCY is not set
-# CONFIG_STAT is not set
-# CONFIG_FEATURE_STAT_FORMAT is not set
-# CONFIG_STTY is not set
-# CONFIG_SUM is not set
-# CONFIG_SYNC is not set
-# CONFIG_TAC is not set
-# CONFIG_TAIL is not set
-# CONFIG_FEATURE_FANCY_TAIL is not set
-# CONFIG_TEE is not set
-# CONFIG_FEATURE_TEE_USE_BLOCK_IO is not set
-# CONFIG_TOUCH is not set
-# CONFIG_TRUE is not set
-# CONFIG_TTY is not set
-# CONFIG_UNAME is not set
-# CONFIG_UNEXPAND is not set
-# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set
-# CONFIG_UNIQ is not set
-# CONFIG_USLEEP is not set
-# CONFIG_UUDECODE is not set
-# CONFIG_UUENCODE is not set
-# CONFIG_WC is not set
-# CONFIG_FEATURE_WC_LARGE is not set
-# CONFIG_WHO is not set
-# CONFIG_WHOAMI is not set
-# CONFIG_YES is not set
-# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set
-# CONFIG_FEATURE_AUTOWIDTH is not set
-# CONFIG_FEATURE_HUMAN_READABLE is not set
-# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set
-
-#
-# Console Utilities
-#
-# CONFIG_CHVT is not set
-# CONFIG_FGCONSOLE is not set
-# CONFIG_CLEAR is not set
-# CONFIG_DEALLOCVT is not set
-# CONFIG_DUMPKMAP is not set
-# CONFIG_KBD_MODE is not set
-# CONFIG_LOADFONT is not set
-# CONFIG_LOADKMAP is not set
-# CONFIG_OPENVT is not set
-# CONFIG_RESET is not set
-# CONFIG_RESIZE is not set
-# CONFIG_FEATURE_RESIZE_PRINT is not set
-# CONFIG_SETCONSOLE is not set
-# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
-# CONFIG_SETFONT is not set
-# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
-CONFIG_DEFAULT_SETFONT_DIR=""
-# CONFIG_SETKEYCODES is not set
-# CONFIG_SETLOGCONS is not set
-# CONFIG_SHOWKEY is not set
-# CONFIG_FEATURE_LOADFONT_PSF2 is not set
-# CONFIG_FEATURE_LOADFONT_RAW is not set
-
-#
-# Debian Utilities
-#
-# CONFIG_MKTEMP is not set
-# CONFIG_PIPE_PROGRESS is not set
-# CONFIG_RUN_PARTS is not set
-# CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set
-# CONFIG_FEATURE_RUN_PARTS_FANCY is not set
-# CONFIG_START_STOP_DAEMON is not set
-# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set
-# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set
-# CONFIG_WHICH is not set
-
-#
-# Editors
-#
-# CONFIG_AWK is not set
-# CONFIG_FEATURE_AWK_LIBM is not set
-# CONFIG_CMP is not set
-# CONFIG_DIFF is not set
-# CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set
-# CONFIG_FEATURE_DIFF_DIR is not set
-# CONFIG_ED is not set
-# CONFIG_PATCH is not set
-# CONFIG_SED is not set
-CONFIG_VI=y
-CONFIG_FEATURE_VI_MAX_LEN=4096
-# CONFIG_FEATURE_VI_8BIT is not set
-CONFIG_FEATURE_VI_COLON=y
-CONFIG_FEATURE_VI_YANKMARK=y
-CONFIG_FEATURE_VI_SEARCH=y
-CONFIG_FEATURE_VI_USE_SIGNALS=y
-CONFIG_FEATURE_VI_DOT_CMD=y
-CONFIG_FEATURE_VI_READONLY=y
-CONFIG_FEATURE_VI_SETOPTS=y
-CONFIG_FEATURE_VI_SET=y
-CONFIG_FEATURE_VI_WIN_RESIZE=y
-CONFIG_FEATURE_VI_ASK_TERMINAL=y
-CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y
-CONFIG_FEATURE_ALLOW_EXEC=y
-
-#
-# Finding Utilities
-#
-# CONFIG_FIND is not set
-# CONFIG_FEATURE_FIND_PRINT0 is not set
-# CONFIG_FEATURE_FIND_MTIME is not set
-# CONFIG_FEATURE_FIND_MMIN is not set
-# CONFIG_FEATURE_FIND_PERM is not set
-# CONFIG_FEATURE_FIND_TYPE is not set
-# CONFIG_FEATURE_FIND_XDEV is not set
-# CONFIG_FEATURE_FIND_MAXDEPTH is not set
-# CONFIG_FEATURE_FIND_NEWER is not set
-# CONFIG_FEATURE_FIND_INUM is not set
-# CONFIG_FEATURE_FIND_EXEC is not set
-# CONFIG_FEATURE_FIND_USER is not set
-# CONFIG_FEATURE_FIND_GROUP is not set
-# CONFIG_FEATURE_FIND_NOT is not set
-# CONFIG_FEATURE_FIND_DEPTH is not set
-# CONFIG_FEATURE_FIND_PAREN is not set
-# CONFIG_FEATURE_FIND_SIZE is not set
-# CONFIG_FEATURE_FIND_PRUNE is not set
-# CONFIG_FEATURE_FIND_DELETE is not set
-# CONFIG_FEATURE_FIND_PATH is not set
-# CONFIG_FEATURE_FIND_REGEX is not set
-# CONFIG_FEATURE_FIND_CONTEXT is not set
-# CONFIG_FEATURE_FIND_LINKS is not set
-# CONFIG_GREP is not set
-# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set
-# CONFIG_FEATURE_GREP_FGREP_ALIAS is not set
-# CONFIG_FEATURE_GREP_CONTEXT is not set
-# CONFIG_XARGS is not set
-# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set
-# CONFIG_FEATURE_XARGS_SUPPORT_QUOTES is not set
-# CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT is not set
-# CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM is not set
-
-#
-# Init Utilities
-#
-# CONFIG_BOOTCHARTD is not set
-# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set
-# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set
-# CONFIG_INIT is not set
-# CONFIG_FEATURE_USE_INITTAB is not set
-# CONFIG_FEATURE_KILL_REMOVED is not set
-CONFIG_FEATURE_KILL_DELAY=0
-# CONFIG_FEATURE_INIT_SCTTY is not set
-# CONFIG_FEATURE_INIT_SYSLOG is not set
-# CONFIG_FEATURE_EXTRA_QUIET is not set
-# CONFIG_FEATURE_INIT_COREDUMPS is not set
-# CONFIG_FEATURE_INITRD is not set
-# CONFIG_HALT is not set
-# CONFIG_FEATURE_CALL_TELINIT is not set
-CONFIG_TELINIT_PATH=""
-# CONFIG_MESG is not set
-
-#
-# Login/Password Management Utilities
-#
-# CONFIG_FEATURE_SHADOWPASSWDS is not set
-# CONFIG_USE_BB_PWD_GRP is not set
-# CONFIG_USE_BB_SHADOW is not set
-# CONFIG_USE_BB_CRYPT is not set
-# CONFIG_USE_BB_CRYPT_SHA is not set
-# CONFIG_ADDGROUP is not set
-# CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set
-# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
-# CONFIG_DELGROUP is not set
-# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
-# CONFIG_FEATURE_CHECK_NAMES is not set
-# CONFIG_ADDUSER is not set
-# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set
-CONFIG_FIRST_SYSTEM_ID=0
-CONFIG_LAST_SYSTEM_ID=0
-# CONFIG_DELUSER is not set
-# CONFIG_GETTY is not set
-# CONFIG_LOGIN is not set
-# CONFIG_PAM is not set
-# CONFIG_LOGIN_SCRIPTS is not set
-# CONFIG_FEATURE_NOLOGIN is not set
-# CONFIG_FEATURE_SECURETTY is not set
-# CONFIG_PASSWD is not set
-# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set
-# CONFIG_CRYPTPW is not set
-# CONFIG_CHPASSWD is not set
-# CONFIG_SU is not set
-# CONFIG_FEATURE_SU_SYSLOG is not set
-# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set
-# CONFIG_SULOGIN is not set
-# CONFIG_VLOCK is not set
-
-#
-# Linux Ext2 FS Progs
-#
-# CONFIG_CHATTR is not set
-# CONFIG_FSCK is not set
-# CONFIG_LSATTR is not set
-# CONFIG_TUNE2FS is not set
-
-#
-# Linux Module Utilities
-#
-# CONFIG_MODINFO is not set
-# CONFIG_MODPROBE_SMALL is not set
-# CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set
-# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set
-# CONFIG_INSMOD is not set
-# CONFIG_RMMOD is not set
-# CONFIG_LSMOD is not set
-# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set
-# CONFIG_MODPROBE is not set
-# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set
-# CONFIG_DEPMOD is not set
-
-#
-# Options common to multiple modutils
-#
-# CONFIG_FEATURE_2_4_MODULES is not set
-# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set
-# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
-# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
-# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
-# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
-# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set
-# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
-# CONFIG_FEATURE_MODUTILS_ALIAS is not set
-# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set
-CONFIG_DEFAULT_MODULES_DIR=""
-CONFIG_DEFAULT_DEPMOD_FILE=""
-
-#
-# Linux System Utilities
-#
-# CONFIG_REV is not set
-# CONFIG_ACPID is not set
-# CONFIG_FEATURE_ACPID_COMPAT is not set
-# CONFIG_BLKID is not set
-# CONFIG_DMESG is not set
-# CONFIG_FEATURE_DMESG_PRETTY is not set
-# CONFIG_FBSET is not set
-# CONFIG_FEATURE_FBSET_FANCY is not set
-# CONFIG_FEATURE_FBSET_READMODE is not set
-# CONFIG_FDFLUSH is not set
-# CONFIG_FDFORMAT is not set
-# CONFIG_FDISK is not set
-# CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set
-# CONFIG_FEATURE_FDISK_WRITABLE is not set
-# CONFIG_FEATURE_AIX_LABEL is not set
-# CONFIG_FEATURE_SGI_LABEL is not set
-# CONFIG_FEATURE_SUN_LABEL is not set
-# CONFIG_FEATURE_OSF_LABEL is not set
-# CONFIG_FEATURE_FDISK_ADVANCED is not set
-# CONFIG_FINDFS is not set
-# CONFIG_FLOCK is not set
-# CONFIG_FREERAMDISK is not set
-# CONFIG_FSCK_MINIX is not set
-# CONFIG_MKFS_EXT2 is not set
-# CONFIG_MKFS_MINIX is not set
-# CONFIG_FEATURE_MINIX2 is not set
-# CONFIG_MKFS_REISER is not set
-# CONFIG_MKFS_VFAT is not set
-# CONFIG_GETOPT is not set
-# CONFIG_FEATURE_GETOPT_LONG is not set
-# CONFIG_HEXDUMP is not set
-# CONFIG_FEATURE_HEXDUMP_REVERSE is not set
-# CONFIG_HD is not set
-# CONFIG_HWCLOCK is not set
-# CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set
-# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set
-# CONFIG_IPCRM is not set
-# CONFIG_IPCS is not set
-# CONFIG_LOSETUP is not set
-# CONFIG_LSPCI is not set
-# CONFIG_LSUSB is not set
-# CONFIG_MDEV is not set
-# CONFIG_FEATURE_MDEV_CONF is not set
-# CONFIG_FEATURE_MDEV_RENAME is not set
-# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set
-# CONFIG_FEATURE_MDEV_EXEC is not set
-# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set
-# CONFIG_MKSWAP is not set
-# CONFIG_FEATURE_MKSWAP_UUID is not set
-# CONFIG_MORE is not set
-# CONFIG_FEATURE_USE_TERMIOS is not set
-CONFIG_MOUNT=y
-CONFIG_FEATURE_MOUNT_FAKE=y
-CONFIG_FEATURE_MOUNT_VERBOSE=y
-# CONFIG_FEATURE_MOUNT_HELPERS is not set
-CONFIG_FEATURE_MOUNT_LABEL=y
-CONFIG_FEATURE_MOUNT_NFS=y
-CONFIG_FEATURE_MOUNT_CIFS=y
-CONFIG_FEATURE_MOUNT_FLAGS=y
-CONFIG_FEATURE_MOUNT_FSTAB=y
-# CONFIG_PIVOT_ROOT is not set
-# CONFIG_RDATE is not set
-# CONFIG_RDEV is not set
-# CONFIG_READPROFILE is not set
-# CONFIG_RTCWAKE is not set
-# CONFIG_SCRIPT is not set
-# CONFIG_SCRIPTREPLAY is not set
-# CONFIG_SETARCH is not set
-# CONFIG_SWAPONOFF is not set
-# CONFIG_FEATURE_SWAPON_PRI is not set
-# CONFIG_SWITCH_ROOT is not set
-CONFIG_UMOUNT=y
-CONFIG_FEATURE_UMOUNT_ALL=y
-
-#
-# Common options for mount/umount
-#
-CONFIG_FEATURE_MOUNT_LOOP=y
-CONFIG_FEATURE_MOUNT_LOOP_CREATE=y
-# CONFIG_FEATURE_MTAB_SUPPORT is not set
-CONFIG_VOLUMEID=y
-
-#
-# Filesystem/Volume identification
-#
-CONFIG_FEATURE_VOLUMEID_EXT=y
-CONFIG_FEATURE_VOLUMEID_BTRFS=y
-CONFIG_FEATURE_VOLUMEID_REISERFS=y
-CONFIG_FEATURE_VOLUMEID_FAT=y
-CONFIG_FEATURE_VOLUMEID_HFS=y
-CONFIG_FEATURE_VOLUMEID_JFS=y
-CONFIG_FEATURE_VOLUMEID_XFS=y
-CONFIG_FEATURE_VOLUMEID_NTFS=y
-CONFIG_FEATURE_VOLUMEID_ISO9660=y
-CONFIG_FEATURE_VOLUMEID_UDF=y
-CONFIG_FEATURE_VOLUMEID_LUKS=y
-CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y
-CONFIG_FEATURE_VOLUMEID_CRAMFS=y
-CONFIG_FEATURE_VOLUMEID_ROMFS=y
-CONFIG_FEATURE_VOLUMEID_SYSV=y
-CONFIG_FEATURE_VOLUMEID_OCFS2=y
-CONFIG_FEATURE_VOLUMEID_LINUXRAID=y
-
-#
-# Miscellaneous Utilities
-#
-# CONFIG_CONSPY is not set
-# CONFIG_UBIATTACH is not set
-# CONFIG_UBIDETACH is not set
-# CONFIG_ADJTIMEX is not set
-# CONFIG_BBCONFIG is not set
-# CONFIG_BEEP is not set
-CONFIG_FEATURE_BEEP_FREQ=0
-CONFIG_FEATURE_BEEP_LENGTH_MS=0
-# CONFIG_CHAT is not set
-# CONFIG_FEATURE_CHAT_NOFAIL is not set
-# CONFIG_FEATURE_CHAT_TTY_HIFI is not set
-# CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set
-# CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set
-# CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set
-# CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set
-# CONFIG_FEATURE_CHAT_CLR_ABORT is not set
-# CONFIG_CHRT is not set
-# CONFIG_CROND is not set
-# CONFIG_FEATURE_CROND_D is not set
-# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
-CONFIG_FEATURE_CROND_DIR=""
-# CONFIG_CRONTAB is not set
-# CONFIG_DC is not set
-# CONFIG_FEATURE_DC_LIBM is not set
-# CONFIG_DEVFSD is not set
-# CONFIG_DEVFSD_MODLOAD is not set
-# CONFIG_DEVFSD_FG_NP is not set
-# CONFIG_DEVFSD_VERBOSE is not set
-# CONFIG_FEATURE_DEVFS is not set
-# CONFIG_DEVMEM is not set
-# CONFIG_EJECT is not set
-# CONFIG_FEATURE_EJECT_SCSI is not set
-# CONFIG_FBSPLASH is not set
-# CONFIG_FLASHCP is not set
-# CONFIG_FLASH_LOCK is not set
-# CONFIG_FLASH_UNLOCK is not set
-# CONFIG_FLASH_ERASEALL is not set
-# CONFIG_IONICE is not set
-# CONFIG_INOTIFYD is not set
-# CONFIG_LAST is not set
-# CONFIG_FEATURE_LAST_SMALL is not set
-# CONFIG_FEATURE_LAST_FANCY is not set
-# CONFIG_LESS is not set
-CONFIG_FEATURE_LESS_MAXLINES=0
-# CONFIG_FEATURE_LESS_BRACKETS is not set
-# CONFIG_FEATURE_LESS_FLAGS is not set
-# CONFIG_FEATURE_LESS_MARKS is not set
-# CONFIG_FEATURE_LESS_REGEXP is not set
-# CONFIG_FEATURE_LESS_WINCH is not set
-# CONFIG_FEATURE_LESS_DASHCMD is not set
-# CONFIG_FEATURE_LESS_LINENUMS is not set
-# CONFIG_HDPARM is not set
-# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set
-# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set
-# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set
-# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set
-# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set
-# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set
-# CONFIG_MAKEDEVS is not set
-# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
-# CONFIG_FEATURE_MAKEDEVS_TABLE is not set
-# CONFIG_MAN is not set
-# CONFIG_MICROCOM is not set
-# CONFIG_MOUNTPOINT is not set
-# CONFIG_MT is not set
-# CONFIG_RAIDAUTORUN is not set
-# CONFIG_READAHEAD is not set
-# CONFIG_RFKILL is not set
-# CONFIG_RUNLEVEL is not set
-# CONFIG_RX is not set
-# CONFIG_SETSID is not set
-# CONFIG_STRINGS is not set
-# CONFIG_TASKSET is not set
-# CONFIG_FEATURE_TASKSET_FANCY is not set
-# CONFIG_TIME is not set
-# CONFIG_TIMEOUT is not set
-# CONFIG_TTYSIZE is not set
-# CONFIG_VOLNAME is not set
-# CONFIG_WALL is not set
-# CONFIG_WATCHDOG is not set
-
-#
-# Networking Utilities
-#
-# CONFIG_NC is not set
-# CONFIG_NC_SERVER is not set
-# CONFIG_NC_EXTRA is not set
-# CONFIG_NC_110_COMPAT is not set
-# CONFIG_FEATURE_IPV6 is not set
-# CONFIG_FEATURE_UNIX_LOCAL is not set
-# CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set
-# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
-# CONFIG_ARP is not set
-# CONFIG_ARPING is not set
-# CONFIG_BRCTL is not set
-# CONFIG_FEATURE_BRCTL_FANCY is not set
-# CONFIG_FEATURE_BRCTL_SHOW is not set
-# CONFIG_DNSD is not set
-# CONFIG_ETHER_WAKE is not set
-# CONFIG_FAKEIDENTD is not set
-# CONFIG_FTPD is not set
-# CONFIG_FEATURE_FTP_WRITE is not set
-# CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set
-# CONFIG_FTPGET is not set
-# CONFIG_FTPPUT is not set
-# CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set
-# CONFIG_HOSTNAME is not set
-# CONFIG_HTTPD is not set
-# CONFIG_FEATURE_HTTPD_RANGES is not set
-# CONFIG_FEATURE_HTTPD_USE_SENDFILE is not set
-# CONFIG_FEATURE_HTTPD_SETUID is not set
-# CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set
-# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
-# CONFIG_FEATURE_HTTPD_CGI is not set
-# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
-# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
-# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set
-# CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set
-# CONFIG_FEATURE_HTTPD_PROXY is not set
-# CONFIG_IFCONFIG is not set
-# CONFIG_FEATURE_IFCONFIG_STATUS is not set
-# CONFIG_FEATURE_IFCONFIG_SLIP is not set
-# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
-# CONFIG_FEATURE_IFCONFIG_HW is not set
-# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
-# CONFIG_IFENSLAVE is not set
-# CONFIG_IFPLUGD is not set
-# CONFIG_IFUPDOWN is not set
-CONFIG_IFUPDOWN_IFSTATE_PATH=""
-# CONFIG_FEATURE_IFUPDOWN_IP is not set
-# CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN is not set
-# CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set
-# CONFIG_FEATURE_IFUPDOWN_IPV4 is not set
-# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set
-# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set
-# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set
-# CONFIG_INETD is not set
-# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set
-# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set
-# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set
-# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set
-# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set
-# CONFIG_FEATURE_INETD_RPC is not set
-# CONFIG_IP is not set
-# CONFIG_FEATURE_IP_ADDRESS is not set
-# CONFIG_FEATURE_IP_LINK is not set
-# CONFIG_FEATURE_IP_ROUTE is not set
-# CONFIG_FEATURE_IP_TUNNEL is not set
-# CONFIG_FEATURE_IP_RULE is not set
-# CONFIG_FEATURE_IP_SHORT_FORMS is not set
-# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
-# CONFIG_IPADDR is not set
-# CONFIG_IPLINK is not set
-# CONFIG_IPROUTE is not set
-# CONFIG_IPTUNNEL is not set
-# CONFIG_IPRULE is not set
-# CONFIG_IPCALC is not set
-# CONFIG_FEATURE_IPCALC_FANCY is not set
-# CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set
-# CONFIG_NAMEIF is not set
-# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
-# CONFIG_NETSTAT is not set
-# CONFIG_FEATURE_NETSTAT_WIDE is not set
-# CONFIG_FEATURE_NETSTAT_PRG is not set
-# CONFIG_NSLOOKUP is not set
-# CONFIG_NTPD is not set
-# CONFIG_FEATURE_NTPD_SERVER is not set
-# CONFIG_PING is not set
-# CONFIG_PING6 is not set
-# CONFIG_FEATURE_FANCY_PING is not set
-# CONFIG_PSCAN is not set
-# CONFIG_ROUTE is not set
-# CONFIG_SLATTACH is not set
-# CONFIG_TCPSVD is not set
-# CONFIG_TELNET is not set
-# CONFIG_FEATURE_TELNET_TTYPE is not set
-# CONFIG_FEATURE_TELNET_AUTOLOGIN is not set
-# CONFIG_TELNETD is not set
-# CONFIG_FEATURE_TELNETD_STANDALONE is not set
-# CONFIG_FEATURE_TELNETD_INETD_WAIT is not set
-# CONFIG_TFTP is not set
-# CONFIG_TFTPD is not set
-# CONFIG_FEATURE_TFTP_GET is not set
-# CONFIG_FEATURE_TFTP_PUT is not set
-# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set
-# CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set
-# CONFIG_TFTP_DEBUG is not set
-# CONFIG_TRACEROUTE is not set
-# CONFIG_TRACEROUTE6 is not set
-# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
-# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
-# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
-# CONFIG_TUNCTL is not set
-# CONFIG_FEATURE_TUNCTL_UG is not set
-CONFIG_UDHCPD=y
-# CONFIG_DHCPRELAY is not set
-CONFIG_DUMPLEASES=y
-CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY=y
-CONFIG_DHCPD_LEASES_FILE="/var/lib/misc/udhcpd.leases"
-CONFIG_UDHCPC=y
-CONFIG_FEATURE_UDHCPC_ARPING=y
-CONFIG_FEATURE_UDHCP_PORT=y
-CONFIG_UDHCP_DEBUG=9
-CONFIG_FEATURE_UDHCP_RFC3397=y
-CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script"
-CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80
-CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS=""
-# CONFIG_UDPSVD is not set
-# CONFIG_VCONFIG is not set
-# CONFIG_WGET is not set
-# CONFIG_FEATURE_WGET_STATUSBAR is not set
-# CONFIG_FEATURE_WGET_AUTHENTICATION is not set
-# CONFIG_FEATURE_WGET_LONG_OPTIONS is not set
-# CONFIG_ZCIP is not set
-
-#
-# Print Utilities
-#
-# CONFIG_LPD is not set
-# CONFIG_LPR is not set
-# CONFIG_LPQ is not set
-
-#
-# Mail Utilities
-#
-# CONFIG_MAKEMIME is not set
-CONFIG_FEATURE_MIME_CHARSET=""
-# CONFIG_POPMAILDIR is not set
-# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set
-# CONFIG_REFORMIME is not set
-# CONFIG_FEATURE_REFORMIME_COMPAT is not set
-# CONFIG_SENDMAIL is not set
-
-#
-# Process Utilities
-#
-# CONFIG_SMEMCAP is not set
-# CONFIG_FREE is not set
-# CONFIG_FUSER is not set
-# CONFIG_KILL is not set
-# CONFIG_KILLALL is not set
-# CONFIG_KILLALL5 is not set
-# CONFIG_NMETER is not set
-# CONFIG_PGREP is not set
-# CONFIG_PIDOF is not set
-# CONFIG_FEATURE_PIDOF_SINGLE is not set
-# CONFIG_FEATURE_PIDOF_OMIT is not set
-# CONFIG_PKILL is not set
-# CONFIG_PS is not set
-# CONFIG_FEATURE_PS_WIDE is not set
-# CONFIG_FEATURE_PS_TIME is not set
-# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set
-# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
-# CONFIG_RENICE is not set
-# CONFIG_BB_SYSCTL is not set
-# CONFIG_TOP is not set
-# CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set
-# CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set
-# CONFIG_FEATURE_TOP_SMP_CPU is not set
-# CONFIG_FEATURE_TOP_DECIMALS is not set
-# CONFIG_FEATURE_TOP_SMP_PROCESS is not set
-# CONFIG_FEATURE_TOPMEM is not set
-# CONFIG_FEATURE_SHOW_THREADS is not set
-# CONFIG_UPTIME is not set
-# CONFIG_WATCH is not set
-
-#
-# Runit Utilities
-#
-# CONFIG_RUNSV is not set
-# CONFIG_RUNSVDIR is not set
-# CONFIG_FEATURE_RUNSVDIR_LOG is not set
-# CONFIG_SV is not set
-CONFIG_SV_DEFAULT_SERVICE_DIR=""
-# CONFIG_SVLOGD is not set
-# CONFIG_CHPST is not set
-# CONFIG_SETUIDGID is not set
-# CONFIG_ENVUIDGID is not set
-# CONFIG_ENVDIR is not set
-# CONFIG_SOFTLIMIT is not set
-# CONFIG_CHCON is not set
-# CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set
-# CONFIG_GETENFORCE is not set
-# CONFIG_GETSEBOOL is not set
-# CONFIG_LOAD_POLICY is not set
-# CONFIG_MATCHPATHCON is not set
-# CONFIG_RESTORECON is not set
-# CONFIG_RUNCON is not set
-# CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set
-# CONFIG_SELINUXENABLED is not set
-# CONFIG_SETENFORCE is not set
-# CONFIG_SETFILES is not set
-# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set
-# CONFIG_SETSEBOOL is not set
-# CONFIG_SESTATUS is not set
-
-#
-# Shells
-#
-# CONFIG_ASH is not set
-# CONFIG_ASH_BASH_COMPAT is not set
-# CONFIG_ASH_JOB_CONTROL is not set
-# CONFIG_ASH_ALIAS is not set
-# CONFIG_ASH_GETOPTS is not set
-# CONFIG_ASH_BUILTIN_ECHO is not set
-# CONFIG_ASH_BUILTIN_PRINTF is not set
-# CONFIG_ASH_BUILTIN_TEST is not set
-# CONFIG_ASH_CMDCMD is not set
-# CONFIG_ASH_MAIL is not set
-# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set
-# CONFIG_ASH_RANDOM_SUPPORT is not set
-# CONFIG_ASH_EXPAND_PRMT is not set
-# CONFIG_HUSH is not set
-# CONFIG_HUSH_BASH_COMPAT is not set
-# CONFIG_HUSH_HELP is not set
-# CONFIG_HUSH_INTERACTIVE is not set
-# CONFIG_HUSH_JOB is not set
-# CONFIG_HUSH_TICK is not set
-# CONFIG_HUSH_IF is not set
-# CONFIG_HUSH_LOOPS is not set
-# CONFIG_HUSH_CASE is not set
-# CONFIG_HUSH_FUNCTIONS is not set
-# CONFIG_HUSH_LOCAL is not set
-# CONFIG_HUSH_EXPORT_N is not set
-# CONFIG_HUSH_RANDOM_SUPPORT is not set
-# CONFIG_FEATURE_SH_IS_ASH is not set
-# CONFIG_FEATURE_SH_IS_HUSH is not set
-CONFIG_FEATURE_SH_IS_NONE=y
-# CONFIG_FEATURE_BASH_IS_ASH is not set
-# CONFIG_FEATURE_BASH_IS_HUSH is not set
-CONFIG_FEATURE_BASH_IS_NONE=y
-# CONFIG_LASH is not set
-# CONFIG_MSH is not set
-# CONFIG_SH_MATH_SUPPORT is not set
-# CONFIG_SH_MATH_SUPPORT_64 is not set
-# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
-# CONFIG_FEATURE_SH_STANDALONE is not set
-# CONFIG_FEATURE_SH_NOFORK is not set
-# CONFIG_CTTYHACK is not set
-
-#
-# System Logging Utilities
-#
-CONFIG_SYSLOGD=y
-CONFIG_FEATURE_ROTATE_LOGFILE=y
-CONFIG_FEATURE_REMOTE_LOG=y
-CONFIG_FEATURE_SYSLOGD_DUP=y
-CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=300
-CONFIG_FEATURE_IPC_SYSLOG=y
-CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16
-# CONFIG_LOGREAD is not set
-# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
-CONFIG_KLOGD=y
-# CONFIG_LOGGER is not set
diff --git a/packaging/top-display-rss.patch b/packaging/top-display-rss.patch
deleted file mode 100644 (file)
index e7776f1..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-diff --git a/procps/top.c b/procps/top.c
-index ec84374..9049578 100644
---- a/procps/top.c
-+++ b/procps/top.c
-@@ -35,7 +35,7 @@
- typedef struct top_status_t {
--      unsigned long vsz;
-+      unsigned long rss;
- #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
-       unsigned long ticks;
-       unsigned pcpu; /* delta of ticks */
-@@ -147,8 +147,8 @@ static int pid_sort(top_status_t *P, top_status_t *Q)
- static int mem_sort(top_status_t *P, top_status_t *Q)
- {
-       /* We want to avoid unsigned->signed and truncation errors */
--      if (Q->vsz < P->vsz) return -1;
--      return Q->vsz != P->vsz; /* 0 if ==, 1 if > */
-+      if (Q->rss < P->rss) return -1;
-+      return Q->rss != P->rss; /* 0 if ==, 1 if > */
- }
-@@ -519,7 +519,7 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width)
-       /* what info of the processes is shown */
-       printf(OPT_BATCH_MODE ? "%.*s" : "\033[7m%.*s\033[0m", scr_width,
--              "  PID  PPID USER     STAT   VSZ %MEM"
-+              "  PID  PPID USER     STAT   RSS %MEM"
-               IF_FEATURE_TOP_SMP_PROCESS(" CPU")
-               IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
-               " COMMAND");
-@@ -588,16 +588,16 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width)
-       s = top;
-       while (--lines_rem >= 0) {
-               unsigned col;
--              CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift);
-+              CALC_STAT(pmem, (s->rss*pmem_scale + pmem_half) >> pmem_shift);
- #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
-               CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
- #endif
--              if (s->vsz >= 100000)
--                      sprintf(vsz_str_buf, "%6ldm", s->vsz/1024);
-+              if (s->rss >= 100000)
-+                      sprintf(vsz_str_buf, "%6ldm", s->rss/1024);
-               else
--                      sprintf(vsz_str_buf, "%7ld", s->vsz);
--              /* PID PPID USER STAT VSZ %MEM [%CPU] COMMAND */
-+                      sprintf(vsz_str_buf, "%7ld", s->rss);
-+              /* PID PPID USER STAT VSZ %RSS [%CPU] COMMAND */
-               col = snprintf(line_buf, scr_width,
-                               "\n" "%5u%6u %-8.8s %s%s" FMT
-                               IF_FEATURE_TOP_SMP_PROCESS(" %3d")
-@@ -929,7 +929,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
-                               top = xrealloc_vector(top, 6, ntop++);
-                               top[n].pid = p->pid;
-                               top[n].ppid = p->ppid;
--                              top[n].vsz = p->vsz;
-+                              top[n].rss = p->rss;
- #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
-                               top[n].ticks = p->stime + p->utime;
- #endif
diff --git a/packaging/u-mount-FreeBSD-support.patch b/packaging/u-mount-FreeBSD-support.patch
deleted file mode 100644 (file)
index 702c051..0000000
+++ /dev/null
@@ -1,386 +0,0 @@
-From 5a075618b1deb735a6170e322052c7ba54b17d9e Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 29 Jul 2010 21:16:09 +0200
-Subject: [PATCH 18/19] (u)mount: FreeBSD support
-
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
----
- util-linux/Config.src |    4 +--
- util-linux/Kbuild.src |    4 +-
- util-linux/mount.c    |   41 ++-------------------
- util-linux/umount.c   |   41 +++------------------
- util-linux/xmount.c   |   66 +++++++++++++++++++++++++++++++++
- util-linux/xmount.h   |   97 +++++++++++++++++++++++++++++++++++++++++++++++++
- 6 files changed, 174 insertions(+), 79 deletions(-)
- create mode 100644 util-linux/xmount.c
- create mode 100644 util-linux/xmount.h
-
-diff --git a/util-linux/Config.src b/util-linux/Config.src
-index 98953c1..99a6fbe 100644
---- a/util-linux/Config.src
-+++ b/util-linux/Config.src
-@@ -492,7 +492,6 @@ config FEATURE_USE_TERMIOS
- config MOUNT
-       bool "mount"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         All files and filesystems in Unix are arranged into one big directory
-         tree. The 'mount' utility is used to graft a filesystem onto a
-@@ -679,7 +678,6 @@ config SWITCH_ROOT
- config UMOUNT
-       bool "umount"
-       default y
--      depends on PLATFORM_LINUX
-       help
-         When you want to remove a mounted filesystem from its current mount
-         point, for example when you are shutting down the system, the
-@@ -699,7 +697,7 @@ comment "Common options for mount/umount"
- config FEATURE_MOUNT_LOOP
-       bool "Support loopback mounts"
-       default y
--      depends on MOUNT || UMOUNT
-+      depends on (MOUNT || UMOUNT) && PLATFORM_LINUX
-       help
-         Enabling this feature allows automatic mounting of files (containing
-         filesystem images) via the linux kernel's loopback devices.
-diff --git a/util-linux/Kbuild.src b/util-linux/Kbuild.src
-index afc0db5..312fc9e 100644
---- a/util-linux/Kbuild.src
-+++ b/util-linux/Kbuild.src
-@@ -33,7 +33,7 @@ lib-$(CONFIG_MKFS_REISER)       += mkfs_reiser.o
- lib-$(CONFIG_MKFS_VFAT)         += mkfs_vfat.o
- lib-$(CONFIG_MKSWAP)            += mkswap.o
- lib-$(CONFIG_MORE)              += more.o
--lib-$(CONFIG_MOUNT)             += mount.o
-+lib-$(CONFIG_MOUNT)             += mount.o xmount.o
- lib-$(CONFIG_PIVOT_ROOT)        += pivot_root.o
- lib-$(CONFIG_RDATE)             += rdate.o
- lib-$(CONFIG_RDEV)              += rdev.o
-@@ -44,4 +44,4 @@ lib-$(CONFIG_SCRIPTREPLAY)      += scriptreplay.o
- lib-$(CONFIG_SETARCH)           += setarch.o
- lib-$(CONFIG_SWAPONOFF)         += swaponoff.o
- lib-$(CONFIG_SWITCH_ROOT)       += switch_root.o
--lib-$(CONFIG_UMOUNT)            += umount.o
-+lib-$(CONFIG_UMOUNT)            += umount.o xmount.o
-diff --git a/util-linux/mount.c b/util-linux/mount.c
-index 9107e43..a62c4e8 100644
---- a/util-linux/mount.c
-+++ b/util-linux/mount.c
-@@ -18,44 +18,9 @@
- //
- #include <mntent.h>
- #include <syslog.h>
--#include <sys/mount.h>
--// Grab more as needed from util-linux's mount/mount_constants.h
--#ifndef MS_DIRSYNC
--# define MS_DIRSYNC     (1 << 7) // Directory modifications are synchronous
--#endif
--#ifndef MS_UNION
--# define MS_UNION       (1 << 8)
--#endif
--#ifndef MS_BIND
--# define MS_BIND        (1 << 12)
--#endif
--#ifndef MS_MOVE
--# define MS_MOVE        (1 << 13)
--#endif
--#ifndef MS_RECURSIVE
--# define MS_RECURSIVE   (1 << 14)
--#endif
--#ifndef MS_SILENT
--# define MS_SILENT      (1 << 15)
--#endif
--// The shared subtree stuff, which went in around 2.6.15
--#ifndef MS_UNBINDABLE
--# define MS_UNBINDABLE  (1 << 17)
--#endif
--#ifndef MS_PRIVATE
--# define MS_PRIVATE     (1 << 18)
--#endif
--#ifndef MS_SLAVE
--# define MS_SLAVE       (1 << 19)
--#endif
--#ifndef MS_SHARED
--# define MS_SHARED      (1 << 20)
--#endif
--#ifndef MS_RELATIME
--# define MS_RELATIME    (1 << 21)
--#endif
- #include "libbb.h"
-+#include "xmount.h"
- #if ENABLE_FEATURE_MOUNT_LABEL
- # include "volume_id.h"
- #else
-@@ -288,7 +253,7 @@ static int verbose_mount(const char *source, const char *target,
-       int rc;
-       errno = 0;
--      rc = mount(source, target, filesystemtype, mountflags, data);
-+      rc = xmount(source, target, filesystemtype, mountflags, data);
-       if (verbose >= 2)
-               bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
-                       source, target, filesystemtype,
-@@ -296,7 +261,7 @@ static int verbose_mount(const char *source, const char *target,
-       return rc;
- }
- #else
--#define verbose_mount(...) mount(__VA_ARGS__)
-+#define verbose_mount(...) xmount(__VA_ARGS__)
- #endif
- // Append mount options to string
-diff --git a/util-linux/umount.c b/util-linux/umount.c
-index a19f86c..781e019 100644
---- a/util-linux/umount.c
-+++ b/util-linux/umount.c
-@@ -8,40 +8,9 @@
-  * Licensed under GPL version 2, see file LICENSE in this tarball for details.
-  */
- #include <mntent.h>
--#include <sys/mount.h>
--/* Make sure we have all the new mount flags we actually try to use. */
--#ifndef MS_BIND
--# define MS_BIND        (1 << 12)
--#endif
--#ifndef MS_MOVE
--# define MS_MOVE        (1 << 13)
--#endif
--#ifndef MS_RECURSIVE
--# define MS_RECURSIVE   (1 << 14)
--#endif
--#ifndef MS_SILENT
--# define MS_SILENT      (1 << 15)
--#endif
--/* The shared subtree stuff, which went in around 2.6.15. */
--#ifndef MS_UNBINDABLE
--# define MS_UNBINDABLE  (1 << 17)
--#endif
--#ifndef MS_PRIVATE
--# define MS_PRIVATE     (1 << 18)
--#endif
--#ifndef MS_SLAVE
--# define MS_SLAVE       (1 << 19)
--#endif
--#ifndef MS_SHARED
--# define MS_SHARED      (1 << 20)
--#endif
--#ifndef MS_RELATIME
--# define MS_RELATIME    (1 << 21)
--#endif
-+
- #include "libbb.h"
--#ifndef PATH_MAX
--# define PATH_MAX (4*1024)
--#endif
-+#include "xmount.h"
- #if defined(__dietlibc__)
-@@ -154,11 +123,11 @@ int umount_main(int argc UNUSED_PARAM, char **argv)
-               if (m) zapit = m->dir;
-               // Let's ask the thing nicely to unmount.
--              curstat = umount(zapit);
-+              curstat = xumount(zapit, 0);
-               // Force the unmount, if necessary.
-               if (curstat && doForce)
--                      curstat = umount2(zapit, doForce);
-+                      curstat = xumount(zapit, doForce);
-               // If still can't umount, maybe remount read-only?
-               if (curstat) {
-@@ -166,7 +135,7 @@ int umount_main(int argc UNUSED_PARAM, char **argv)
-                               // Note! Even if we succeed here, later we should not
-                               // free loop device or erase mtab entry!
-                               const char *msg = "%s busy - remounted read-only";
--                              curstat = mount(m->device, zapit, NULL, MS_REMOUNT|MS_RDONLY, NULL);
-+                              curstat = xmount(m->device, zapit, NULL, MS_REMOUNT|MS_RDONLY, NULL);
-                               if (curstat) {
-                                       msg = "can't remount %s read-only";
-                                       status = EXIT_FAILURE;
-diff --git a/util-linux/xmount.c b/util-linux/xmount.c
-new file mode 100644
-index 0000000..3f322b8
---- /dev/null
-+++ b/util-linux/xmount.c
-@@ -0,0 +1,70 @@
-+#include "libbb.h"
-+#include "xmount.h"
-+
-+#ifdef __linux__
-+
-+/* xmount and xumount short-circuited to mount and umount2 in xmount.h */
-+
-+#elif defined(__FreeBSD_kernel__)
-+
-+static void build_iovec(struct iovec **iov, int *iovlen, const char *name,
-+              void *val, size_t len)
-+{
-+      int i;
-+
-+      if (*iovlen < 0)
-+              return;
-+      i = *iovlen;
-+      *iov = realloc(*iov, sizeof **iov * (i + 2));
-+      if (*iov == NULL) {
-+              *iovlen = -1;
-+              return;
-+      }
-+      (*iov)[i].iov_base = strdup(name);
-+      (*iov)[i].iov_len = strlen(name) + 1;
-+      i++;
-+      (*iov)[i].iov_base = val;
-+      if (len == (size_t)-1) {
-+              if (val != NULL)
-+                      len = strlen(val) + 1;
-+              else
-+                      len = 0;
-+      }
-+      (*iov)[i].iov_len = (int)len;
-+      *iovlen = ++i;
-+}
-+
-+int FAST_FUNC xmount(const char *source, const char *target,
-+              const char *filesystemtype, unsigned long mountflags,
-+              const void *data UNUSED_PARAM)
-+{
-+      struct iovec *iov = NULL;
-+      int iovlen = 0;
-+      char *fspath, *from;
-+      int ret;
-+
-+      fspath = realpath(target, NULL);
-+      from = realpath(source, NULL);
-+
-+      build_iovec(&iov, &iovlen, "fstype", (void*)filesystemtype, (size_t)-1);
-+      build_iovec(&iov, &iovlen, "fspath", fspath, (size_t)-1);
-+      if (!strcmp(filesystemtype, "nullfs"))
-+              /* nullfs uses a "target" instead of "from" */
-+              build_iovec(&iov, &iovlen, "target", from, (size_t)-1);
-+      else
-+              build_iovec(&iov, &iovlen, "from", from, (size_t)-1);
-+
-+      ret = nmount(iov, iovlen, mountflags);
-+
-+      free(from);
-+      free(fspath);
-+      
-+      return ret;
-+}
-+
-+int FAST_FUNC xumount(const char *target, int flags)
-+{
-+      return unmount(target, flags);
-+}
-+
-+#endif
-diff --git a/util-linux/xmount.h b/util-linux/xmount.h
-new file mode 100644
-index 0000000..caef564
---- /dev/null
-+++ b/util-linux/xmount.h
-@@ -0,0 +1,97 @@
-+/* vi: set sw=4 ts=4: */
-+/*
-+ * System-specific definitions for mount.
-+ *
-+ * Copyright (C) 2010 by Jeremie Koenig <jk@jk.fr.eu.org>
-+ * Copyright (C) 2010 by Luca Favatella <slackydeb@gmail.com>
-+ *
-+ * The Linux prototypes for mount() and umount2() are used as a reference for
-+ * our xmount() and xumount(), which should be implemented as a compatibility
-+ * wrappers for non-Linux systems (see xmount.c).
-+ */
-+
-+/*
-+ * Definitions for mount flags. Non-Linux systems are free to use whatever
-+ * their version of xmount() will work with.
-+ */
-+
-+#ifdef __linux__
-+# include <sys/mount.h>
-+/* Make sure we have all the new mount flags we actually try to use
-+ * (grab more as needed from util-linux's mount/mount_constants.h). */
-+# ifndef MS_DIRSYNC
-+#  define MS_DIRSYNC     (1 << 7) // Directory modifications are synchronous
-+# endif
-+# ifndef MS_UNION
-+#  define MS_UNION       (1 << 8)
-+# endif
-+# ifndef MS_BIND
-+#  define MS_BIND        (1 << 12)
-+# endif
-+# ifndef MS_MOVE
-+#  define MS_MOVE        (1 << 13)
-+# endif
-+# ifndef MS_RECURSIVE
-+#  define MS_RECURSIVE   (1 << 14)
-+# endif
-+# ifndef MS_SILENT
-+#  define MS_SILENT      (1 << 15)
-+# endif
-+/* The shared subtree stuff, which went in around 2.6.15. */
-+# ifndef MS_UNBINDABLE
-+#  define MS_UNBINDABLE  (1 << 17)
-+# endif
-+# ifndef MS_PRIVATE
-+#  define MS_PRIVATE     (1 << 18)
-+# endif
-+# ifndef MS_SLAVE
-+#  define MS_SLAVE       (1 << 19)
-+# endif
-+# ifndef MS_SHARED
-+#  define MS_SHARED      (1 << 20)
-+# endif
-+# ifndef MS_RELATIME
-+#  define MS_RELATIME    (1 << 21)
-+# endif
-+
-+#elif defined(__FreeBSD_kernel__)
-+# include <sys/mount.h>
-+# define MS_NOSUID      MNT_NOSUID
-+# define MS_NODEV       MNT_NODEV
-+# define MS_NOEXEC      MNT_NOEXEC
-+# define MS_SYNCHRONOUS MNT_SYNCHRONOUS
-+# define MS_DIRSYNC     0
-+# define MS_NOATIME     MNT_NOATIME
-+# define MS_NODIRATIME  0
-+# define MS_MANDLOCK    0
-+# define MS_RELATIME    0
-+# define MS_SILENT      0
-+# define MS_UNION       MNT_UNION
-+# define MS_BIND        0
-+# define MS_MOVE        0
-+# define MS_SHARED      0
-+# define MS_SLAVE       0
-+# define MS_PRIVATE     0
-+# define MS_UNBINDABLE  0
-+# define MS_RECURSIVE   0
-+# define MS_RDONLY      MNT_RDONLY
-+# define MS_REMOUNT     MNT_UPDATE
-+
-+#else
-+# error There is no xmount() implementation for your system.
-+#endif
-+
-+/*
-+ * Prototypes for xmount() and xumount(): on Linux we use the system calls
-+ * directly, otherwise xmount() and xumount() should be implemented as
-+ * compatibility wrappers (see xmount.c).
-+ */
-+
-+#ifdef __linux__
-+# define xmount mount
-+# define xumount umount2
-+#else
-+int xmount(const char *source, const char *target, const char *filesystemtype,
-+              unsigned long mountflags, const void *data) FAST_FUNC;
-+int xumount(const char *target, int flags) FAST_FUNC;
-+#endif
--- 
-1.7.1
-
diff --git a/packaging/udhcpc-fast-request.patch b/packaging/udhcpc-fast-request.patch
deleted file mode 100644 (file)
index 47f5f3e..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-From: Hakgoo Lee <goodguy.lee@samsung.com>
-Date: Thu, 16 Sep 2010 14:51:22 +0900
-Subject: [PATCH] Add FEATURE_UDHCPC_FAST_REQUEST to udhcp.
-
-If selected, udhcpc will send Request if leased IP and DHCP server is specified.
-So Discover/Offer routine can be skipped. It can reduce service recovery time in WiFi.
----
- include/usage.src.h         |    4 ++++
- networking/udhcp/Config.src |    8 ++++++++
- networking/udhcp/dhcpc.c    |   28 ++++++++++++++++++++++++++++
- 3 files changed, 40 insertions(+), 0 deletions(-)
-
-diff --git a/include/usage.src.h b/include/usage.src.h
-index 94a3256..e3c987c 100644
---- a/include/usage.src.h
-+++ b/include/usage.src.h
-@@ -4539,6 +4539,10 @@ INSERT
-       IF_FEATURE_UDHCPC_ARPING( \
-      "\n      -a,--arping             Use arping to validate offered address" \
-       ) \
-+      IF_FEATURE_UDHCPC_FAST_REQUEST( \
-+     "\n      -d,--request-direct=IP  IP address to request without discover, must be used with -D" \
-+     "\n      -D,--dhcp-server=IP     DHCP server IP address to get leased IP address" \
-+      ) \
-      "\n      -O,--request-option OPT Request DHCP option OPT (cumulative)" \
-      "\n      -o,--no-default-options Don't request any options (unless -O is given)" \
-      "\n      -x OPT:VAL              Include option OPT in sent packets (cumulative)" \
-diff --git a/networking/udhcp/Config.src b/networking/udhcp/Config.src
-index 331dffc..c4ec82f 100644
---- a/networking/udhcp/Config.src
-+++ b/networking/udhcp/Config.src
-@@ -130,3 +130,11 @@ 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_UDHCPC_FAST_REQUEST
-+      bool "Send Fast Request without Discover/Offer (e.g. When same subnet is connected again like WiFi AP)."
-+      default y
-+      depends on UDHCPC
-+      help
-+        If selected, udhcpc will send Request if leased IP and DHCP server is specified.
-+        So Discover/Offer routine can be skipped. It can reduce service recovery time in WiFi.
-diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
-index de1b798..e740871 100644
---- a/networking/udhcp/dhcpc.c
-+++ b/networking/udhcp/dhcpc.c
-@@ -767,6 +767,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
- {
-       uint8_t *temp, *message;
-       const char *str_c, *str_V, *str_h, *str_F, *str_r;
-+      IF_FEATURE_UDHCPC_FAST_REQUEST(char *str_d, *str_D;)
-       IF_FEATURE_UDHCP_PORT(char *str_P;)
-       llist_t *list_O = NULL;
-       llist_t *list_x = NULL;
-@@ -812,6 +813,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
-               "background\0"     No_argument       "b"
-               IF_FEATURE_UDHCPC_ARPING("arping\0"     No_argument       "a")
-               IF_FEATURE_UDHCP_PORT("client-port\0"   Required_argument "P")
-+              IF_FEATURE_UDHCPC_FAST_REQUEST("request-direct\0" Required_argument "d")
-+              IF_FEATURE_UDHCPC_FAST_REQUEST("dhcp-server\0"    Required_argument "D")
-               ;
- #endif
-       enum {
-@@ -841,9 +844,13 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
-               USE_FOR_MMU(             OPTBIT_b,)
-               IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
-               IF_FEATURE_UDHCP_PORT(   OPTBIT_P,)
-+              IF_FEATURE_UDHCPC_FAST_REQUEST(OPTBIT_d,)
-+              IF_FEATURE_UDHCPC_FAST_REQUEST(OPTBIT_D,)
-               USE_FOR_MMU(             OPT_b = 1 << OPTBIT_b,)
-               IF_FEATURE_UDHCPC_ARPING(OPT_a = 1 << OPTBIT_a,)
-               IF_FEATURE_UDHCP_PORT(   OPT_P = 1 << OPTBIT_P,)
-+              IF_FEATURE_UDHCPC_FAST_REQUEST(OPT_d = 1 << OPTBIT_d,)
-+              IF_FEATURE_UDHCPC_FAST_REQUEST(OPT_D = 1 << OPTBIT_D,)
-       };
-       /* Default options. */
-@@ -865,6 +872,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
-               USE_FOR_MMU("b")
-               IF_FEATURE_UDHCPC_ARPING("a")
-               IF_FEATURE_UDHCP_PORT("P:")
-+              IF_FEATURE_UDHCPC_FAST_REQUEST("d:D:")
-               "v"
-               , &str_c, &str_V, &str_h, &str_h, &str_F
-               , &client_config.interface, &client_config.pidfile, &str_r /* i,p */
-@@ -873,6 +881,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
-               , &list_O
-               , &list_x
-               IF_FEATURE_UDHCP_PORT(, &str_P)
-+              IF_FEATURE_UDHCPC_FAST_REQUEST(, &str_d, &str_D)
- #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
-               , &dhcp_verbose
- #endif
-@@ -950,6 +959,18 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
-               logmode |= LOGMODE_SYSLOG;
-       }
-+#if ENABLE_FEATURE_UDHCPC_FAST_REQUEST
-+      if (opt & OPT_d) {
-+              log1("Parsing request-direct option");
-+              requested_ip = inet_addr(str_d);
-+              state = REQUESTING;
-+      }
-+      if (opt & OPT_D) {
-+              log1("Parsing dhcp-server option");
-+              server_addr = inet_addr(str_D);
-+      }
-+#endif
-+
-       /* Make sure fd 0,1,2 are open */
-       bb_sanitize_stdio();
-       /* Equivalent of doing a fflush after every \n */
-@@ -963,7 +984,14 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
-       /* We want random_xid to be random... */
-       srand(monotonic_us());
-+#if ENABLE_FEATURE_UDHCPC_FAST_REQUEST
-+      if (state != REQUESTING)
-+              state = INIT_SELECTING;
-+      log1("Initial state is %d", state);
-+#else
-       state = INIT_SELECTING;
-+#endif
-+
-       udhcp_run_script(NULL, "deconfig");
-       change_listen_mode(LISTEN_RAW);
-       packet_num = 0;
--- 
diff --git a/packaging/update-scripts-kconfig-_shipped.patch b/packaging/update-scripts-kconfig-_shipped.patch
deleted file mode 100644 (file)
index 52cc64e..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-commit 6e06da5efd5d6e341ae2f5116c449994740f5613
-Author: Denys Vlasenko <vda.linux@googlemail.com>
-Date:   Mon Aug 2 02:17:25 2010 +0200
-
-    update _shipped file with hurd fix
-    
-    Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-
-diff --git a/scripts/kconfig/lex.zconf.c_shipped b/scripts/kconfig/lex.zconf.c_shipped
-index 4837bbf..51f15e1 100644
---- a/scripts/kconfig/lex.zconf.c_shipped
-+++ b/scripts/kconfig/lex.zconf.c_shipped
-@@ -2235,13 +2235,14 @@ static void zconf_endhelp(void)
-  */
- FILE *zconf_fopen(const char *name)
- {
--      char *env, fullname[PATH_MAX+1];
-+      char *env;
-       FILE *f;
-       f = fopen(name, "r");
-       if (!f && name[0] != '/') {
-               env = getenv(SRCTREE);
-               if (env) {
-+                      char *fullname = alloca(strlen(env) + strlen(name) + 2);
-                       sprintf(fullname, "%s/%s", env, name);
-                       f = fopen(fullname, "r");
-               }
diff --git a/packaging/usrbin.links b/packaging/usrbin.links
deleted file mode 100644 (file)
index 4c4c18c..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-[
-[[
-adjtimex
-ar
-arping
-basename
-brctl
-cal
-catv
-chpst
-chrt
-chvt
-cksum
-cmp
-comm
-crontab
-cut
-dc
-deallocvt
-diff
-dirname
-dos2unix
-du
-dumpleases
-eject
-env
-envdir
-envuidgid
-ether-wake
-expand
-expr
-fdformat
-find
-flock
-fold
-free
-freeramdisk
-ftpget
-ftpput
-getopt
-hd
-head
-hexdump
-hostid
-id
-install
-ionice
-ipcalc
-ipcrm
-ipcs
-kbd_mode
-killall
-length
-less
-linux32
-linux64
-loadfont
-logger
-logname
-lzcat
-lzma
-lzop
-lzopcat
-md5sum
-microcom
-mkfifo
-nice
-nmeter
-nohup
-nslookup
-od
-openvt
-passwd
-patch
-pgrep
-pkill
-printenv
-printf
-pscan
-readahead
-realpath
-renice
-resize
-rev
-rtcwake
-runsv
-runsvdir
-rx
-script
-scriptreplay
-seq
-setarch
-setkeycodes
-setlogcons
-setsid
-setuidgid
-sha1sum
-sha256sum
-sha512sum
-showkey
-softlimit
-sort
-split
-stat
-strings
-sum
-sv
-svlogd
-tac
-tail
-taskset
-tcpsvd
-tee
-test
-tftp
-time
-timeout
-top
-tr
-tty
-ttysize
-udhcpc
-udpsvd
-unexpand
-uniq
-unix2dos
-unlzma
-unlzop
-unzip
-uptime
-uudecode
-uuencode
-vlock
-volname
-wall
-watch
-wc
-wget
-who
-whoami
-xargs
-yes
-zcip
-vi
diff --git a/packaging/usrsbin.links b/packaging/usrsbin.links
deleted file mode 100644 (file)
index 27966d1..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-addgroup
-adduser
-arp
-chat
-chpasswd
-chroot
-crond
-delgroup
-deluser
-dhcprelay
-dnsd
-fakeidentd
-flashcp
-flash_eraseall
-flash_lock
-flash_unlock
-httpd
-inetd
-rdate
-rdev
-readprofile
-sendmail
-telnetd
-tftpd
-ubiattach
-ubidetach
-udhcpd
-watchdog
diff --git a/packaging/version.patch b/packaging/version.patch
deleted file mode 100644 (file)
index a09b782..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
---- a/Makefile.flags
-+++ b/Makefile.flags
-@@ -4,6 +4,11 @@
- BB_VER = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
- export BB_VER
-+ifndef BB_EXTRA_VERSION
-+BB_BT = AUTOCONF_TIMESTAMP
-+else
-+BB_BT = KBUILD_STR($(BB_EXTRA_VERSION))
-+endif
- SKIP_STRIP = n
- # -std=gnu99 needed for [U]LLONG_MAX on some systems
-@@ -15,7 +20,7 @@
-       -include include/autoconf.h \
-       -D_GNU_SOURCE -DNDEBUG \
-       $(if $(CONFIG_LFS),-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64) \
--      -D"BB_VER=KBUILD_STR($(BB_VER))" -DBB_BT=AUTOCONF_TIMESTAMP
-+      -D"BB_VER=KBUILD_STR($(BB_VER))" -D"BB_BT=$(BB_BT)"
- CFLAGS += $(call cc-option,-Wall,)
- CFLAGS += $(call cc-option,-Wshadow,)
diff --git a/packaging/vlock-disable-linux-console-calls-on-other-systems.patch b/packaging/vlock-disable-linux-console-calls-on-other-systems.patch
deleted file mode 100644 (file)
index f717473..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-From 68fca4cd55e7bf6075eb1ccd303ae57a7ec1b8da Mon Sep 17 00:00:00 2001
-From: Jeremie Koenig <jk@jk.fr.eu.org>
-Date: Thu, 29 Jul 2010 04:29:52 +0200
-Subject: [PATCH 12/12] vlock: disable linux console calls on other systems
-
-Signed-off-by: Jeremie Koenig <jk@jk.fr.eu.org>
-Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
----
- loginutils/Config.src |    1 -
- loginutils/vlock.c    |   15 +++++++++++++--
- 2 files changed, 13 insertions(+), 3 deletions(-)
-
-diff --git a/loginutils/Config.src b/loginutils/Config.src
-index 6ec2893..5d497c4 100644
---- a/loginutils/Config.src
-+++ b/loginutils/Config.src
-@@ -295,7 +295,6 @@ config SULOGIN
- config VLOCK
-       bool "vlock"
-       default y
--      depends on PLATFORM_LINUX
-       select FEATURE_SUID
-       help
-         Build the "vlock" applet which allows you to lock (virtual) terminals.
-diff --git a/loginutils/vlock.c b/loginutils/vlock.c
-index 85f489c..59aeb54 100644
---- a/loginutils/vlock.c
-+++ b/loginutils/vlock.c
-@@ -15,9 +15,11 @@
- /* Fixed by Erik Andersen to do passwords the tinylogin way...
-  * It now works with md5, sha1, etc passwords. */
--#include <sys/vt.h>
- #include "libbb.h"
-+#ifdef __linux__
-+#include <sys/vt.h>
-+
- static void release_vt(int signo UNUSED_PARAM)
- {
-       /* If -a, param is 0, which means:
-@@ -30,14 +32,17 @@ static void acquire_vt(int signo UNUSED_PARAM)
-       /* ACK to kernel that switch to console is successful */
-       ioctl(STDIN_FILENO, VT_RELDISP, VT_ACKACQ);
- }
-+#endif
- int vlock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
- int vlock_main(int argc UNUSED_PARAM, char **argv)
- {
-+#ifdef __linux__
-       struct vt_mode vtm;
-+      struct vt_mode ovtm;
-+#endif
-       struct termios term;
-       struct termios oterm;
--      struct vt_mode ovtm;
-       struct passwd *pw;
-       pw = xgetpwuid(getuid());
-@@ -55,6 +60,7 @@ int vlock_main(int argc UNUSED_PARAM, char **argv)
-               + (1 << SIGINT )
-               , SIG_IGN);
-+#ifdef __linux__
-       /* We will use SIGUSRx for console switch control: */
-       /* 1: set handlers */
-       signal_SA_RESTART_empty_mask(SIGUSR1, release_vt);
-@@ -62,12 +68,14 @@ int vlock_main(int argc UNUSED_PARAM, char **argv)
-       /* 2: unmask them */
-       sig_unblock(SIGUSR1);
-       sig_unblock(SIGUSR2);
-+#endif
-       /* Revert stdin/out to our controlling tty
-        * (or die if we have none) */
-       xmove_fd(xopen(CURRENT_TTY, O_RDWR), STDIN_FILENO);
-       xdup2(STDIN_FILENO, STDOUT_FILENO);
-+#ifdef __linux__
-       xioctl(STDIN_FILENO, VT_GETMODE, &vtm);
-       ovtm = vtm;
-       /* "console switches are controlled by us, not kernel!" */
-@@ -75,6 +83,7 @@ int vlock_main(int argc UNUSED_PARAM, char **argv)
-       vtm.relsig = SIGUSR1;
-       vtm.acqsig = SIGUSR2;
-       ioctl(STDIN_FILENO, VT_SETMODE, &vtm);
-+#endif
-       tcgetattr(STDIN_FILENO, &oterm);
-       term = oterm;
-@@ -95,7 +104,9 @@ int vlock_main(int argc UNUSED_PARAM, char **argv)
-               puts("Password incorrect");
-       } while (1);
-+#ifdef __linux__
-       ioctl(STDIN_FILENO, VT_SETMODE, &ovtm);
-+#endif
-       tcsetattr_stdin_TCSANOW(&oterm);
-       fflush_stdout_and_exit(EXIT_SUCCESS);
- }
--- 
-1.7.1
-
index 008290e..194fe01 100644 (file)
@@ -1,6 +1,6 @@
 # Makefile for busybox
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y :=
 
index d91491f..642e8a8 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /*
  * mv -f ./"$DATAFILE" save/
  */
 
+//usage:#define lpd_trivial_usage
+//usage:       "SPOOLDIR [HELPER [ARGS]]"
+//usage:#define lpd_full_usage "\n\n"
+//usage:       "SPOOLDIR must contain (symlinks to) device nodes or directories"
+//usage:     "\nwith names matching print queue names. In the first case, jobs are"
+//usage:     "\nsent directly to the device. Otherwise each job is stored in queue"
+//usage:     "\ndirectory and HELPER program is called. Name of file to print"
+//usage:     "\nis passed in $DATAFILE variable."
+//usage:     "\nExample:"
+//usage:     "\n       tcpsvd -E 0 515 softlimit -m 999999 lpd /var/spool ./print"
+
 #include "libbb.h"
 
 // strip argument of bad chars
@@ -91,7 +102,7 @@ static char *xmalloc_read_stdin(void)
 {
        // SECURITY:
        size_t max = 4 * 1024; // more than enough for commands!
-       return xmalloc_reads(STDIN_FILENO, NULL, &max);
+       return xmalloc_reads(STDIN_FILENO, &max);
 }
 
 int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE;
@@ -135,7 +146,7 @@ int lpd_main(int argc UNUSED_PARAM, char *argv[])
        while (1) {
                char *fname;
                int fd;
-               // int is easier than ssize_t: can use xatoi_u,
+               // int is easier than ssize_t: can use xatoi_positive,
                // and can correctly display error returns (-1)
                int expected_len, real_len;
 
index f21cffd..70cda77 100644 (file)
@@ -7,10 +7,29 @@
  * Original idea and code:
  *      Walter Harms <WHarms@bfs.de>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  *
  * See RFC 1179 for protocol description.
  */
+
+//usage:#define lpr_trivial_usage
+//usage:       "-P queue[@host[:port]] -U USERNAME -J TITLE -Vmh [FILE]..."
+/* -C CLASS exists too, not shown.
+ * CLASS is supposed to be printed on banner page, if one is requested */
+//usage:#define lpr_full_usage "\n\n"
+//usage:       "       -P      lp service to connect to (else uses $PRINTER)"
+//usage:     "\n       -m      Send mail on completion"
+//usage:     "\n       -h      Print banner page too"
+//usage:     "\n       -V      Verbose"
+//usage:
+//usage:#define lpq_trivial_usage
+//usage:       "[-P queue[@host[:port]]] [-U USERNAME] [-d JOBID]... [-fs]"
+//usage:#define lpq_full_usage "\n\n"
+//usage:       "       -P      lp service to connect to (else uses $PRINTER)"
+//usage:     "\n       -d      Delete jobs"
+//usage:     "\n       -f      Force any waiting job to be printed"
+//usage:     "\n       -s      Short display"
+
 #include "libbb.h"
 
 /*
@@ -70,6 +89,10 @@ int lpqr_main(int argc UNUSED_PARAM, char *argv[])
        unsigned opts;
        int fd;
 
+       queue = getenv("PRINTER");
+       if (!queue)
+               queue = "lp";
+
        // parse options
        // TODO: set opt_complementary: s,d,f are mutually exclusive
        opts = getopt32(argv,
@@ -79,16 +102,7 @@ int lpqr_main(int argc UNUSED_PARAM, char *argv[])
        );
        argv += optind;
 
-       // if queue is not specified -> use $PRINTER
-       if (!(opts & OPT_P))
-               queue = getenv("PRINTER");
-       // if queue is still not specified ->
-       if (!queue) {
-               // ... queue defaults to "lp"
-               // server defaults to "localhost"
-               queue = "lp";
-       // if queue is specified ->
-       } else {
+       {
                // queue name is to the left of '@'
                char *s = strchr(queue, '@');
                if (s) {
@@ -159,9 +173,7 @@ int lpqr_main(int argc UNUSED_PARAM, char *argv[])
                // if data file is stdin, we need to dump it first
                if (LONE_DASH(*argv)) {
                        strcpy(tempfile, "/tmp/lprXXXXXX");
-                       dfd = mkstemp(tempfile);
-                       if (dfd < 0)
-                               bb_perror_msg_and_die("mkstemp");
+                       dfd = xmkstemp(tempfile);
                        bb_copyfd_eof(STDIN_FILENO, dfd);
                        xlseek(dfd, 0, SEEK_SET);
                        *argv = (char*)bb_msg_standard_input;
@@ -169,6 +181,17 @@ int lpqr_main(int argc UNUSED_PARAM, char *argv[])
                        dfd = xopen(*argv, O_RDONLY);
                }
 
+               st.st_size = 0; /* paranoia: fstat may theoretically fail */
+               fstat(dfd, &st);
+
+               /* Apparently, some servers are buggy and won't accept 0-sized jobs.
+                * Standard lpr works around it by refusing to send such jobs:
+                */
+               if (st.st_size == 0) {
+                       bb_error_msg("nothing to print");
+                       continue;
+               }
+
                /* "The name ... should start with ASCII "cfA",
                 * followed by a three digit job number, followed
                 * by the host name which has constructed the file."
@@ -193,14 +216,11 @@ int lpqr_main(int argc UNUSED_PARAM, char *argv[])
                        , (opts & LPR_m) ? user : ""
                        , remote_filename
                );
-               // delete possible "\nX\n" patterns
+               // delete possible "\nX\n" (that is, one-char) patterns
                c = controlfile;
-               cflen = (unsigned)strlen(controlfile);
                while ((c = strchr(c, '\n')) != NULL) {
                        if (c[1] && c[2] == '\n') {
-                               /* can't use strcpy, results are undefined */
-                               memmove(c, c+2, cflen - (c-controlfile) - 1);
-                               cflen -= 2;
+                               overlapping_strcpy(c, c+2);
                        } else {
                                c++;
                        }
@@ -211,6 +231,7 @@ int lpqr_main(int argc UNUSED_PARAM, char *argv[])
                        bb_error_msg("sending control file");
                /* "Acknowledgement processing must occur as usual
                 * after the command is sent." */
+               cflen = (unsigned)strlen(controlfile);
                fdprintf(fd, "\x2" "%u c%s\n", cflen, remote_filename);
                get_response_or_say_and_die(fd, "sending control file");
                /* "Once all of the contents have
@@ -224,8 +245,6 @@ int lpqr_main(int argc UNUSED_PARAM, char *argv[])
                // send data file, with name "dfaXXX"
                if (opts & LPR_V)
                        bb_error_msg("sending data file");
-               st.st_size = 0; /* paranoia: fstat may theoretically fail */
-               fstat(dfd, &st);
                fdprintf(fd, "\x3" "%"OFF_FMT"u d%s\n", st.st_size, remote_filename);
                get_response_or_say_and_die(fd, "sending data file");
                if (bb_copyfd_size(dfd, fd, st.st_size) != st.st_size) {
index e61de56..527d9ee 100644 (file)
@@ -10,6 +10,7 @@ INSERT
 config FREE
        bool "free"
        default y
+       select PLATFORM_LINUX #sysinfo()
        help
          free displays the total amount of free and used physical and swap
          memory in the system, as well as the buffers used by the kernel.
@@ -45,12 +46,6 @@ config KILLALL5
        default y
        depends on KILL
 
-config NMETER
-       bool "nmeter"
-       default y
-       help
-         Prints selected system stats continuously, one line per update.
-
 config PGREP
        bool "pgrep"
        default y
@@ -95,16 +90,25 @@ config PS
 config FEATURE_PS_WIDE
        bool "Enable wide output option (-w)"
        default y
-       depends on PS
+       depends on PS && !DESKTOP
        help
          Support argument 'w' for wide output.
          If given once, 132 chars are printed, and if given more
          than once, the length is unlimited.
 
+config FEATURE_PS_LONG
+       bool "Enable long output option (-l)"
+       default y
+       depends on PS && !DESKTOP
+       help
+         Support argument 'l' for long output.
+         Adds fields PPID, RSS, START, TIME & TTY
+
 config FEATURE_PS_TIME
        bool "Enable time and elapsed time output"
        default y
        depends on PS && DESKTOP
+       select PLATFORM_LINUX
        help
          Support -o time and -o etime output specifiers.
 
@@ -136,74 +140,13 @@ config BB_SYSCTL
        help
          Configure kernel parameters at runtime.
 
-config TOP
-       bool "top"
-       default y
-       help
-         The top program provides a dynamic real-time view of a running
-         system.
-
-config FEATURE_TOP_CPU_USAGE_PERCENTAGE
-       bool "Show CPU per-process usage percentage"
-       default y
-       depends on TOP
-       help
-         Make top display CPU usage for each process.
-         This adds about 2k.
-
-config FEATURE_TOP_CPU_GLOBAL_PERCENTS
-       bool "Show CPU global usage percentage"
-       default y
-       depends on FEATURE_TOP_CPU_USAGE_PERCENTAGE
-       help
-         Makes top display "CPU: NN% usr NN% sys..." line.
-         This adds about 0.5k.
-
-config FEATURE_TOP_SMP_CPU
-       bool "SMP CPU usage display ('c' key)"
-       default y
-       depends on FEATURE_TOP_CPU_GLOBAL_PERCENTS
-       help
-         Allow 'c' key to switch between individual/cumulative CPU stats
-         This adds about 0.5k.
-
-config FEATURE_TOP_DECIMALS
-       bool "Show 1/10th of a percent in CPU/mem statistics"
-       default y
-       depends on FEATURE_TOP_CPU_USAGE_PERCENTAGE
-       help
-         Show 1/10th of a percent in CPU/mem statistics.
-         This adds about 0.3k.
-
-config FEATURE_TOP_SMP_PROCESS
-       bool "Show CPU process runs on ('j' field)"
-       default y
-       depends on TOP
-       help
-         Show CPU where process was last found running on.
-         This is the 'j' field.
-
-config FEATURE_TOPMEM
-       bool "Topmem command ('s' key)"
-       default y
-       depends on TOP
-       help
-         Enable 's' in top (gives lots of memory info).
-
 config FEATURE_SHOW_THREADS
-       bool "Support for showing threads in ps/top"
-       default y
-       depends on PS || TOP
-       help
-         Enables ps -T option and 'h' command in top
-
-config UPTIME
-       bool "uptime"
+       bool "Support for showing threads in ps/pstree/top"
        default y
+       depends on PS || TOP || PSTREE
        help
-         uptime gives a one line display of the current time, how long
-         the system has been running, how many users are currently logged
-         on, and the system load averages for the past 1, 5, and 15 minutes.
+         Enables the ps -T option, showing of threads in pstree,
+         and 'h' command in top.
 
 config WATCH
        bool "watch"
index c41f12b..89b1cc0 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
@@ -11,7 +11,6 @@ lib-$(CONFIG_FREE)    += free.o
 lib-$(CONFIG_FUSER)    += fuser.o
 lib-$(CONFIG_KILL)     += kill.o
 lib-$(CONFIG_ASH)      += kill.o  # used for built-in kill by ash
-lib-$(CONFIG_NMETER)    += nmeter.o
 lib-$(CONFIG_PGREP)    += pgrep.o
 lib-$(CONFIG_PKILL)    += pgrep.o
 lib-$(CONFIG_PIDOF)    += pidof.o
index 473d70b..47f2fc3 100644 (file)
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /* getopt not needed */
 
+//usage:#define free_trivial_usage
+//usage:       "" IF_DESKTOP("[-b/k/m/g]")
+//usage:#define free_full_usage "\n\n"
+//usage:       "Display the amount of free and used system memory"
+//usage:
+//usage:#define free_example_usage
+//usage:       "$ free\n"
+//usage:       "              total         used         free       shared      buffers\n"
+//usage:       "  Mem:       257628       248724         8904        59644        93124\n"
+//usage:       " Swap:       128516         8404       120112\n"
+//usage:       "Total:       386144       257128       129016\n"
+
 #include "libbb.h"
+#ifdef __linux__
+# include <sys/sysinfo.h>
+#endif
+
+struct globals {
+       unsigned mem_unit;
+#if ENABLE_DESKTOP
+       unsigned unit_steps;
+# define G_unit_steps G.unit_steps
+#else
+# define G_unit_steps 10
+#endif
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { } while (0)
+
+
+static unsigned long long scale(unsigned long d)
+{
+       return ((unsigned long long)d * G.mem_unit) >> G_unit_steps;
+}
+
 
 int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
 {
        struct sysinfo info;
-       unsigned mem_unit;
+
+       INIT_G();
 
 #if ENABLE_DESKTOP
-       if (argv[1] && argv[1][0] == '-')
-               bb_show_usage();
+       G.unit_steps = 10;
+       if (argv[1] && argv[1][0] == '-') {
+               switch (argv[1][1]) {
+               case 'b':
+                       G.unit_steps = 0;
+                       break;
+               case 'k': /* 2^10 */
+                       /* G.unit_steps = 10; - already is */
+                       break;
+               case 'm': /* 2^(2*10) */
+                       G.unit_steps = 20;
+                       break;
+               case 'g': /* 2^(3*10) */
+                       G.unit_steps = 30;
+                       break;
+               default:
+                       bb_show_usage();
+               }
+       }
 #endif
 
        sysinfo(&info);
 
        /* Kernels prior to 2.4.x will return info.mem_unit==0, so cope... */
-       mem_unit = 1;
-       if (info.mem_unit != 0) {
-               mem_unit = info.mem_unit;
-       }
-
-       /* Convert values to kbytes */
-       if (mem_unit == 1) {
-               info.totalram >>= 10;
-               info.freeram >>= 10;
-#if BB_MMU
-               info.totalswap >>= 10;
-               info.freeswap >>= 10;
-#endif
-               info.sharedram >>= 10;
-               info.bufferram >>= 10;
-       } else {
-               mem_unit >>= 10;
-               /* TODO:  Make all this stuff not overflow when mem >= 4 Tb */
-               info.totalram *= mem_unit;
-               info.freeram *= mem_unit;
-#if BB_MMU
-               info.totalswap *= mem_unit;
-               info.freeswap *= mem_unit;
-#endif
-               info.sharedram *= mem_unit;
-               info.bufferram *= mem_unit;
-       }
+       G.mem_unit = (info.mem_unit ? info.mem_unit : 1);
 
-       printf("      %13s%13s%13s%13s%13s\n",
+       printf("     %13s%13s%13s%13s%13s\n",
                "total",
                "used",
                "free",
                "shared", "buffers" /* swap and total don't have these columns */
+               /* procps version 3.2.8 also shows "cached" column, but
+                * sysinfo() does not provide this value, need to parse
+                * /proc/meminfo instead and get "Cached: NNN kB" from there.
+                */
+       );
+
+#define FIELDS_5 "%13llu%13llu%13llu%13llu%13llu\n"
+#define FIELDS_3 (FIELDS_5 + 2*6)
+#define FIELDS_2 (FIELDS_5 + 3*6)
+
+       printf("Mem: ");
+       printf(FIELDS_5,
+               scale(info.totalram),
+               scale(info.totalram - info.freeram),
+               scale(info.freeram),
+               scale(info.sharedram),
+               scale(info.bufferram)
        );
-       printf("%6s%13lu%13lu%13lu%13lu%13lu\n", "Mem:",
-               info.totalram,
-               info.totalram - info.freeram,
-               info.freeram,
-               info.sharedram, info.bufferram
+       /* Show alternate, more meaningful busy/free numbers by counting
+        * buffer cache as free memory (make it "-/+ buffers/cache"
+        * if/when we add support for "cached" column): */
+       printf("-/+ buffers:      ");
+       printf(FIELDS_2,
+               scale(info.totalram - info.freeram - info.bufferram),
+               scale(info.freeram + info.bufferram)
        );
 #if BB_MMU
-       printf("%6s%13lu%13lu%13lu\n", "Swap:",
-               info.totalswap,
-               info.totalswap - info.freeswap,
-               info.freeswap
-       );
-       printf("%6s%13lu%13lu%13lu\n", "Total:",
-               info.totalram + info.totalswap,
-               (info.totalram - info.freeram) + (info.totalswap - info.freeswap),
-               info.freeram + info.freeswap
+       printf("Swap:");
+       printf(FIELDS_3,
+               scale(info.totalswap),
+               scale(info.totalswap - info.freeswap),
+               scale(info.freeswap)
        );
 #endif
        return EXIT_SUCCESS;
index 85523c3..05b52ab 100644 (file)
@@ -4,9 +4,19 @@
  *
  * Copyright 2004 Tony J. White
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#define fuser_trivial_usage
+//usage:       "[OPTIONS] FILE or PORT/PROTO"
+//usage:#define fuser_full_usage "\n\n"
+//usage:       "Find processes which use FILEs or PORTs\n"
+//usage:     "\n       -m      Find processes which use same fs as FILEs"
+//usage:     "\n       -4,-6   Search only IPv4/IPv6 space"
+//usage:     "\n       -s      Don't display PIDs"
+//usage:     "\n       -k      Kill found processes"
+//usage:     "\n       -SIGNAL Signal to send (default: KILL)"
+
 #include "libbb.h"
 
 #define MAX_LINE 255
@@ -26,33 +36,18 @@ typedef struct inode_list {
        dev_t dev;
 } inode_list;
 
-typedef struct pid_list {
-       struct pid_list *next;
-       pid_t pid;
-} pid_list;
-
-
 struct globals {
-       pid_list *pid_list_head;
+       int recursion_depth;
+       pid_t mypid;
        inode_list *inode_list_head;
-};
+       smallint kill_failed;
+       int killsig;
+} FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
-#define INIT_G() do { } while (0)
-
-
-static void add_pid(const pid_t pid)
-{
-       pid_list **curr = &G.pid_list_head;
-
-       while (*curr) {
-               if ((*curr)->pid == pid)
-                       return;
-               curr = &(*curr)->next;
-       }
-
-       *curr = xzalloc(sizeof(pid_list));
-       (*curr)->pid = pid;
-}
+#define INIT_G() do { \
+       G.mypid = getpid(); \
+       G.killsig = SIGKILL; \
+} while (0)
 
 static void add_inode(const struct stat *st)
 {
@@ -72,48 +67,7 @@ static void add_inode(const struct stat *st)
        (*curr)->inode = st->st_ino;
 }
 
-static void scan_proc_net(const char *path, unsigned port)
-{
-       char line[MAX_LINE + 1];
-       long long uint64_inode;
-       unsigned tmp_port;
-       FILE *f;
-       struct stat st;
-       int fd;
-
-       /* find socket dev */
-       st.st_dev = 0;
-       fd = socket(AF_INET, SOCK_DGRAM, 0);
-       if (fd >= 0) {
-               fstat(fd, &st);
-               close(fd);
-       }
-
-       f = fopen_for_read(path);
-       if (!f)
-               return;
-
-       while (fgets(line, MAX_LINE, f)) {
-               char addr[68];
-               if (sscanf(line, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x "
-                               "%*x:%*x %*x %*d %*d %llu",
-                               addr, &tmp_port, &uint64_inode) == 3
-               ) {
-                       int len = strlen(addr);
-                       if (len == 8 && (option_mask32 & OPT_IP6))
-                               continue;
-                       if (len > 8 && (option_mask32 & OPT_IP4))
-                               continue;
-                       if (tmp_port == port) {
-                               st.st_ino = uint64_inode;
-                               add_inode(&st);
-                       }
-               }
-       }
-       fclose(f);
-}
-
-static int search_dev_inode(const struct stat *st)
+static smallint search_dev_inode(const struct stat *st)
 {
        inode_list *ilist = G.inode_list_head;
 
@@ -129,130 +83,202 @@ static int search_dev_inode(const struct stat *st)
        return 0;
 }
 
-static void scan_pid_maps(const char *fname, pid_t pid)
+enum {
+       PROC_NET = 0,
+       PROC_DIR,
+       PROC_DIR_LINKS,
+       PROC_SUBDIR_LINKS,
+};
+
+static smallint scan_proc_net_or_maps(const char *path, unsigned port)
 {
-       FILE *file;
-       char line[MAX_LINE + 1];
-       int major, minor;
+       FILE *f;
+       char line[MAX_LINE + 1], addr[68];
+       int major, minor, r;
        long long uint64_inode;
-       struct stat st;
+       unsigned tmp_port;
+       smallint retval;
+       struct stat statbuf;
+       const char *fmt;
+       void *fag, *sag;
 
-       file = fopen_for_read(fname);
-       if (!file)
-               return;
+       f = fopen_for_read(path);
+       if (!f)
+               return 0;
 
-       while (fgets(line, MAX_LINE, file)) {
-               if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3)
-                       continue;
-               st.st_ino = uint64_inode;
-               if (major == 0 && minor == 0 && st.st_ino == 0)
-                       continue;
-               st.st_dev = makedev(major, minor);
-               if (search_dev_inode(&st))
-                       add_pid(pid);
-       }
-       fclose(file);
-}
+       if (G.recursion_depth == PROC_NET) {
+               int fd;
 
-static void scan_link(const char *lname, pid_t pid)
-{
-       struct stat st;
+               /* find socket dev */
+               statbuf.st_dev = 0;
+               fd = socket(AF_INET, SOCK_DGRAM, 0);
+               if (fd >= 0) {
+                       fstat(fd, &statbuf);
+                       close(fd);
+               }
 
-       if (stat(lname, &st) >= 0) {
-               if (search_dev_inode(&st))
-                       add_pid(pid);
+               fmt = "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x "
+                       "%*x:%*x %*x:%*x %*x %*d %*d %llu";
+               fag = addr;
+               sag = &tmp_port;
+       } else {
+               fmt = "%*s %*s %*s %x:%x %llu";
+               fag = &major;
+               sag = &minor;
        }
-}
-
-static void scan_dir_links(const char *dname, pid_t pid)
-{
-       DIR *d;
-       struct dirent *de;
-       char *lname;
 
-       d = opendir(dname);
-       if (!d)
-               return;
-
-       while ((de = readdir(d)) != NULL) {
-               lname = concat_subpath_file(dname, de->d_name);
-               if (lname == NULL)
+       retval = 0;
+       while (fgets(line, MAX_LINE, f)) {
+               r = sscanf(line, fmt, fag, sag, &uint64_inode);
+               if (r != 3)
                        continue;
-               scan_link(lname, pid);
-               free(lname);
+
+               statbuf.st_ino = uint64_inode;
+               if (G.recursion_depth == PROC_NET) {
+                       r = strlen(addr);
+                       if (r == 8 && (option_mask32 & OPT_IP6))
+                               continue;
+                       if (r > 8 && (option_mask32 & OPT_IP4))
+                               continue;
+                       if (tmp_port == port)
+                               add_inode(&statbuf);
+               } else {
+                       if (major != 0 && minor != 0 && statbuf.st_ino != 0) {
+                               statbuf.st_dev = makedev(major, minor);
+                               retval = search_dev_inode(&statbuf);
+                               if (retval)
+                                       break;
+                       }
+               }
        }
-       closedir(d);
+       fclose(f);
+
+       return retval;
 }
 
-/* NB: does chdir internally */
-static void scan_proc_pids(void)
+static smallint scan_recursive(const char *path)
 {
        DIR *d;
-       struct dirent *de;
-       pid_t pid;
-
-       xchdir("/proc");
-       d = opendir("/proc");
-       if (!d)
-               return;
-
-       while ((de = readdir(d)) != NULL) {
-               pid = (pid_t)bb_strtou(de->d_name, NULL, 10);
-               if (errno)
-                       continue;
-               if (chdir(de->d_name) < 0)
-                       continue;
-               scan_link("cwd", pid);
-               scan_link("exe", pid);
-               scan_link("root", pid);
-
-               scan_dir_links("fd", pid);
-               scan_dir_links("lib", pid);
-               scan_dir_links("mmap", pid);
+       struct dirent *d_ent;
+       smallint stop_scan;
+       smallint retval;
+
+       d = opendir(path);
+       if (d == NULL)
+               return 0;
+
+       G.recursion_depth++;
+       retval = 0;
+       stop_scan = 0;
+       while (!stop_scan && (d_ent = readdir(d)) != NULL) {
+               struct stat statbuf;
+               pid_t pid;
+               char *subpath;
+
+               subpath = concat_subpath_file(path, d_ent->d_name);
+               if (subpath == NULL)
+                       continue; /* . or .. */
+
+               switch (G.recursion_depth) {
+               case PROC_DIR:
+                       pid = (pid_t)bb_strtou(d_ent->d_name, NULL, 10);
+                       if (errno != 0
+                        || pid == G.mypid
+                       /* "this PID doesn't use specified FILEs or PORT/PROTO": */
+                        || scan_recursive(subpath) == 0
+                       ) {
+                               break;
+                       }
+                       if (option_mask32 & OPT_KILL) {
+                               if (kill(pid, G.killsig) != 0) {
+                                       bb_perror_msg("kill pid %s", d_ent->d_name);
+                                       G.kill_failed = 1;
+                               }
+                       }
+                       if (!(option_mask32 & OPT_SILENT))
+                               printf("%s ", d_ent->d_name);
+                       retval = 1;
+                       break;
 
-               scan_pid_maps("maps", pid);
-               xchdir("/proc");
+               case PROC_DIR_LINKS:
+                       switch (
+                               index_in_substrings(
+                                       "cwd"  "\0" "exe"  "\0"
+                                       "root" "\0" "fd"   "\0"
+                                       "lib"  "\0" "mmap" "\0"
+                                       "maps" "\0",
+                                       d_ent->d_name
+                               )
+                       ) {
+                       enum {
+                               CWD_LINK,
+                               EXE_LINK,
+                               ROOT_LINK,
+                               FD_DIR_LINKS,
+                               LIB_DIR_LINKS,
+                               MMAP_DIR_LINKS,
+                               MAPS,
+                       };
+                       case CWD_LINK:
+                       case EXE_LINK:
+                       case ROOT_LINK:
+                               goto scan_link;
+                       case FD_DIR_LINKS:
+                       case LIB_DIR_LINKS:
+                       case MMAP_DIR_LINKS:
+                               stop_scan = scan_recursive(subpath);
+                               if (stop_scan)
+                                       retval = stop_scan;
+                               break;
+                       case MAPS:
+                               stop_scan = scan_proc_net_or_maps(subpath, 0);
+                               if (stop_scan)
+                                       retval = stop_scan;
+                       default:
+                               break;
+                       }
+                       break;
+               case PROC_SUBDIR_LINKS:
+  scan_link:
+                       if (stat(subpath, &statbuf) < 0)
+                               break;
+                       stop_scan = search_dev_inode(&statbuf);
+                       if (stop_scan)
+                               retval = stop_scan;
+               default:
+                       break;
+               }
+               free(subpath);
        }
        closedir(d);
+       G.recursion_depth--;
+       return retval;
 }
 
 int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int fuser_main(int argc UNUSED_PARAM, char **argv)
 {
-       pid_list *plist;
-       pid_t mypid;
        char **pp;
-       struct stat st;
-       unsigned port;
-       int opt;
-       int exitcode;
-       int killsig;
-/*
-fuser [OPTIONS] FILE or PORT/PROTO
-Find processes which use FILEs or PORTs
-        -m      Find processes which use same fs as FILEs
-        -4      Search only IPv4 space
-        -6      Search only IPv6 space
-        -s      Don't display PIDs
-        -k      Kill found processes
-        -SIGNAL Signal to send (default: KILL)
-*/
+
+       INIT_G();
+
        /* Handle -SIGNAL. Oh my... */
-       killsig = SIGKILL; /* yes, the default is not SIGTERM */
        pp = argv;
        while (*++pp) {
+               int sig;
                char *arg = *pp;
+
                if (arg[0] != '-')
                        continue;
                if (arg[1] == '-' && arg[2] == '\0') /* "--" */
                        break;
                if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0')
                        continue; /* it's "-4" or "-6" */
-               opt = get_signum(&arg[1]);
-               if (opt < 0)
+               sig = get_signum(&arg[1]);
+               if (sig < 0)
                        continue;
                /* "-SIGNAL" option found. Remove it and bail out */
-               killsig = opt;
+               G.killsig = sig;
                do {
                        pp[0] = arg = pp[1];
                        pp++;
@@ -261,57 +287,35 @@ Find processes which use FILEs or PORTs
        }
 
        opt_complementary = "-1"; /* at least one param */
-       opt = getopt32(argv, OPTION_STRING);
+       getopt32(argv, OPTION_STRING);
        argv += optind;
 
        pp = argv;
        while (*pp) {
                /* parse net arg */
-               char path[20], tproto[5];
-               if (sscanf(*pp, "%u/%4s", &port, tproto) != 2)
-                       goto file;
-               sprintf(path, "/proc/net/%s", tproto);
-               if (access(path, R_OK) != 0) { /* PORT/PROTO */
-                       scan_proc_net(path, port);
-               } else { /* FILE */
- file:
-                       xstat(*pp, &st);
-                       add_inode(&st);
+               unsigned port;
+               char path[sizeof("/proc/net/TCP6")];
+
+               strcpy(path, "/proc/net/");
+               if (sscanf(*pp, "%u/%4s", &port, path + sizeof("/proc/net/")-1) == 2
+                && access(path, R_OK) == 0
+               ) {
+                       /* PORT/PROTO */
+                       scan_proc_net_or_maps(path, port);
+               } else {
+                       /* FILE */
+                       struct stat statbuf;
+                       xstat(*pp, &statbuf);
+                       add_inode(&statbuf);
                }
                pp++;
        }
 
-       scan_proc_pids(); /* changes dir to "/proc" */
-
-       mypid = getpid();
-       plist = G.pid_list_head;
-       while (1) {
-               if (!plist)
-                       return EXIT_FAILURE;
-               if (plist->pid != mypid)
-                       break;
-               plist = plist->next;
-       }
-
-       exitcode = EXIT_SUCCESS;
-       do {
-               if (plist->pid != mypid) {
-                       if (opt & OPT_KILL) {
-                               if (kill(plist->pid, killsig) != 0) {
-                                       bb_perror_msg("kill pid %u", (unsigned)plist->pid);
-                                       exitcode = EXIT_FAILURE;
-                               }
-                       }
-                       if (!(opt & OPT_SILENT)) {
-                               printf("%u ", (unsigned)plist->pid);
-                       }
-               }
-               plist = plist->next;
-       } while (plist);
-
-       if (!(opt & (OPT_SILENT))) {
-               bb_putchar('\n');
+       if (scan_recursive("/proc")) {
+               if (!(option_mask32 & OPT_SILENT))
+                       bb_putchar('\n');
+               return G.kill_failed;
        }
 
-       return exitcode;
+       return EXIT_FAILURE;
 }
diff --git a/procps/iostat.c b/procps/iostat.c
new file mode 100644 (file)
index 0000000..978d234
--- /dev/null
@@ -0,0 +1,535 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Report CPU and I/O stats, based on sysstat version 9.1.2 by Sebastien Godard
+ *
+ * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//config:config IOSTAT
+//config:      bool "iostat"
+//config:      default y
+//config:      help
+//config:        Report CPU and I/O statistics
+
+//applet:IF_IOSTAT(APPLET(iostat, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_IOSTAT) += iostat.o
+
+#include "libbb.h"
+#include <sys/utsname.h>  /* struct utsname */
+
+//#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
+#define debug(fmt, ...) ((void)0)
+
+#define MAX_DEVICE_NAME 12
+#define MAX_DEVICE_NAME_STR "12"
+
+#if 1
+typedef unsigned long long cputime_t;
+typedef long long icputime_t;
+# define FMT_DATA "ll"
+# define CPUTIME_MAX (~0ULL)
+#else
+typedef unsigned long cputime_t;
+typedef long icputime_t;
+# define FMT_DATA "l"
+# define CPUTIME_MAX (~0UL)
+#endif
+
+enum {
+       STATS_CPU_USER,
+       STATS_CPU_NICE,
+       STATS_CPU_SYSTEM,
+       STATS_CPU_IDLE,
+       STATS_CPU_IOWAIT,
+       STATS_CPU_IRQ,
+       STATS_CPU_SOFTIRQ,
+       STATS_CPU_STEAL,
+       STATS_CPU_GUEST,
+
+       GLOBAL_UPTIME,
+       SMP_UPTIME,
+
+       N_STATS_CPU,
+};
+
+typedef struct {
+       cputime_t vector[N_STATS_CPU];
+} stats_cpu_t;
+
+typedef struct {
+       stats_cpu_t *prev;
+       stats_cpu_t *curr;
+       cputime_t itv;
+} stats_cpu_pair_t;
+
+typedef struct {
+       unsigned long long rd_sectors;
+       unsigned long long wr_sectors;
+       unsigned long rd_ops;
+       unsigned long wr_ops;
+} stats_dev_data_t;
+
+typedef struct stats_dev {
+       struct stats_dev *next;
+       char dname[MAX_DEVICE_NAME + 1];
+       stats_dev_data_t prev_data;
+       stats_dev_data_t curr_data;
+} stats_dev_t;
+
+/* Globals. Sort by size and access frequency. */
+struct globals {
+       smallint show_all;
+       unsigned total_cpus;            /* Number of CPUs */
+       unsigned clk_tck;               /* Number of clock ticks per second */
+       llist_t *dev_name_list;         /* List of devices entered on the command line */
+       stats_dev_t *stats_dev_list;
+       struct tm tmtime;
+       struct {
+               const char *str;
+               unsigned div;
+       } unit;
+};
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+       SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+       G.unit.str = "Blk"; \
+       G.unit.div = 1; \
+} while (0)
+
+/* Must match option string! */
+enum {
+       OPT_c = 1 << 0,
+       OPT_d = 1 << 1,
+       OPT_t = 1 << 2,
+       OPT_z = 1 << 3,
+       OPT_k = 1 << 4,
+       OPT_m = 1 << 5,
+};
+
+static ALWAYS_INLINE unsigned get_user_hz(void)
+{
+       return sysconf(_SC_CLK_TCK);
+}
+
+static ALWAYS_INLINE int this_is_smp(void)
+{
+       return (G.total_cpus > 1);
+}
+
+static void print_header(void)
+{
+       char buf[32];
+       struct utsname uts;
+
+       uname(&uts); /* never fails */
+
+       /* Date representation for the current locale */
+       strftime(buf, sizeof(buf), "%x", &G.tmtime);
+
+       printf("%s %s (%s) \t%s \t_%s_\t(%u CPU)\n\n",
+                       uts.sysname, uts.release, uts.nodename,
+                       buf, uts.machine, G.total_cpus);
+}
+
+static void get_localtime(struct tm *ptm)
+{
+       time_t timer;
+       time(&timer);
+       localtime_r(&timer, ptm);
+}
+
+static void print_timestamp(void)
+{
+       char buf[64];
+       /* %x: date representation for the current locale */
+       /* %X: time representation for the current locale */
+       strftime(buf, sizeof(buf), "%x %X", &G.tmtime);
+       printf("%s\n", buf);
+}
+
+static cputime_t get_smp_uptime(void)
+{
+       FILE *fp;
+       unsigned long sec, dec;
+
+       fp = xfopen_for_read("/proc/uptime");
+
+       if (fscanf(fp, "%lu.%lu", &sec, &dec) != 2)
+               bb_error_msg_and_die("can't read '%s'", "/proc/uptime");
+
+       fclose(fp);
+
+       return (cputime_t)sec * G.clk_tck + dec * G.clk_tck / 100;
+}
+
+/* Fetch CPU statistics from /proc/stat */
+static void get_cpu_statistics(stats_cpu_t *sc)
+{
+       FILE *fp;
+       char buf[1024];
+
+       fp = xfopen_for_read("/proc/stat");
+
+       memset(sc, 0, sizeof(*sc));
+
+       while (fgets(buf, sizeof(buf), fp)) {
+               int i;
+               char *ibuf;
+
+               /* Does the line start with "cpu "? */
+               if (!starts_with_cpu(buf) || buf[3] != ' ') {
+                       continue;
+               }
+               ibuf = buf + 4;
+               for (i = STATS_CPU_USER; i <= STATS_CPU_GUEST; i++) {
+                       ibuf = skip_whitespace(ibuf);
+                       sscanf(ibuf, "%"FMT_DATA"u", &sc->vector[i]);
+                       if (i != STATS_CPU_GUEST) {
+                               sc->vector[GLOBAL_UPTIME] += sc->vector[i];
+                       }
+                       ibuf = skip_non_whitespace(ibuf);
+               }
+               break;
+       }
+
+       if (this_is_smp()) {
+               sc->vector[SMP_UPTIME] = get_smp_uptime();
+       }
+
+       fclose(fp);
+}
+
+static ALWAYS_INLINE cputime_t get_interval(cputime_t old, cputime_t new)
+{
+       cputime_t itv = new - old;
+
+       return (itv == 0) ? 1 : itv;
+}
+
+#if CPUTIME_MAX > 0xffffffff
+/*
+ * Handle overflow conditions properly for counters which can have
+ * less bits than cputime_t, depending on the kernel version.
+ */
+/* Surprisingly, on 32bit inlining is a size win */
+static ALWAYS_INLINE cputime_t overflow_safe_sub(cputime_t prev, cputime_t curr)
+{
+       cputime_t v = curr - prev;
+
+       if ((icputime_t)v < 0     /* curr < prev - counter overflow? */
+        && prev <= 0xffffffff /* kernel uses 32bit value for the counter? */
+       ) {
+               /* Add 33th bit set to 1 to curr, compensating for the overflow */
+               /* double shift defeats "warning: left shift count >= width of type" */
+               v += ((cputime_t)1 << 16) << 16;
+       }
+       return v;
+}
+#else
+static ALWAYS_INLINE cputime_t overflow_safe_sub(cputime_t prev, cputime_t curr)
+{
+       return curr - prev;
+}
+#endif
+
+static double percent_value(cputime_t prev, cputime_t curr, cputime_t itv)
+{
+       return ((double)overflow_safe_sub(prev, curr)) / itv * 100;
+}
+
+static void print_stats_cpu_struct(stats_cpu_pair_t *stats)
+{
+       cputime_t *p = stats->prev->vector;
+       cputime_t *c = stats->curr->vector;
+       printf("        %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n",
+               percent_value(p[STATS_CPU_USER]  , c[STATS_CPU_USER]  , stats->itv),
+               percent_value(p[STATS_CPU_NICE]  , c[STATS_CPU_NICE]  , stats->itv),
+               percent_value(p[STATS_CPU_SYSTEM] + p[STATS_CPU_SOFTIRQ] + p[STATS_CPU_IRQ],
+                       c[STATS_CPU_SYSTEM] + c[STATS_CPU_SOFTIRQ] + c[STATS_CPU_IRQ], stats->itv),
+               percent_value(p[STATS_CPU_IOWAIT], c[STATS_CPU_IOWAIT], stats->itv),
+               percent_value(p[STATS_CPU_STEAL] , c[STATS_CPU_STEAL] , stats->itv),
+               percent_value(p[STATS_CPU_IDLE]  , c[STATS_CPU_IDLE]  , stats->itv)
+       );
+}
+
+static void cpu_report(stats_cpu_pair_t *stats)
+{
+       /* Always print a header */
+       puts("avg-cpu:  %user   %nice %system %iowait  %steal   %idle");
+
+       /* Print current statistics */
+       print_stats_cpu_struct(stats);
+}
+
+static void print_stats_dev_struct(stats_dev_t *stats_dev, cputime_t itv)
+{
+       stats_dev_data_t *p = &stats_dev->prev_data;
+       stats_dev_data_t *c = &stats_dev->curr_data;
+       if (option_mask32 & OPT_z)
+               if (p->rd_ops == c->rd_ops && p->wr_ops == c->wr_ops)
+                       return;
+
+       printf("%-13s %8.2f %12.2f %12.2f %10llu %10llu\n",
+               stats_dev->dname,
+               percent_value(p->rd_ops + p->wr_ops, c->rd_ops + c->wr_ops, itv),
+               percent_value(p->rd_sectors, c->rd_sectors, itv) / G.unit.div,
+               percent_value(p->wr_sectors, c->wr_sectors, itv) / G.unit.div,
+               (c->rd_sectors - p->rd_sectors) / G.unit.div,
+               (c->wr_sectors - p->wr_sectors) / G.unit.div
+       );
+}
+
+static void print_devstat_header(void)
+{
+       printf("Device:%15s%6s%s/s%6s%s/s%6s%s%6s%s\n",
+               "tps",
+               G.unit.str, "_read", G.unit.str, "_wrtn",
+               G.unit.str, "_read", G.unit.str, "_wrtn"
+       );
+}
+
+/*
+ * Is input partition of format [sdaN]?
+ */
+static int is_partition(const char *dev)
+{
+       /* Ok, this is naive... */
+       return ((dev[0] - 's') | (dev[1] - 'd') | (dev[2] - 'a')) == 0 && isdigit(dev[3]);
+}
+
+static stats_dev_t *stats_dev_find_or_new(const char *dev_name)
+{
+       stats_dev_t **curr = &G.stats_dev_list;
+
+       while (*curr != NULL) {
+               if (strcmp((*curr)->dname, dev_name) == 0)
+                       return *curr;
+               curr = &(*curr)->next;
+       }
+
+       *curr = xzalloc(sizeof(stats_dev_t));
+       strncpy((*curr)->dname, dev_name, MAX_DEVICE_NAME);
+       return *curr;
+}
+
+static void stats_dev_free(stats_dev_t *stats_dev)
+{
+       if (stats_dev) {
+               stats_dev_free(stats_dev->next);
+               free(stats_dev);
+       }
+}
+
+static void do_disk_statistics(cputime_t itv)
+{
+       char buf[128];
+       char dev_name[MAX_DEVICE_NAME + 1];
+       unsigned long long rd_sec_or_dummy;
+       unsigned long long wr_sec_or_dummy;
+       stats_dev_data_t *curr_data;
+       stats_dev_t *stats_dev;
+       FILE *fp;
+       int rc;
+
+       fp = xfopen_for_read("/proc/diskstats");
+       /* Read and possibly print stats from /proc/diskstats */
+       while (fgets(buf, sizeof(buf), fp)) {
+               sscanf(buf, "%*s %*s %"MAX_DEVICE_NAME_STR"s", dev_name);
+               if (G.dev_name_list) {
+                       /* Is device name in list? */
+                       if (!llist_find_str(G.dev_name_list, dev_name))
+                               continue;
+               } else if (is_partition(dev_name)) {
+                       continue;
+               }
+
+               stats_dev = stats_dev_find_or_new(dev_name);
+               curr_data = &stats_dev->curr_data;
+
+               rc = sscanf(buf, "%*s %*s %*s %lu %llu %llu %llu %lu %*s %llu",
+                       &curr_data->rd_ops,
+                       &rd_sec_or_dummy,
+                       &curr_data->rd_sectors,
+                       &wr_sec_or_dummy,
+                       &curr_data->wr_ops,
+                       &curr_data->wr_sectors);
+               if (rc != 6) {
+                       curr_data->rd_sectors = rd_sec_or_dummy;
+                       curr_data->wr_sectors = wr_sec_or_dummy;
+                       //curr_data->rd_ops = ;
+                       curr_data->wr_ops = (unsigned long)curr_data->rd_sectors;
+               }
+
+               if (!G.dev_name_list /* User didn't specify device */
+                && !G.show_all
+                && curr_data->rd_ops == 0
+                && curr_data->wr_ops == 0
+               ) {
+                       /* Don't print unused device */
+                       continue;
+               }
+
+               /* Print current statistics */
+               print_stats_dev_struct(stats_dev, itv);
+               stats_dev->prev_data = *curr_data;
+       }
+
+       fclose(fp);
+}
+
+static void dev_report(cputime_t itv)
+{
+       /* Always print a header */
+       print_devstat_header();
+
+       /* Fetch current disk statistics */
+       do_disk_statistics(itv);
+}
+
+//usage:#define iostat_trivial_usage
+//usage:       "[-c] [-d] [-t] [-z] [-k|-m] [ALL|BLOCKDEV...] [INTERVAL [COUNT]]"
+//usage:#define iostat_full_usage "\n\n"
+//usage:       "Report CPU and I/O statistics\n"
+//usage:     "\n       -c      Show CPU utilization"
+//usage:     "\n       -d      Show device utilization"
+//usage:     "\n       -t      Print current time"
+//usage:     "\n       -z      Omit devices with no activity"
+//usage:     "\n       -k      Use kb/s"
+//usage:     "\n       -m      Use Mb/s"
+
+int iostat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int iostat_main(int argc UNUSED_PARAM, char **argv)
+{
+       int opt;
+       unsigned interval;
+       int count;
+       stats_cpu_t stats_data[2];
+       smallint current_stats;
+
+       INIT_G();
+
+       memset(&stats_data, 0, sizeof(stats_data));
+
+       /* Get number of clock ticks per sec */
+       G.clk_tck = get_user_hz();
+
+       /* Determine number of CPUs */
+       G.total_cpus = get_cpu_count();
+       if (G.total_cpus == 0)
+               G.total_cpus = 1;
+
+       /* Parse and process arguments */
+       /* -k and -m are mutually exclusive */
+       opt_complementary = "k--m:m--k";
+       opt = getopt32(argv, "cdtzkm");
+       if (!(opt & (OPT_c + OPT_d)))
+               /* Default is -cd */
+               opt |= OPT_c + OPT_d;
+
+       argv += optind;
+
+       /* Store device names into device list */
+       while (*argv && !isdigit(*argv[0])) {
+               if (strcmp(*argv, "ALL") != 0) {
+                       /* If not ALL, save device name */
+                       char *dev_name = skip_dev_pfx(*argv);
+                       if (!llist_find_str(G.dev_name_list, dev_name)) {
+                               llist_add_to(&G.dev_name_list, dev_name);
+                       }
+               } else {
+                       G.show_all = 1;
+               }
+               argv++;
+       }
+
+       interval = 0;
+       count = 1;
+       if (*argv) {
+               /* Get interval */
+               interval = xatoi_positive(*argv);
+               count = (interval != 0 ? -1 : 1);
+               argv++;
+               if (*argv)
+                       /* Get count value */
+                       count = xatoi_positive(*argv);
+       }
+
+       if (opt & OPT_m) {
+               G.unit.str = " MB";
+               G.unit.div = 2048;
+       }
+
+       if (opt & OPT_k) {
+               G.unit.str = " kB";
+               G.unit.div = 2;
+       }
+
+       get_localtime(&G.tmtime);
+
+       /* Display header */
+       print_header();
+
+       current_stats = 0;
+       /* Main loop */
+       for (;;) {
+               stats_cpu_pair_t stats;
+
+               stats.prev = &stats_data[current_stats ^ 1];
+               stats.curr = &stats_data[current_stats];
+
+               /* Fill the time structure */
+               get_localtime(&G.tmtime);
+
+               /* Fetch current CPU statistics */
+               get_cpu_statistics(stats.curr);
+
+               /* Get interval */
+               stats.itv = get_interval(
+                       stats.prev->vector[GLOBAL_UPTIME],
+                       stats.curr->vector[GLOBAL_UPTIME]
+               );
+
+               if (opt & OPT_t)
+                       print_timestamp();
+
+               if (opt & OPT_c) {
+                       cpu_report(&stats);
+                       if (opt & OPT_d)
+                               /* Separate outputs by a newline */
+                               bb_putchar('\n');
+               }
+
+               if (opt & OPT_d) {
+                       if (this_is_smp()) {
+                               stats.itv = get_interval(
+                                       stats.prev->vector[SMP_UPTIME],
+                                       stats.curr->vector[SMP_UPTIME]
+                               );
+                       }
+                       dev_report(stats.itv);
+               }
+
+               bb_putchar('\n');
+
+               if (count > 0) {
+                       if (--count == 0)
+                               break;
+               }
+
+               /* Swap stats */
+               current_stats ^= 1;
+
+               sleep(interval);
+       }
+
+       if (ENABLE_FEATURE_CLEAN_UP) {
+               llist_free(G.dev_name_list, NULL);
+               stats_dev_free(G.stats_dev_list);
+               free(&G);
+       }
+
+       return EXIT_SUCCESS;
+}
index 1d343ed..c5c7a8d 100644 (file)
@@ -5,9 +5,45 @@
  * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define kill_trivial_usage
+//usage:       "[-l] [-SIG] PID..."
+//usage:#define kill_full_usage "\n\n"
+//usage:       "Send a signal (default: TERM) to given PIDs\n"
+//usage:     "\n       -l      List all signal names and numbers"
+/* //usage:  "\n       -s SIG  Yet another way of specifying SIG" */
+//usage:
+//usage:#define kill_example_usage
+//usage:       "$ ps | grep apache\n"
+//usage:       "252 root     root     S [apache]\n"
+//usage:       "263 www-data www-data S [apache]\n"
+//usage:       "264 www-data www-data S [apache]\n"
+//usage:       "265 www-data www-data S [apache]\n"
+//usage:       "266 www-data www-data S [apache]\n"
+//usage:       "267 www-data www-data S [apache]\n"
+//usage:       "$ kill 252\n"
+//usage:
+//usage:#define killall_trivial_usage
+//usage:       "[-l] [-q] [-SIG] PROCESS_NAME..."
+//usage:#define killall_full_usage "\n\n"
+//usage:       "Send a signal (default: TERM) to given processes\n"
+//usage:     "\n       -l      List all signal names and numbers"
+/* //usage:  "\n       -s SIG  Yet another way of specifying SIG" */
+//usage:     "\n       -q      Don't complain if no processes were killed"
+//usage:
+//usage:#define killall_example_usage
+//usage:       "$ killall apache\n"
+//usage:
+//usage:#define killall5_trivial_usage
+//usage:       "[-l] [-SIG] [-o PID]..."
+//usage:#define killall5_full_usage "\n\n"
+//usage:       "Send a signal (default: TERM) to all processes outside current session\n"
+//usage:     "\n       -l      List all signal names and numbers"
+//usage:     "\n       -o PID  Don't signal this PID"
+/* //usage:  "\n       -s SIG  Yet another way of specifying SIG" */
+
 #include "libbb.h"
 
 /* Note: kill_main is directly called from shell in order to implement
@@ -24,7 +60,7 @@
  * This is needed to avoid collision with kill -9 ... syntax
  */
 
-int kill_main(int argc, char **argv)
+int kill_main(int argc UNUSED_PARAM, char **argv)
 {
        char *arg;
        pid_t pid;
@@ -43,10 +79,9 @@ int kill_main(int argc, char **argv)
 #endif
 
        /* Parse any options */
-       argc--;
        arg = *++argv;
 
-       if (argc < 1 || arg[0] != '-') {
+       if (!arg || arg[0] != '-') {
                goto do_it_now;
        }
 
@@ -55,13 +90,14 @@ int kill_main(int argc, char **argv)
         * echo "Died of SIG`kill -l $?`"
         * We try to mimic what kill from coreutils-6.8 does */
        if (arg[1] == 'l' && arg[2] == '\0') {
-               if (argc == 1) {
+               arg = *++argv;
+               if (!arg) {
                        /* Print the whole signal list */
                        print_signames();
                        return 0;
                }
                /* -l <sig list> */
-               while ((arg = *++argv)) {
+               do {
                        if (isdigit(arg[0])) {
                                signo = bb_strtou(arg, NULL, 10);
                                if (errno) {
@@ -82,8 +118,8 @@ int kill_main(int argc, char **argv)
                                }
                                printf("%d\n", signo);
                        }
-               }
-               /* If they specified -l, we are all done */
+                       arg = *++argv;
+               } while (arg);
                return EXIT_SUCCESS;
        }
 
@@ -91,8 +127,7 @@ int kill_main(int argc, char **argv)
        if (killall && arg[1] == 'q' && arg[2] == '\0') {
                quiet = 1;
                arg = *++argv;
-               argc--;
-               if (argc < 1)
+               if (!arg)
                        bb_show_usage();
                if (arg[0] != '-')
                        goto do_it_now;
@@ -104,8 +139,7 @@ int kill_main(int argc, char **argv)
        if (killall5 && arg[0] == 'o')
                goto do_it_now;
 
-       if (argc > 1 && arg[0] == 's' && arg[1] == '\0') { /* -s SIG? */
-               argc--;
+       if (argv[1] && arg[0] == 's' && arg[1] == '\0') { /* -s SIG? */
                arg = *++argv;
        } /* else it must be -SIG */
        signo = get_signum(arg);
@@ -114,7 +148,6 @@ int kill_main(int argc, char **argv)
                return EXIT_FAILURE;
        }
        arg = *++argv;
-       argc--;
 
  do_it_now:
        pid = getpid();
@@ -122,38 +155,44 @@ int kill_main(int argc, char **argv)
        if (killall5) {
                pid_t sid;
                procps_status_t* p = NULL;
-               int ret = 0;
+               /* compat: exitcode 2 is "no one was signaled" */
+               int ret = 2;
 
                /* Find out our session id */
                sid = getsid(pid);
                /* Stop all processes */
-               kill(-1, SIGSTOP);
+               if (signo != SIGSTOP && signo != SIGCONT)
+                       kill(-1, SIGSTOP);
                /* Signal all processes except those in our session */
-               while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID))) {
-                       int i;
+               while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID)) != NULL) {
+                       char **args;
 
                        if (p->sid == (unsigned)sid
+                        || p->sid == 0 /* compat: kernel thread, don't signal it */
                         || p->pid == (unsigned)pid
-                        || p->pid == 1)
+                        || p->pid == 1
+                       ) {
                                continue;
+                       }
 
                        /* All remaining args must be -o PID options.
                         * Check p->pid against them. */
-                       for (i = 0; i < argc; i++) {
+                       args = argv;
+                       while (*args) {
                                pid_t omit;
 
-                               arg = argv[i];
+                               arg = *args++;
                                if (arg[0] != '-' || arg[1] != 'o') {
                                        bb_error_msg("bad option '%s'", arg);
                                        ret = 1;
                                        goto resume;
                                }
                                arg += 2;
-                               if (!arg[0] && argv[++i])
-                                       arg = argv[i];
+                               if (!arg[0] && *args)
+                                       arg = *args++;
                                omit = bb_strtoi(arg, NULL, 10);
                                if (errno) {
-                                       bb_error_msg("bad pid '%s'", arg);
+                                       bb_error_msg("invalid number '%s'", arg);
                                        ret = 1;
                                        goto resume;
                                }
@@ -161,23 +200,25 @@ int kill_main(int argc, char **argv)
                                        goto dont_kill;
                        }
                        kill(p->pid, signo);
+                       ret = 0;
  dont_kill: ;
                }
  resume:
                /* And let them continue */
-               kill(-1, SIGCONT);
+               if (signo != SIGSTOP && signo != SIGCONT)
+                       kill(-1, SIGCONT);
                return ret;
        }
 
        /* Pid or name is required for kill/killall */
-       if (argc < 1) {
+       if (!arg) {
                bb_error_msg("you need to specify whom to kill");
                return EXIT_FAILURE;
        }
 
        if (killall) {
                /* Looks like they want to do a killall.  Do that */
-               while (arg) {
+               do {
                        pid_t* pidList;
 
                        pidList = find_pid_by_name(arg);
@@ -200,23 +241,44 @@ int kill_main(int argc, char **argv)
                        }
                        free(pidList);
                        arg = *++argv;
-               }
+               } while (arg);
                return errors;
        }
 
        /* Looks like they want to do a kill. Do that */
        while (arg) {
-               /* Support shell 'space' trick */
-               if (arg[0] == ' ')
-                       arg++;
+#if ENABLE_ASH || ENABLE_HUSH
+               /*
+                * We need to support shell's "hack formats" of
+                * " -PRGP_ID" (yes, with a leading space)
+                * and " PID1 PID2 PID3" (with degenerate case "")
+                */
+               while (*arg != '\0') {
+                       char *end;
+                       if (*arg == ' ')
+                               arg++;
+                       pid = bb_strtoi(arg, &end, 10);
+                       if (errno && (errno != EINVAL || *end != ' ')) {
+                               bb_error_msg("invalid number '%s'", arg);
+                               errors++;
+                               break;
+                       }
+                       if (kill(pid, signo) != 0) {
+                               bb_perror_msg("can't kill pid %d", (int)pid);
+                               errors++;
+                       }
+                       arg = end; /* can only point to ' ' or '\0' now */
+               }
+#else
                pid = bb_strtoi(arg, NULL, 10);
                if (errno) {
-                       bb_error_msg("bad pid '%s'", arg);
+                       bb_error_msg("invalid number '%s'", arg);
                        errors++;
                } else if (kill(pid, signo) != 0) {
                        bb_perror_msg("can't kill pid %d", (int)pid);
                        errors++;
                }
+#endif
                arg = *++argv;
        }
        return errors;
diff --git a/procps/lsof.c b/procps/lsof.c
new file mode 100644 (file)
index 0000000..b0156a5
--- /dev/null
@@ -0,0 +1,79 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini lsof implementation for busybox
+ *
+ * Copyright (C) 2012 by Sven Oliver 'SvOlli' Moll <svolli@svolli.de>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//config:config LSOF
+//config:      bool "lsof"
+//config:      default y
+//config:      help
+//config:        Show open files in the format of:
+//config:        PID <TAB> /path/to/executable <TAB> /path/to/opened/file
+
+//applet:IF_LSOF(APPLET(lsof, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_LSOF) += lsof.o
+
+//usage:#define lsof_trivial_usage
+//usage:       ""
+//usage:#define lsof_full_usage "\n\n"
+//usage:       "Show all open files"
+
+#include "libbb.h"
+
+/*
+ * Examples of "standard" lsof output:
+ *
+ * COMMAND    PID USER   FD   TYPE             DEVICE     SIZE       NODE NAME
+ * init         1 root  cwd    DIR                8,5     4096          2 /
+ * init         1 root  rtd    DIR                8,5     4096          2 /
+ * init         1 root  txt    REG                8,5   872400      63408 /app/busybox-1.19.2/busybox
+ * rpc.portm 1064 root  mem    REG                8,5    43494      47451 /app/glibc-2.11/lib/libnss_files-2.11.so
+ * rpc.portm 1064 root    3u  IPv4               2178                 UDP *:111
+ * rpc.portm 1064 root    4u  IPv4               1244                 TCP *:111 (LISTEN)
+ * runsvdir  1116 root    0r   CHR                1,3                1214 /dev/null
+ * runsvdir  1116 root    1w   CHR                1,3                1214 /dev/null
+ * runsvdir  1116 root    2w   CHR                1,3                1214 /dev/null
+ * runsvdir  1116 root    3r   DIR                8,6     1560      58359 /.local/var/service
+ * gpm       1128 root    4u  unix 0xffff88007c09ccc0                1302 /dev/gpmctl
+ */
+
+int lsof_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int lsof_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+       procps_status_t *proc = NULL;
+
+       while ((proc = procps_scan(proc, PSSCAN_PID|PSSCAN_EXE)) != NULL) {
+               char name[sizeof("/proc/%u/fd/0123456789") + sizeof(int)*3];
+               unsigned baseofs;
+               DIR *d_fd;
+               char *fdlink;
+               struct dirent *entry;
+
+               if (getpid() == proc->pid)
+                       continue;
+
+               baseofs = sprintf(name, "/proc/%u/fd/", proc->pid);
+               d_fd = opendir(name);
+               if (d_fd) {
+                       while ((entry = readdir(d_fd)) != NULL) {
+                               /* Skip entries '.' and '..' (and any hidden file) */
+                               if (entry->d_name[0] == '.')
+                                       continue;
+
+                               safe_strncpy(name + baseofs, entry->d_name, 10);
+                               if ((fdlink = xmalloc_readlink(name)) != NULL) {
+                                       printf("%d\t%s\t%s\n", proc->pid, proc->exe, fdlink);
+                                       free(fdlink);
+                               }
+                       }
+                       closedir(d_fd);
+               }
+       }
+
+       return EXIT_SUCCESS;
+}
diff --git a/procps/mpstat.c b/procps/mpstat.c
new file mode 100644 (file)
index 0000000..aa5a5c7
--- /dev/null
@@ -0,0 +1,977 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Per-processor statistics, based on sysstat version 9.1.2 by Sebastien Godard
+ *
+ * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//applet:IF_MPSTAT(APPLET(mpstat, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MPSTAT) += mpstat.o
+
+//config:config MPSTAT
+//config:      bool "mpstat"
+//config:      default y
+//config:      help
+//config:        Per-processor statistics
+
+#include "libbb.h"
+#include <sys/utsname.h>  /* struct utsname */
+
+//#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
+#define debug(fmt, ...) ((void)0)
+
+/* Size of /proc/interrupts line, CPU data excluded */
+#define INTERRUPTS_LINE    64
+/* Maximum number of interrupts */
+#define NR_IRQS            256
+#define NR_IRQCPU_PREALLOC 3
+#define MAX_IRQNAME_LEN    16
+#define MAX_PF_NAME        512
+/* sysstat 9.0.6 uses width 8, but newer code which also prints /proc/softirqs
+ * data needs more: "interrupts" in /proc/softirqs have longer names,
+ * most are up to 8 chars, one (BLOCK_IOPOLL) is even longer.
+ * We are printing headers in the " IRQNAME/s" form, experimentally
+ * anything smaller than 10 chars looks ugly for /proc/softirqs stats.
+ */
+#define INTRATE_SCRWIDTH      10
+#define INTRATE_SCRWIDTH_STR "10"
+
+/* System files */
+#define PROCFS_STAT       "/proc/stat"
+#define PROCFS_INTERRUPTS "/proc/interrupts"
+#define PROCFS_SOFTIRQS   "/proc/softirqs"
+#define PROCFS_UPTIME     "/proc/uptime"
+
+
+#if 1
+typedef unsigned long long data_t;
+typedef long long idata_t;
+#define FMT_DATA "ll"
+#define DATA_MAX ULLONG_MAX
+#else
+typedef unsigned long data_t;
+typedef long idata_t;
+#define FMT_DATA "l"
+#define DATA_MAX ULONG_MAX
+#endif
+
+
+struct stats_irqcpu {
+       unsigned interrupts;
+       char irq_name[MAX_IRQNAME_LEN];
+};
+
+struct stats_cpu {
+       data_t cpu_user;
+       data_t cpu_nice;
+       data_t cpu_system;
+       data_t cpu_idle;
+       data_t cpu_iowait;
+       data_t cpu_steal;
+       data_t cpu_irq;
+       data_t cpu_softirq;
+       data_t cpu_guest;
+};
+
+struct stats_irq {
+       data_t irq_nr;
+};
+
+
+/* Globals. Sort by size and access frequency. */
+struct globals {
+       int interval;
+       int count;
+       unsigned cpu_nr;                /* Number of CPUs */
+       unsigned irqcpu_nr;             /* Number of interrupts per CPU */
+       unsigned softirqcpu_nr;         /* Number of soft interrupts per CPU */
+       unsigned options;
+       unsigned hz;
+       unsigned cpu_bitmap_len;
+       smallint p_option;
+       // 9.0.6 does not do it. Try "mpstat -A 1 2" - headers are repeated!
+       //smallint header_done;
+       //smallint avg_header_done;
+       unsigned char *cpu_bitmap;      /* Bit 0: global, bit 1: 1st proc... */
+       data_t global_uptime[3];
+       data_t per_cpu_uptime[3];
+       struct stats_cpu *st_cpu[3];
+       struct stats_irq *st_irq[3];
+       struct stats_irqcpu *st_irqcpu[3];
+       struct stats_irqcpu *st_softirqcpu[3];
+       struct tm timestamp[3];
+};
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+       SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+/* The selected interrupts statistics (bits in G.options) */
+enum {
+       D_CPU      = 1 << 0,
+       D_IRQ_SUM  = 1 << 1,
+       D_IRQ_CPU  = 1 << 2,
+       D_SOFTIRQS = 1 << 3,
+};
+
+
+/* Is option on? */
+static ALWAYS_INLINE int display_opt(int opt)
+{
+       return (opt & G.options);
+}
+
+#if DATA_MAX > 0xffffffff
+/*
+ * Handle overflow conditions properly for counters which can have
+ * less bits than data_t, depending on the kernel version.
+ */
+/* Surprisingly, on 32bit inlining is a size win */
+static ALWAYS_INLINE data_t overflow_safe_sub(data_t prev, data_t curr)
+{
+       data_t v = curr - prev;
+
+       if ((idata_t)v < 0     /* curr < prev - counter overflow? */
+        && prev <= 0xffffffff /* kernel uses 32bit value for the counter? */
+       ) {
+               /* Add 33th bit set to 1 to curr, compensating for the overflow */
+               /* double shift defeats "warning: left shift count >= width of type" */
+               v += ((data_t)1 << 16) << 16;
+       }
+       return v;
+}
+#else
+static ALWAYS_INLINE data_t overflow_safe_sub(data_t prev, data_t curr)
+{
+       return curr - prev;
+}
+#endif
+
+static double percent_value(data_t prev, data_t curr, data_t itv)
+{
+       return ((double)overflow_safe_sub(prev, curr)) / itv * 100;
+}
+
+static double hz_value(data_t prev, data_t curr, data_t itv)
+{
+       //bb_error_msg("curr:%lld prev:%lld G.hz:%u", curr, prev, G.hz);
+       return ((double)overflow_safe_sub(prev, curr)) / itv * G.hz;
+}
+
+static ALWAYS_INLINE data_t jiffies_diff(data_t old, data_t new)
+{
+       data_t diff = new - old;
+       return (diff == 0) ? 1 : diff;
+}
+
+static int is_cpu_in_bitmap(unsigned cpu)
+{
+       return G.cpu_bitmap[cpu >> 3] & (1 << (cpu & 7));
+}
+
+static void write_irqcpu_stats(struct stats_irqcpu *per_cpu_stats[],
+               int total_irqs,
+               data_t itv,
+               int prev, int current,
+               const char *prev_str, const char *current_str)
+{
+       int j;
+       int offset, cpu;
+       struct stats_irqcpu *p0, *q0;
+
+       /* Check if number of IRQs has changed */
+       if (G.interval != 0) {
+               for (j = 0; j <= total_irqs; j++) {
+                       p0 = &per_cpu_stats[current][j];
+                       if (p0->irq_name[0] != '\0') {
+                               q0 = &per_cpu_stats[prev][j];
+                               if (strcmp(p0->irq_name, q0->irq_name) != 0) {
+                                       /* Strings are different */
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       /* Print header */
+       printf("\n%-11s  CPU", prev_str);
+       {
+               /* A bit complex code to "buy back" space if one header is too wide.
+                * Here's how it looks like. BLOCK_IOPOLL eats too much space,
+                * and latter headers use smaller width to compensate:
+                * ...BLOCK/s BLOCK_IOPOLL/s TASKLET/s SCHED/s HRTIMER/s  RCU/s
+                * ...   2.32      0.00      0.01     17.58      0.14    141.96
+                */
+               int expected_len = 0;
+               int printed_len = 0;
+               for (j = 0; j < total_irqs; j++) {
+                       p0 = &per_cpu_stats[current][j];
+                       if (p0->irq_name[0] != '\0') {
+                               int n = (INTRATE_SCRWIDTH-3) - (printed_len - expected_len);
+                               printed_len += printf(" %*s/s", n > 0 ? n : 0, skip_whitespace(p0->irq_name));
+                               expected_len += INTRATE_SCRWIDTH;
+                       }
+               }
+       }
+       bb_putchar('\n');
+
+       for (cpu = 1; cpu <= G.cpu_nr; cpu++) {
+               /* Check if we want stats about this CPU */
+               if (!is_cpu_in_bitmap(cpu) && G.p_option) {
+                       continue;
+               }
+
+               printf("%-11s %4u", current_str, cpu - 1);
+
+               for (j = 0; j < total_irqs; j++) {
+                       /* IRQ field set only for proc 0 */
+                       p0 = &per_cpu_stats[current][j];
+
+                       /*
+                        * An empty string for irq name means that
+                        * interrupt is no longer used.
+                        */
+                       if (p0->irq_name[0] != '\0') {
+                               offset = j;
+                               q0 = &per_cpu_stats[prev][offset];
+
+                               /*
+                                * If we want stats for the time since boot
+                                * we have p0->irq != q0->irq.
+                                */
+                               if (strcmp(p0->irq_name, q0->irq_name) != 0
+                                && G.interval != 0
+                               ) {
+                                       if (j) {
+                                               offset = j - 1;
+                                               q0 = &per_cpu_stats[prev][offset];
+                                       }
+                                       if (strcmp(p0->irq_name, q0->irq_name) != 0
+                                        && (j + 1 < total_irqs)
+                                       ) {
+                                               offset = j + 1;
+                                               q0 = &per_cpu_stats[prev][offset];
+                                       }
+                               }
+
+                               if (strcmp(p0->irq_name, q0->irq_name) == 0
+                                || G.interval == 0
+                               ) {
+                                       struct stats_irqcpu *p, *q;
+                                       p = &per_cpu_stats[current][(cpu - 1) * total_irqs + j];
+                                       q = &per_cpu_stats[prev][(cpu - 1) * total_irqs + offset];
+                                       printf("%"INTRATE_SCRWIDTH_STR".2f",
+                                               (double)(p->interrupts - q->interrupts) / itv * G.hz);
+                               } else {
+                                       printf("        N/A");
+                               }
+                       }
+               }
+               bb_putchar('\n');
+       }
+}
+
+static data_t get_per_cpu_interval(const struct stats_cpu *scc,
+               const struct stats_cpu *scp)
+{
+       return ((scc->cpu_user + scc->cpu_nice +
+                scc->cpu_system + scc->cpu_iowait +
+                scc->cpu_idle + scc->cpu_steal +
+                scc->cpu_irq + scc->cpu_softirq) -
+               (scp->cpu_user + scp->cpu_nice +
+                scp->cpu_system + scp->cpu_iowait +
+                scp->cpu_idle + scp->cpu_steal +
+                scp->cpu_irq + scp->cpu_softirq));
+}
+
+static void print_stats_cpu_struct(const struct stats_cpu *p,
+               const struct stats_cpu *c,
+               data_t itv)
+{
+       printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n",
+               percent_value(p->cpu_user - p->cpu_guest,
+               /**/                          c->cpu_user - c->cpu_guest, itv),
+               percent_value(p->cpu_nice   , c->cpu_nice   , itv),
+               percent_value(p->cpu_system , c->cpu_system , itv),
+               percent_value(p->cpu_iowait , c->cpu_iowait , itv),
+               percent_value(p->cpu_irq    , c->cpu_irq    , itv),
+               percent_value(p->cpu_softirq, c->cpu_softirq, itv),
+               percent_value(p->cpu_steal  , c->cpu_steal  , itv),
+               percent_value(p->cpu_guest  , c->cpu_guest  , itv),
+               percent_value(p->cpu_idle   , c->cpu_idle   , itv)
+       );
+}
+
+static void write_stats_core(int prev, int current,
+               const char *prev_str, const char *current_str)
+{
+       struct stats_cpu *scc, *scp;
+       data_t itv, global_itv;
+       int cpu;
+
+       /* Compute time interval */
+       itv = global_itv = jiffies_diff(G.global_uptime[prev], G.global_uptime[current]);
+
+       /* Reduce interval to one CPU */
+       if (G.cpu_nr > 1)
+               itv = jiffies_diff(G.per_cpu_uptime[prev], G.per_cpu_uptime[current]);
+
+       /* Print CPU stats */
+       if (display_opt(D_CPU)) {
+
+               ///* This is done exactly once */
+               //if (!G.header_done) {
+                       printf("\n%-11s  CPU    %%usr   %%nice    %%sys %%iowait    %%irq   %%soft  %%steal  %%guest   %%idle\n",
+                               prev_str
+                       );
+               //      G.header_done = 1;
+               //}
+
+               for (cpu = 0; cpu <= G.cpu_nr; cpu++) {
+                       data_t per_cpu_itv;
+
+                       /* Print stats about this particular CPU? */
+                       if (!is_cpu_in_bitmap(cpu))
+                               continue;
+
+                       scc = &G.st_cpu[current][cpu];
+                       scp = &G.st_cpu[prev][cpu];
+                       per_cpu_itv = global_itv;
+
+                       printf((cpu ? "%-11s %4u" : "%-11s  all"), current_str, cpu - 1);
+                       if (cpu) {
+                               double idle;
+                               /*
+                                * If the CPU is offline, then it isn't in /proc/stat,
+                                * so all values are 0.
+                                * NB: Guest time is already included in user time.
+                                */
+                               if ((scc->cpu_user | scc->cpu_nice | scc->cpu_system |
+                                    scc->cpu_iowait | scc->cpu_idle | scc->cpu_steal |
+                                    scc->cpu_irq | scc->cpu_softirq) == 0
+                               ) {
+                                       /*
+                                        * Set current struct fields to values from prev.
+                                        * iteration. Then their values won't jump from
+                                        * zero, when the CPU comes back online.
+                                        */
+                                       *scc = *scp;
+                                       idle = 0.0;
+                                       goto print_zeros;
+                               }
+                               /* Compute interval again for current proc */
+                               per_cpu_itv = get_per_cpu_interval(scc, scp);
+                               if (per_cpu_itv == 0) {
+                                       /*
+                                        * If the CPU is tickless then there is no change in CPU values
+                                        * but the sum of values is not zero.
+                                        */
+                                       idle = 100.0;
+ print_zeros:
+                                       printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n",
+                                               0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, idle);
+                                       continue;
+                               }
+                       }
+                       print_stats_cpu_struct(scp, scc, per_cpu_itv);
+               }
+       }
+
+       /* Print total number of IRQs per CPU */
+       if (display_opt(D_IRQ_SUM)) {
+
+               ///* Print average header, this is done exactly once */
+               //if (!G.avg_header_done) {
+                       printf("\n%-11s  CPU    intr/s\n", prev_str);
+               //      G.avg_header_done = 1;
+               //}
+
+               for (cpu = 0; cpu <= G.cpu_nr; cpu++) {
+                       data_t per_cpu_itv;
+
+                       /* Print stats about this CPU? */
+                       if (!is_cpu_in_bitmap(cpu))
+                               continue;
+
+                       per_cpu_itv = itv;
+                       printf((cpu ? "%-11s %4u" : "%-11s  all"), current_str, cpu - 1);
+                       if (cpu) {
+                               scc = &G.st_cpu[current][cpu];
+                               scp = &G.st_cpu[prev][cpu];
+                               /* Compute interval again for current proc */
+                               per_cpu_itv = get_per_cpu_interval(scc, scp);
+                               if (per_cpu_itv == 0) {
+                                       printf(" %9.2f\n", 0.0);
+                                       continue;
+                               }
+                       }
+                       //bb_error_msg("G.st_irq[%u][%u].irq_nr:%lld - G.st_irq[%u][%u].irq_nr:%lld",
+                       // current, cpu, G.st_irq[prev][cpu].irq_nr, prev, cpu, G.st_irq[current][cpu].irq_nr);
+                       printf(" %9.2f\n", hz_value(G.st_irq[prev][cpu].irq_nr, G.st_irq[current][cpu].irq_nr, per_cpu_itv));
+               }
+       }
+
+       if (display_opt(D_IRQ_CPU)) {
+               write_irqcpu_stats(G.st_irqcpu, G.irqcpu_nr,
+                               itv,
+                               prev, current,
+                               prev_str, current_str
+               );
+       }
+
+       if (display_opt(D_SOFTIRQS)) {
+               write_irqcpu_stats(G.st_softirqcpu, G.softirqcpu_nr,
+                               itv,
+                               prev, current,
+                               prev_str, current_str
+               );
+       }
+}
+
+/*
+ * Print the statistics
+ */
+static void write_stats(int current)
+{
+       char prev_time[16];
+       char curr_time[16];
+
+       strftime(prev_time, sizeof(prev_time), "%X", &G.timestamp[!current]);
+       strftime(curr_time, sizeof(curr_time), "%X", &G.timestamp[current]);
+
+       write_stats_core(!current, current, prev_time, curr_time);
+}
+
+static void write_stats_avg(int current)
+{
+       write_stats_core(2, current, "Average:", "Average:");
+}
+
+/*
+ * Read CPU statistics
+ */
+static void get_cpu_statistics(struct stats_cpu *cpu, data_t *up, data_t *up0)
+{
+       FILE *fp;
+       char buf[1024];
+
+       fp = xfopen_for_read(PROCFS_STAT);
+
+       while (fgets(buf, sizeof(buf), fp)) {
+               data_t sum;
+               unsigned cpu_number;
+               struct stats_cpu *cp;
+
+               if (!starts_with_cpu(buf))
+                       continue; /* not "cpu" */
+
+               cp = cpu; /* for "cpu " case */
+               if (buf[3] != ' ') {
+                       /* "cpuN " */
+                       if (G.cpu_nr == 0
+                        || sscanf(buf + 3, "%u ", &cpu_number) != 1
+                        || cpu_number >= G.cpu_nr
+                       ) {
+                               continue;
+                       }
+                       cp = &cpu[cpu_number + 1];
+               }
+
+               /* Read the counters, save them */
+               /* Not all fields have to be present */
+               memset(cp, 0, sizeof(*cp));
+               sscanf(buf, "%*s"
+                       " %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u"
+                       " %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u"
+                       " %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u",
+                       &cp->cpu_user, &cp->cpu_nice, &cp->cpu_system,
+                       &cp->cpu_idle, &cp->cpu_iowait, &cp->cpu_irq,
+                       &cp->cpu_softirq, &cp->cpu_steal, &cp->cpu_guest
+               );
+               /*
+                * Compute uptime in jiffies (1/HZ), it'll be the sum of
+                * individual CPU's uptimes.
+                * NB: We have to omit cpu_guest, because cpu_user includes it.
+                */
+               sum = cp->cpu_user + cp->cpu_nice + cp->cpu_system +
+                       cp->cpu_idle + cp->cpu_iowait + cp->cpu_irq +
+                       cp->cpu_softirq + cp->cpu_steal;
+
+               if (buf[3] == ' ') {
+                       /* "cpu " */
+                       *up = sum;
+               } else {
+                       /* "cpuN " */
+                       if (cpu_number == 0 && *up0 != 0) {
+                               /* Compute uptime of single CPU */
+                               *up0 = sum;
+                       }
+               }
+       }
+       fclose(fp);
+}
+
+/*
+ * Read IRQs from /proc/stat
+ */
+static void get_irqs_from_stat(struct stats_irq *irq)
+{
+       FILE *fp;
+       char buf[1024];
+
+       fp = fopen_for_read(PROCFS_STAT);
+       if (!fp)
+               return;
+
+       while (fgets(buf, sizeof(buf), fp)) {
+               //bb_error_msg("/proc/stat:'%s'", buf);
+               if (strncmp(buf, "intr ", 5) == 0) {
+                       /* Read total number of IRQs since system boot */
+                       sscanf(buf + 5, "%"FMT_DATA"u", &irq->irq_nr);
+               }
+       }
+
+       fclose(fp);
+}
+
+/*
+ * Read stats from /proc/interrupts or /proc/softirqs
+ */
+static void get_irqs_from_interrupts(const char *fname,
+               struct stats_irqcpu *per_cpu_stats[],
+               int irqs_per_cpu, int current)
+{
+       FILE *fp;
+       struct stats_irq *irq_i;
+       struct stats_irqcpu *ic;
+       char *buf;
+       unsigned buflen;
+       unsigned cpu;
+       unsigned irq;
+       int cpu_index[G.cpu_nr];
+       int iindex;
+
+// Moved to caller.
+// Otherwise reading of /proc/softirqs
+// was resetting counts to 0 after we painstakingly collected them from
+// /proc/interrupts. Which resulted in:
+// 01:32:47 PM  CPU    intr/s
+// 01:32:47 PM  all    591.47
+// 01:32:47 PM    0      0.00 <= ???
+// 01:32:47 PM    1      0.00 <= ???
+//     for (cpu = 1; cpu <= G.cpu_nr; cpu++) {
+//             G.st_irq[current][cpu].irq_nr = 0;
+//             //bb_error_msg("G.st_irq[%u][%u].irq_nr=0", current, cpu);
+//     }
+
+       fp = fopen_for_read(fname);
+       if (!fp)
+               return;
+
+       buflen = INTERRUPTS_LINE + 16 * G.cpu_nr;
+       buf = xmalloc(buflen);
+
+       /* Parse header and determine, which CPUs are online */
+       iindex = 0;
+       while (fgets(buf, buflen, fp)) {
+               char *cp, *next;
+               next = buf;
+               while ((cp = strstr(next, "CPU")) != NULL
+                && iindex < G.cpu_nr
+               ) {
+                       cpu = strtoul(cp + 3, &next, 10);
+                       cpu_index[iindex++] = cpu;
+               }
+               if (iindex) /* We found header */
+                       break;
+       }
+
+       irq = 0;
+       while (fgets(buf, buflen, fp)
+        && irq < irqs_per_cpu
+       ) {
+               int len;
+               char last_char;
+               char *cp;
+
+               /* Skip over "IRQNAME:" */
+               cp = strchr(buf, ':');
+               if (!cp)
+                       continue;
+               last_char = cp[-1];
+
+               ic = &per_cpu_stats[current][irq];
+               len = cp - buf;
+               if (len >= sizeof(ic->irq_name)) {
+                       len = sizeof(ic->irq_name) - 1;
+               }
+               safe_strncpy(ic->irq_name, buf, len + 1);
+               //bb_error_msg("%s: irq%d:'%s' buf:'%s'", fname, irq, ic->irq_name, buf);
+               cp++;
+
+               for (cpu = 0; cpu < iindex; cpu++) {
+                       char *next;
+                       ic = &per_cpu_stats[current][cpu_index[cpu] * irqs_per_cpu + irq];
+                       irq_i = &G.st_irq[current][cpu_index[cpu] + 1];
+                       ic->interrupts = strtoul(cp, &next, 10);
+                       /* Count only numerical IRQs */
+                       if (isdigit(last_char)) {
+                               irq_i->irq_nr += ic->interrupts;
+                               //bb_error_msg("G.st_irq[%u][%u].irq_nr + %u = %lld",
+                               // current, cpu_index[cpu] + 1, ic->interrupts, irq_i->irq_nr);
+                       }
+                       cp = next;
+               }
+               irq++;
+       }
+       fclose(fp);
+       free(buf);
+
+       while (irq < irqs_per_cpu) {
+               /* Number of interrupts per CPU has changed */
+               ic = &per_cpu_stats[current][irq];
+               ic->irq_name[0] = '\0'; /* False interrupt */
+               irq++;
+       }
+}
+
+static void get_uptime(data_t *uptime)
+{
+       FILE *fp;
+       char buf[sizeof(long)*3 * 2 + 4]; /* enough for long.long */
+       unsigned long uptime_sec, decimal;
+
+       fp = fopen_for_read(PROCFS_UPTIME);
+       if (!fp)
+               return;
+       if (fgets(buf, sizeof(buf), fp)) {
+               if (sscanf(buf, "%lu.%lu", &uptime_sec, &decimal) == 2) {
+                       *uptime = (data_t)uptime_sec * G.hz + decimal * G.hz / 100;
+               }
+       }
+
+       fclose(fp);
+}
+
+static void get_localtime(struct tm *tm)
+{
+       time_t timer;
+       time(&timer);
+       localtime_r(&timer, tm);
+}
+
+static void alarm_handler(int sig UNUSED_PARAM)
+{
+       signal(SIGALRM, alarm_handler);
+       alarm(G.interval);
+}
+
+static void main_loop(void)
+{
+       unsigned current;
+       unsigned cpus;
+
+       /* Read the stats */
+       if (G.cpu_nr > 1) {
+               G.per_cpu_uptime[0] = 0;
+               get_uptime(&G.per_cpu_uptime[0]);
+       }
+
+       get_cpu_statistics(G.st_cpu[0], &G.global_uptime[0], &G.per_cpu_uptime[0]);
+
+       if (display_opt(D_IRQ_SUM))
+               get_irqs_from_stat(G.st_irq[0]);
+
+       if (display_opt(D_IRQ_SUM | D_IRQ_CPU))
+               get_irqs_from_interrupts(PROCFS_INTERRUPTS, G.st_irqcpu,
+                                       G.irqcpu_nr, 0);
+
+       if (display_opt(D_SOFTIRQS))
+               get_irqs_from_interrupts(PROCFS_SOFTIRQS, G.st_softirqcpu,
+                                       G.softirqcpu_nr, 0);
+
+       if (G.interval == 0) {
+               /* Display since boot time */
+               cpus = G.cpu_nr + 1;
+               G.timestamp[1] = G.timestamp[0];
+               memset(G.st_cpu[1], 0, sizeof(G.st_cpu[1][0]) * cpus);
+               memset(G.st_irq[1], 0, sizeof(G.st_irq[1][0]) * cpus);
+               memset(G.st_irqcpu[1], 0, sizeof(G.st_irqcpu[1][0]) * cpus * G.irqcpu_nr);
+               memset(G.st_softirqcpu[1], 0, sizeof(G.st_softirqcpu[1][0]) * cpus * G.softirqcpu_nr);
+
+               write_stats(0);
+
+               /* And we're done */
+               return;
+       }
+
+       /* Set a handler for SIGALRM */
+       alarm_handler(0);
+
+       /* Save the stats we already have. We need them to compute the average */
+       G.timestamp[2] = G.timestamp[0];
+       G.global_uptime[2] = G.global_uptime[0];
+       G.per_cpu_uptime[2] = G.per_cpu_uptime[0];
+       cpus = G.cpu_nr + 1;
+       memcpy(G.st_cpu[2], G.st_cpu[0], sizeof(G.st_cpu[0][0]) * cpus);
+       memcpy(G.st_irq[2], G.st_irq[0], sizeof(G.st_irq[0][0]) * cpus);
+       memcpy(G.st_irqcpu[2], G.st_irqcpu[0], sizeof(G.st_irqcpu[0][0]) * cpus * G.irqcpu_nr);
+       if (display_opt(D_SOFTIRQS)) {
+               memcpy(G.st_softirqcpu[2], G.st_softirqcpu[0],
+                       sizeof(G.st_softirqcpu[0][0]) * cpus * G.softirqcpu_nr);
+       }
+
+       current = 1;
+       while (1) {
+               /* Suspend until a signal is received */
+               pause();
+
+               /* Set structures to 0 to distinguish off/online CPUs */
+               memset(&G.st_cpu[current][/*cpu:*/ 1], 0, sizeof(G.st_cpu[0][0]) * G.cpu_nr);
+
+               get_localtime(&G.timestamp[current]);
+
+               /* Read stats */
+               if (G.cpu_nr > 1) {
+                       G.per_cpu_uptime[current] = 0;
+                       get_uptime(&G.per_cpu_uptime[current]);
+               }
+               get_cpu_statistics(G.st_cpu[current], &G.global_uptime[current], &G.per_cpu_uptime[current]);
+
+               if (display_opt(D_IRQ_SUM))
+                       get_irqs_from_stat(G.st_irq[current]);
+
+               if (display_opt(D_IRQ_SUM | D_IRQ_CPU)) {
+                       int cpu;
+                       for (cpu = 1; cpu <= G.cpu_nr; cpu++) {
+                               G.st_irq[current][cpu].irq_nr = 0;
+                       }
+                       /* accumulates .irq_nr */
+                       get_irqs_from_interrupts(PROCFS_INTERRUPTS, G.st_irqcpu,
+                                       G.irqcpu_nr, current);
+               }
+
+               if (display_opt(D_SOFTIRQS))
+                       get_irqs_from_interrupts(PROCFS_SOFTIRQS,
+                                       G.st_softirqcpu,
+                                       G.softirqcpu_nr, current);
+
+               write_stats(current);
+
+               if (G.count > 0) {
+                       if (--G.count == 0)
+                               break;
+               }
+
+               current ^= 1;
+       }
+
+       /* Print average statistics */
+       write_stats_avg(current);
+}
+
+/* Initialization */
+
+/* Get number of clock ticks per sec */
+static ALWAYS_INLINE unsigned get_hz(void)
+{
+       return sysconf(_SC_CLK_TCK);
+}
+
+static void alloc_struct(int cpus)
+{
+       int i;
+       for (i = 0; i < 3; i++) {
+               G.st_cpu[i] = xzalloc(sizeof(G.st_cpu[i][0]) * cpus);
+               G.st_irq[i] = xzalloc(sizeof(G.st_irq[i][0]) * cpus);
+               G.st_irqcpu[i] = xzalloc(sizeof(G.st_irqcpu[i][0]) * cpus * G.irqcpu_nr);
+               G.st_softirqcpu[i] = xzalloc(sizeof(G.st_softirqcpu[i][0]) * cpus * G.softirqcpu_nr);
+       }
+       G.cpu_bitmap_len = (cpus >> 3) + 1;
+       G.cpu_bitmap = xzalloc(G.cpu_bitmap_len);
+}
+
+static void print_header(struct tm *t)
+{
+       char cur_date[16];
+       struct utsname uts;
+
+       /* Get system name, release number and hostname */
+       uname(&uts);
+
+       strftime(cur_date, sizeof(cur_date), "%x", t);
+
+       printf("%s %s (%s)\t%s\t_%s_\t(%u CPU)\n",
+               uts.sysname, uts.release, uts.nodename, cur_date, uts.machine, G.cpu_nr);
+}
+
+/*
+ * Get number of interrupts available per processor
+ */
+static int get_irqcpu_nr(const char *f, int max_irqs)
+{
+       FILE *fp;
+       char *line;
+       unsigned linelen;
+       unsigned irq;
+
+       fp = fopen_for_read(f);
+       if (!fp)  /* No interrupts file */
+               return 0;
+
+       linelen = INTERRUPTS_LINE + 16 * G.cpu_nr;
+       line = xmalloc(linelen);
+
+       irq = 0;
+       while (fgets(line, linelen, fp)
+        && irq < max_irqs
+       ) {
+               int p = strcspn(line, ":");
+               if ((p > 0) && (p < 16))
+                       irq++;
+       }
+
+       fclose(fp);
+       free(line);
+
+       return irq;
+}
+
+//usage:#define mpstat_trivial_usage
+//usage:       "[-A] [-I SUM|CPU|ALL|SCPU] [-u] [-P num|ALL] [INTERVAL [COUNT]]"
+//usage:#define mpstat_full_usage "\n\n"
+//usage:       "Per-processor statistics\n"
+//usage:     "\n       -A                      Same as -I ALL -u -P ALL"
+//usage:     "\n       -I SUM|CPU|ALL|SCPU     Report interrupt statistics"
+//usage:     "\n       -P num|ALL              Processor to monitor"
+//usage:     "\n       -u                      Report CPU utilization"
+
+int mpstat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mpstat_main(int UNUSED_PARAM argc, char **argv)
+{
+       char *opt_irq_fmt;
+       char *opt_set_cpu;
+       int i, opt;
+       enum {
+               OPT_ALL    = 1 << 0, /* -A */
+               OPT_INTS   = 1 << 1, /* -I */
+               OPT_SETCPU = 1 << 2, /* -P */
+               OPT_UTIL   = 1 << 3, /* -u */
+       };
+
+       /* Dont buffer data if redirected to a pipe */
+       setbuf(stdout, NULL);
+
+       INIT_G();
+
+       G.interval = -1;
+
+       /* Get number of processors */
+       G.cpu_nr = get_cpu_count();
+
+       /* Get number of clock ticks per sec */
+       G.hz = get_hz();
+
+       /* Calculate number of interrupts per processor */
+       G.irqcpu_nr = get_irqcpu_nr(PROCFS_INTERRUPTS, NR_IRQS) + NR_IRQCPU_PREALLOC;
+
+       /* Calculate number of soft interrupts per processor */
+       G.softirqcpu_nr = get_irqcpu_nr(PROCFS_SOFTIRQS, NR_IRQS) + NR_IRQCPU_PREALLOC;
+
+       /* Allocate space for structures. + 1 for global structure. */
+       alloc_struct(G.cpu_nr + 1);
+
+       /* Parse and process arguments */
+       opt = getopt32(argv, "AI:P:u", &opt_irq_fmt, &opt_set_cpu);
+       argv += optind;
+
+       if (*argv) {
+               /* Get interval */
+               G.interval = xatoi_positive(*argv);
+               G.count = -1;
+               argv++;
+               if (*argv) {
+                       /* Get count value */
+                       if (G.interval == 0)
+                               bb_show_usage();
+                       G.count = xatoi_positive(*argv);
+                       //if (*++argv)
+                       //      bb_show_usage();
+               }
+       }
+       if (G.interval < 0)
+               G.interval = 0;
+
+       if (opt & OPT_ALL) {
+               G.p_option = 1;
+               G.options |= D_CPU + D_IRQ_SUM + D_IRQ_CPU + D_SOFTIRQS;
+               /* Select every CPU */
+               memset(G.cpu_bitmap, 0xff, G.cpu_bitmap_len);
+       }
+
+       if (opt & OPT_INTS) {
+               static const char v[] = {
+                       D_IRQ_CPU, D_IRQ_SUM, D_SOFTIRQS,
+                       D_IRQ_SUM + D_IRQ_CPU + D_SOFTIRQS
+               };
+               i = index_in_strings("CPU\0SUM\0SCPU\0ALL\0", opt_irq_fmt);
+               if (i == -1)
+                       bb_show_usage();
+               G.options |= v[i];
+       }
+
+       if ((opt & OPT_UTIL) /* -u? */
+        || G.options == 0  /* nothing? (use default then) */
+       ) {
+               G.options |= D_CPU;
+       }
+
+       if (opt & OPT_SETCPU) {
+               char *t;
+               G.p_option = 1;
+
+               for (t = strtok(opt_set_cpu, ","); t; t = strtok(NULL, ",")) {
+                       if (strcmp(t, "ALL") == 0) {
+                               /* Select every CPU */
+                               memset(G.cpu_bitmap, 0xff, G.cpu_bitmap_len);
+                       } else {
+                               /* Get CPU number */
+                               unsigned n = xatoi_positive(t);
+                               if (n >= G.cpu_nr)
+                                       bb_error_msg_and_die("not that many processors");
+                               n++;
+                               G.cpu_bitmap[n >> 3] |= 1 << (n & 7);
+                       }
+               }
+       }
+
+       if (!G.p_option)
+               /* Display global stats */
+               G.cpu_bitmap[0] = 1;
+
+       /* Get time */
+       get_localtime(&G.timestamp[0]);
+
+       /* Display header */
+       print_header(&G.timestamp[0]);
+
+       /* The main loop */
+       main_loop();
+
+       if (ENABLE_FEATURE_CLEAN_UP) {
+               /* Clean up */
+               for (i = 0; i < 3; i++) {
+                       free(G.st_cpu[i]);
+                       free(G.st_irq[i]);
+                       free(G.st_irqcpu[i]);
+                       free(G.st_softirqcpu[i]);
+               }
+               free(G.cpu_bitmap);
+               free(&G);
+       }
+
+       return EXIT_SUCCESS;
+}
index bb1e819..5d5b83b 100644 (file)
@@ -1,9 +1,44 @@
 /*
-** Licensed under the GPL v2, see the file LICENSE in this tarball
-**
-** Based on nanotop.c from floppyfw project
-**
-** Contact me: vda.linux@googlemail.com */
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ *
+ * Based on nanotop.c from floppyfw project
+ *
+ * Contact me: vda.linux@googlemail.com
+ */
+
+//config:config NMETER
+//config:      bool "nmeter"
+//config:      default y
+//config:      help
+//config:        Prints selected system stats continuously, one line per update.
+
+//applet:IF_NMETER(APPLET(nmeter, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_NMETER) += nmeter.o
+
+//usage:#define nmeter_trivial_usage
+//usage:       "[-d MSEC] FORMAT_STRING"
+//usage:#define nmeter_full_usage "\n\n"
+//usage:       "Monitor system in real time"
+//usage:     "\n"
+//usage:     "\n -d MSEC       Milliseconds between updates (default:1000)"
+//usage:     "\n"
+//usage:     "\nFormat specifiers:"
+//usage:     "\n %Nc or %[cN]  CPU. N - bar size (default:10)"
+//usage:     "\n               (displays: S:system U:user N:niced D:iowait I:irq i:softirq)"
+//usage:     "\n %[nINTERFACE] Network INTERFACE"
+//usage:     "\n %m            Allocated memory"
+//usage:     "\n %[mf]         Free memory"
+//usage:     "\n %[mt]         Total memory"
+//usage:     "\n %s            Allocated swap"
+//usage:     "\n %f            Number of used file descriptors"
+//usage:     "\n %Ni           Total/specific IRQ rate"
+//usage:     "\n %x            Context switch rate"
+//usage:     "\n %p            Forks"
+//usage:     "\n %[pn]         # of processes"
+//usage:     "\n %b            Block io"
+//usage:     "\n %Nt           Time (with N decimal points)"
+//usage:     "\n %r            Print <cr> instead of <lf> at EOL"
 
 //TODO:
 // simplify code
@@ -236,33 +271,59 @@ static int rdval_loadavg(const char* p, ullong *vec, ...)
 }
 
 // Parses /proc/diskstats
-//   1  2 3   4         5        6(rd)  7      8     9     10(wr) 11     12 13     14
+//   1  2 3   4     5     6(rd)  7      8     9     10(wr) 11     12 13     14
 //   3  0 hda 51292 14441 841783 926052 25717 79650 843256 3029804 0 148459 3956933
 //   3  1 hda1 0 0 0 0 <- ignore if only 4 fields
+// Linux 3.0 (maybe earlier) started printing full stats for hda1 too.
+// Had to add code which skips such devices.
 static int rdval_diskstats(const char* p, ullong *vec)
 {
-       ullong rd = rd; // for compiler
-       int indexline = 0;
+       char devname[32];
+       unsigned devname_len = 0;
+       int value_idx = 0;
+
        vec[0] = 0;
        vec[1] = 0;
        while (1) {
-               indexline++;
-               while (*p == ' ' || *p == '\t') p++;
-               if (*p == '\0') break;
+               value_idx++;
+               while (*p == ' ' || *p == '\t')
+                       p++;
+               if (*p == '\0')
+                       break;
                if (*p == '\n') {
-                       indexline = 0;
+                       value_idx = 0;
                        p++;
                        continue;
                }
-               if (indexline == 6) {
-                       rd = strtoull(p, NULL, 10);
-               } else if (indexline == 10) {
-                       vec[0] += rd;  // TODO: *sectorsize (don't know how to find out sectorsize)
+               if (value_idx == 3) {
+                       char *end = strchrnul(p, ' ');
+                       /* If this a hda1-like device (same prefix as last one + digit)? */
+                       if (devname_len && strncmp(devname, p, devname_len) == 0 && isdigit(p[devname_len])) {
+                               p = end;
+                               goto skip_line; /* skip entire line */
+                       }
+                       /* It is not. Remember the name for future checks */
+                       devname_len = end - p;
+                       if (devname_len > sizeof(devname)-1)
+                               devname_len = sizeof(devname)-1;
+                       strncpy(devname, p, devname_len);
+                       /* devname[devname_len] = '\0'; - not really needed */
+                       p = end;
+               } else
+               if (value_idx == 6) {
+                       // TODO: *sectorsize (don't know how to find out sectorsize)
+                       vec[0] += strtoull(p, NULL, 10);
+               } else
+               if (value_idx == 10) {
+                       // TODO: *sectorsize (don't know how to find out sectorsize)
                        vec[1] += strtoull(p, NULL, 10);
-                       while (*p != '\n' && *p != '\0') p++;
+ skip_line:
+                       while (*p != '\n' && *p != '\0')
+                               p++;
                        continue;
                }
-               while (*p > ' ') p++; // skip over value
+               while ((unsigned char)(*p) > ' ') // skip over value
+                       p++;
        }
        return 0;
 }
@@ -272,8 +333,7 @@ static void scale(ullong ul)
        char buf[5];
 
        /* see http://en.wikipedia.org/wiki/Tera */
-       smart_ulltoa4(ul, buf, " kmgtpezy");
-       buf[4] = '\0';
+       smart_ulltoa4(ul, buf, " kmgtpezy")[0] = '\0';
        put(buf);
 }
 
@@ -422,7 +482,7 @@ static s_stat* init_int(const char *param)
        if (param[0] == '\0') {
                s->no = 1;
        } else {
-               int n = xatoi_u(param);
+               int n = xatoi_positive(param);
                s->no = n + 2;
        }
        return (s_stat*)s;
@@ -768,6 +828,7 @@ static void FAST_FUNC collect_info(s_stat *s)
 
 typedef s_stat* init_func(const char *param);
 
+// Deprecated %NNNd is to be removed, -d MSEC supersedes it
 static const char options[] ALIGN1 = "ncmsfixptbdr";
 static init_func *const init_functions[] = {
        init_if,
@@ -791,23 +852,28 @@ int nmeter_main(int argc UNUSED_PARAM, char **argv)
        s_stat *first = NULL;
        s_stat *last = NULL;
        s_stat *s;
+       char *opt_d;
        char *cur, *prev;
 
        INIT_G();
 
        xchdir("/proc");
 
-       if (!argv[1])
-               bb_show_usage();
-
        if (open_read_close("version", buf, sizeof(buf)-1) > 0) {
                buf[sizeof(buf)-1] = '\0';
                is26 = (strstr(buf, " 2.4.") == NULL);
        }
 
-       // Can use argv[1] directly, but this will mess up
+       if (getopt32(argv, "d:", &opt_d))
+               init_delay(opt_d);
+       argv += optind;
+
+       if (!argv[0])
+               bb_show_usage();
+
+       // Can use argv[0] directly, but this will mess up
        // parameters as seen by e.g. ps. Making a copy...
-       cur = xstrdup(argv[1]);
+       cur = xstrdup(argv[0]);
        while (1) {
                char *param, *p;
                prev = cur;
index 45de8bc..1c7c7c4 100644 (file)
@@ -4,8 +4,35 @@
  *
  * Copyright (C) 2007 Loic Grenie <loic.grenie@gmail.com>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define pgrep_trivial_usage
+//usage:       "[-flnovx] [-s SID|-P PPID|PATTERN]"
+//usage:#define pgrep_full_usage "\n\n"
+//usage:       "Display process(es) selected by regex PATTERN\n"
+//usage:     "\n       -l      Show command name too"
+//usage:     "\n       -f      Match against entire command line"
+//usage:     "\n       -n      Show the newest process only"
+//usage:     "\n       -o      Show the oldest process only"
+//usage:     "\n       -v      Negate the match"
+//usage:     "\n       -x      Match whole name (not substring)"
+//usage:     "\n       -s      Match session ID (0 for current)"
+//usage:     "\n       -P      Match parent process ID"
+//usage:
+//usage:#define pkill_trivial_usage
+//usage:       "[-l|-SIGNAL] [-fnovx] [-s SID|-P PPID|PATTERN]"
+//usage:#define pkill_full_usage "\n\n"
+//usage:       "Send a signal to process(es) selected by regex PATTERN\n"
+//usage:     "\n       -l      List all signals"
+//usage:     "\n       -f      Match against entire command line"
+//usage:     "\n       -n      Signal the newest process only"
+//usage:     "\n       -o      Signal the oldest process only"
+//usage:     "\n       -v      Negate the match"
+//usage:     "\n       -x      Match whole name (not substring)"
+//usage:     "\n       -s      Match session ID (0 for current)"
+//usage:     "\n       -P      Match parent process ID"
+
 #include "libbb.h"
 #include "xregex.h"
 
@@ -38,9 +65,9 @@ static void act(unsigned pid, char *cmd, int signo)
 {
        if (pgrep) {
                if (option_mask32 & (1 << OPTBIT_L)) /* OPT_LIST */
-                       printf("%d %s\n", pid, cmd);
+                       printf("%u %s\n", pid, cmd);
                else
-                       printf("%d\n", pid);
+                       printf("%u\n", pid);
        } else
                kill(pid, signo);
 }
@@ -101,7 +128,7 @@ int pgrep_main(int argc UNUSED_PARAM, char **argv)
                bb_show_usage();
 
        if (argv[0])
-               xregcomp(&re_buffer, argv[0], 0);
+               xregcomp(&re_buffer, argv[0], OPT_ANCHOR ? REG_EXTENDED : (REG_EXTENDED|REG_NOSUB));
 
        matched_pid = 0;
        cmd_last = NULL;
index bf5e784..6d7b591 100644 (file)
@@ -4,9 +4,37 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#if (ENABLE_FEATURE_PIDOF_SINGLE || ENABLE_FEATURE_PIDOF_OMIT)
+//usage:#define pidof_trivial_usage
+//usage:       "[OPTIONS] [NAME]..."
+//usage:#define USAGE_PIDOF "\n"
+//usage:#else
+//usage:#define pidof_trivial_usage
+//usage:       "[NAME]..."
+//usage:#define USAGE_PIDOF /* none */
+//usage:#endif
+//usage:#define pidof_full_usage "\n\n"
+//usage:       "List PIDs of all processes with names that match NAMEs"
+//usage:       USAGE_PIDOF
+//usage:       IF_FEATURE_PIDOF_SINGLE(
+//usage:     "\n       -s      Show only one PID"
+//usage:       )
+//usage:       IF_FEATURE_PIDOF_OMIT(
+//usage:     "\n       -o PID  Omit given pid"
+//usage:     "\n               Use %PPID to omit pid of pidof's parent"
+//usage:       )
+//usage:
+//usage:#define pidof_example_usage
+//usage:       "$ pidof init\n"
+//usage:       "1\n"
+//usage:       IF_FEATURE_PIDOF_OMIT(
+//usage:       "$ pidof /bin/sh\n20351 5973 5950\n")
+//usage:       IF_FEATURE_PIDOF_OMIT(
+//usage:       "$ pidof /bin/sh -o %PPID\n20351 5950")
+
 #include "libbb.h"
 
 enum {
diff --git a/procps/pmap.c b/procps/pmap.c
new file mode 100644 (file)
index 0000000..fd995a5
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * pmap implementation for busybox
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ * Written by Alexander Shishkin <virtuoso@slind.org>
+ *
+ * Licensed under GPLv2 or later, see the LICENSE file in this source tree
+ * for details.
+ */
+
+//config:config PMAP
+//config:       bool "pmap"
+//config:       default y
+//config:       help
+//config:         Display processes' memory mappings.
+
+//applet:IF_PMAP(APPLET(pmap, BB_DIR_USR_BIN, BB_SUID_DROP))
+//kbuild:lib-$(CONFIG_PMAP) += pmap.o
+
+//usage:#define pmap_trivial_usage
+//usage:       "[-xq] PID"
+//usage:#define pmap_full_usage "\n\n"
+//usage:       "Display detailed process memory usage"
+//usage:     "\n"
+//usage:     "\n       -x      Show details"
+//usage:     "\n       -q      Quiet"
+
+#include "libbb.h"
+
+#if ULONG_MAX == 0xffffffff
+# define TABS "\t"
+# define AFMT "8"
+# define DASHES ""
+#else
+# define TABS "\t\t"
+# define AFMT "16"
+# define DASHES "--------"
+#endif
+
+enum {
+       OPT_x = 1 << 0,
+       OPT_q = 1 << 1,
+};
+
+static void print_smaprec(struct smaprec *currec, void *data)
+{
+       unsigned opt = (uintptr_t)data;
+
+       printf("%0" AFMT "lx ", currec->smap_start);
+
+       if (opt & OPT_x)
+               printf("%7lu %7lu %7lu %7lu ",
+                       currec->smap_size,
+                       currec->smap_pss,
+                       currec->private_dirty,
+                       currec->smap_swap);
+       else
+               printf("%7luK", currec->smap_size);
+
+       printf(" %.4s  %s\n", currec->smap_mode, currec->smap_name);
+}
+
+static int procps_get_maps(pid_t pid, unsigned opt)
+{
+       struct smaprec total;
+       int ret;
+       char buf[256];
+
+       read_cmdline(buf, sizeof(buf), pid, "no such process");
+       printf("%u: %s\n", (int)pid, buf);
+
+       if (!(opt & OPT_q) && (opt & OPT_x))
+               puts("Address" TABS "  Kbytes     PSS   Dirty    Swap  Mode  Mapping");
+
+       memset(&total, 0, sizeof(total));
+
+       ret = procps_read_smaps(pid, &total, print_smaprec, (void*)(uintptr_t)opt);
+       if (ret)
+               return ret;
+
+       if (!(opt & OPT_q)) {
+               if (opt & OPT_x)
+                       printf("--------" DASHES "  ------  ------  ------  ------\n"
+                               "total" TABS " %7lu %7lu %7lu %7lu\n",
+                               total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap);
+               else
+                       printf("mapped: %luK\n", total.smap_size);
+       }
+
+       return 0;
+}
+
+int pmap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pmap_main(int argc UNUSED_PARAM, char **argv)
+{
+       unsigned opts;
+       int ret;
+
+       opts = getopt32(argv, "xq");
+       argv += optind;
+
+       ret = 0;
+       while (*argv) {
+               pid_t pid = xatoi_positive(*argv++);
+               /* GNU pmap returns 42 if any of the pids failed */
+               if (procps_get_maps(pid, opts) != 0)
+                       ret = 42;
+       }
+
+       return ret;
+}
diff --git a/procps/powertop.c b/procps/powertop.c
new file mode 100644 (file)
index 0000000..e3c29d1
--- /dev/null
@@ -0,0 +1,856 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * A mini 'powertop' utility:
+ *   Analyze power consumption on Intel-based laptops.
+ * Based on powertop 1.11.
+ *
+ * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//applet:IF_POWERTOP(APPLET(powertop, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_POWERTOP) += powertop.o
+
+//config:config POWERTOP
+//config:      bool "powertop"
+//config:      default y
+//config:      help
+//config:        Analyze power consumption on Intel-based laptops
+
+// XXX This should be configurable
+#define ENABLE_FEATURE_POWERTOP_PROCIRQ 1
+
+#include "libbb.h"
+
+
+//#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
+#define debug(fmt, ...) ((void)0)
+
+
+#define BLOATY_HPET_IRQ_NUM_DETECTION 0
+#define MAX_CSTATE_COUNT   8
+#define IRQCOUNT           40
+
+
+#define DEFAULT_SLEEP      10
+#define DEFAULT_SLEEP_STR "10"
+
+/* Frequency of the ACPI timer */
+#define FREQ_ACPI          3579.545
+#define FREQ_ACPI_1000     3579545
+
+/* Max filename length of entry in /sys/devices subsystem */
+#define BIG_SYSNAME_LEN    16
+
+typedef unsigned long long ullong;
+
+struct line {
+       char *string;
+       int count;
+       /*int disk_count;*/
+};
+
+#if ENABLE_FEATURE_POWERTOP_PROCIRQ
+struct irqdata {
+       smallint active;
+       int number;
+       ullong count;
+       char irq_desc[32];
+};
+#endif
+
+struct globals {
+       struct line *lines; /* the most often used member */
+       int lines_cnt;
+       int lines_cumulative_count;
+       int maxcstate;
+       unsigned total_cpus;
+       smallint cant_enable_timer_stats;
+#if ENABLE_FEATURE_POWERTOP_PROCIRQ
+# if BLOATY_HPET_IRQ_NUM_DETECTION
+       smallint scanned_timer_list;
+       int percpu_hpet_start;
+       int percpu_hpet_end;
+# endif
+       int interrupt_0;
+       int total_interrupt;
+       struct irqdata interrupts[IRQCOUNT];
+#endif
+       ullong start_usage[MAX_CSTATE_COUNT];
+       ullong last_usage[MAX_CSTATE_COUNT];
+       ullong start_duration[MAX_CSTATE_COUNT];
+       ullong last_duration[MAX_CSTATE_COUNT];
+#if ENABLE_FEATURE_USE_TERMIOS
+       struct termios init_settings;
+#endif
+};
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+       SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+#if ENABLE_FEATURE_USE_TERMIOS
+static void reset_term(void)
+{
+       tcsetattr_stdin_TCSANOW(&G.init_settings);
+}
+
+static void sig_handler(int signo UNUSED_PARAM)
+{
+       reset_term();
+       _exit(EXIT_FAILURE);
+}
+#endif
+
+static int write_str_to_file(const char *fname, const char *str)
+{
+       FILE *fp = fopen_for_write(fname);
+       if (!fp)
+               return 1;
+       fputs(str, fp);
+       fclose(fp);
+       return 0;
+}
+
+/* Make it more readable */
+#define start_timer() write_str_to_file("/proc/timer_stats", "1\n")
+#define stop_timer()  write_str_to_file("/proc/timer_stats", "0\n")
+
+static NOINLINE void clear_lines(void)
+{
+       int i;
+       if (G.lines) {
+               for (i = 0; i < G.lines_cnt; i++)
+                       free(G.lines[i].string);
+               free(G.lines);
+               G.lines_cnt = 0;
+               G.lines = NULL;
+       }
+}
+
+static void update_lines_cumulative_count(void)
+{
+       int i;
+       for (i = 0; i < G.lines_cnt; i++)
+               G.lines_cumulative_count += G.lines[i].count;
+}
+
+static int line_compare(const void *p1, const void *p2)
+{
+       const struct line *a = p1;
+       const struct line *b = p2;
+       return (b->count /*+ 50 * b->disk_count*/) - (a->count /*+ 50 * a->disk_count*/);
+}
+
+static void sort_lines(void)
+{
+       qsort(G.lines, G.lines_cnt, sizeof(G.lines[0]), line_compare);
+}
+
+/* Save C-state usage and duration. Also update maxcstate. */
+static void read_cstate_counts(ullong *usage, ullong *duration)
+{
+       DIR *dir;
+       struct dirent *d;
+
+       dir = opendir("/proc/acpi/processor");
+       if (!dir)
+               return;
+
+       while ((d = readdir(dir)) != NULL) {
+               FILE *fp;
+               char buf[192];
+               int level;
+               int len;
+
+               len = strlen(d->d_name); /* "CPUnn" */
+               if (len < 3 || len > BIG_SYSNAME_LEN)
+                       continue;
+
+               sprintf(buf, "%s/%s/power", "/proc/acpi/processor", d->d_name);
+               fp = fopen_for_read(buf);
+               if (!fp)
+                       continue;
+
+// Example file contents:
+// active state:            C0
+// max_cstate:              C8
+// maximum allowed latency: 2000000000 usec
+// states:
+//     C1:                  type[C1] promotion[--] demotion[--] latency[001] usage[00006173] duration[00000000000000000000]
+//     C2:                  type[C2] promotion[--] demotion[--] latency[001] usage[00085191] duration[00000000000083024907]
+//     C3:                  type[C3] promotion[--] demotion[--] latency[017] usage[01017622] duration[00000000017921327182]
+               level = 0;
+               while (fgets(buf, sizeof(buf), fp)) {
+                       char *p = strstr(buf, "age[");
+                       if (!p)
+                               continue;
+                       p += 4;
+                       usage[level] += bb_strtoull(p, NULL, 10) + 1;
+                       p = strstr(buf, "ation[");
+                       if (!p)
+                               continue;
+                       p += 6;
+                       duration[level] += bb_strtoull(p, NULL, 10);
+
+                       if (level >= MAX_CSTATE_COUNT-1)
+                               break;
+                       level++;
+                       if (level > G.maxcstate)  /* update maxcstate */
+                               G.maxcstate = level;
+               }
+               fclose(fp);
+       }
+       closedir(dir);
+}
+
+/* Add line and/or update count */
+static void save_line(const char *string, int count)
+{
+       int i;
+       for (i = 0; i < G.lines_cnt; i++) {
+               if (strcmp(string, G.lines[i].string) == 0) {
+                       /* It's already there, only update count */
+                       G.lines[i].count += count;
+                       return;
+               }
+       }
+
+       /* Add new line */
+       G.lines = xrealloc_vector(G.lines, 4, G.lines_cnt);
+       G.lines[G.lines_cnt].string = xstrdup(string);
+       G.lines[G.lines_cnt].count = count;
+       /*G.lines[G.lines_cnt].disk_count = 0;*/
+       G.lines_cnt++;
+}
+
+#if ENABLE_FEATURE_POWERTOP_PROCIRQ
+static int is_hpet_irq(const char *name)
+{
+       char *p;
+# if BLOATY_HPET_IRQ_NUM_DETECTION
+       long hpet_chan;
+
+       /* Learn the range of existing hpet timers. This is done once */
+       if (!G.scanned_timer_list) {
+               FILE *fp;
+               char buf[80];
+
+               G.scanned_timer_list = true;
+               fp = fopen_for_read("/proc/timer_list");
+               if (!fp)
+                       return 0;
+
+               while (fgets(buf, sizeof(buf), fp)) {
+                       p = strstr(buf, "Clock Event Device: hpet");
+                       if (!p)
+                               continue;
+                       p += sizeof("Clock Event Device: hpet")-1;
+                       if (!isdigit(*p))
+                               continue;
+                       hpet_chan = xatoi_positive(p);
+                       if (hpet_chan < G.percpu_hpet_start)
+                               G.percpu_hpet_start = hpet_chan;
+                       if (hpet_chan > G.percpu_hpet_end)
+                               G.percpu_hpet_end = hpet_chan;
+               }
+               fclose(fp);
+       }
+# endif
+//TODO: optimize
+       p = strstr(name, "hpet");
+       if (!p)
+               return 0;
+       p += 4;
+       if (!isdigit(*p))
+               return 0;
+# if BLOATY_HPET_IRQ_NUM_DETECTION
+       hpet_chan = xatoi_positive(p);
+       if (hpet_chan < G.percpu_hpet_start || hpet_chan > G.percpu_hpet_end)
+               return 0;
+# endif
+       return 1;
+}
+
+/* Save new IRQ count, return delta from old one */
+static int save_irq_count(int irq, ullong count)
+{
+       int unused = IRQCOUNT;
+       int i;
+       for (i = 0; i < IRQCOUNT; i++) {
+               if (G.interrupts[i].active && G.interrupts[i].number == irq) {
+                       ullong old = G.interrupts[i].count;
+                       G.interrupts[i].count = count;
+                       return count - old;
+               }
+               if (!G.interrupts[i].active && unused > i)
+                       unused = i;
+       }
+       if (unused < IRQCOUNT) {
+               G.interrupts[unused].active = 1;
+               G.interrupts[unused].count = count;
+               G.interrupts[unused].number = irq;
+       }
+       return count;
+}
+
+/* Read /proc/interrupts, save IRQ counts and IRQ description */
+static void process_irq_counts(void)
+{
+       FILE *fp;
+       char buf[128];
+
+       /* Reset values */
+       G.interrupt_0 = 0;
+       G.total_interrupt = 0;
+
+       fp = xfopen_for_read("/proc/interrupts");
+       while (fgets(buf, sizeof(buf), fp)) {
+               char irq_desc[sizeof("   <kernel IPI> : ") + sizeof(buf)];
+               char *p;
+               const char *name;
+               int nr;
+               ullong count;
+               ullong delta;
+
+               p = strchr(buf, ':');
+               if (!p)
+                       continue;
+               /*  0:  143646045  153901007   IO-APIC-edge      timer
+                *   ^
+                */
+               *p = '\0';
+               /* Deal with non-maskable interrupts -- make up fake numbers */
+               nr = index_in_strings("NMI\0RES\0CAL\0TLB\0TRM\0THR\0SPU\0", buf);
+               if (nr >= 0) {
+                       nr += 20000;
+               } else {
+                       /* bb_strtou doesn't eat leading spaces, using strtoul */
+                       errno = 0;
+                       nr = strtoul(buf, NULL, 10);
+                       if (errno)
+                               continue;
+               }
+               p++;
+               /*  0:  143646045  153901007   IO-APIC-edge      timer
+                *    ^
+                */
+               /* Sum counts for this IRQ */
+               count = 0;
+               while (1) {
+                       char *tmp;
+                       p = skip_whitespace(p);
+                       if (!isdigit(*p))
+                               break;
+                       count += bb_strtoull(p, &tmp, 10);
+                       p = tmp;
+               }
+               /*   0:  143646045  153901007   IO-APIC-edge      timer
+                * NMI:          1          2   Non-maskable interrupts
+                *                              ^
+                */
+               if (nr < 20000) {
+                       /* Skip to the interrupt name, e.g. 'timer' */
+                       p = strchr(p, ' ');
+                       if (!p)
+                               continue;
+                       p = skip_whitespace(p);
+               }
+
+               name = p;
+               strchrnul(name, '\n')[0] = '\0';
+               /* Save description of the interrupt */
+               if (nr >= 20000)
+                       sprintf(irq_desc, "   <kernel IPI> : %s", name);
+               else
+                       sprintf(irq_desc, "    <interrupt> : %s", name);
+
+               delta = save_irq_count(nr, count);
+
+               /* Skip per CPU timer interrupts */
+               if (is_hpet_irq(name))
+                       continue;
+
+               if (nr != 0 && delta != 0)
+                       save_line(irq_desc, delta);
+
+               if (nr == 0)
+                       G.interrupt_0 = delta;
+               else
+                       G.total_interrupt += delta;
+       }
+
+       fclose(fp);
+}
+#else /* !ENABLE_FEATURE_POWERTOP_PROCIRQ */
+# define process_irq_counts()  ((void)0)
+#endif
+
+static NOINLINE int process_timer_stats(void)
+{
+       char buf[128];
+       char line[15 + 3 + 128];
+       int n;
+       FILE *fp;
+
+       buf[0] = '\0';
+
+       n = 0;
+       fp = NULL;
+       if (!G.cant_enable_timer_stats)
+               fp = fopen_for_read("/proc/timer_stats");
+       if (fp) {
+// Example file contents:
+// Timer Stats Version: v0.2
+// Sample period: 1.329 s
+//    76,     0 swapper          hrtimer_start_range_ns (tick_sched_timer)
+//    88,     0 swapper          hrtimer_start_range_ns (tick_sched_timer)
+//    24,  3787 firefox          hrtimer_start_range_ns (hrtimer_wakeup)
+//   46D,  1136 kondemand/1      do_dbs_timer (delayed_work_timer_fn)
+// ...
+//     1,  1656 Xorg             hrtimer_start_range_ns (hrtimer_wakeup)
+//     1,  2159 udisks-daemon    hrtimer_start_range_ns (hrtimer_wakeup)
+// 331 total events, 249.059 events/sec
+               while (fgets(buf, sizeof(buf), fp)) {
+                       const char *count, *process, *func;
+                       char *p;
+                       int idx;
+                       unsigned cnt;
+
+                       count = skip_whitespace(buf);
+                       p = strchr(count, ',');
+                       if (!p)
+                               continue;
+                       *p++ = '\0';
+                       cnt = bb_strtou(count, NULL, 10);
+                       if (strcmp(skip_non_whitespace(count), " total events") == 0) {
+#if ENABLE_FEATURE_POWERTOP_PROCIRQ
+                               n = cnt / G.total_cpus;
+                               if (n > 0 && n < G.interrupt_0) {
+                                       sprintf(line, "    <interrupt> : %s", "extra timer interrupt");
+                                       save_line(line, G.interrupt_0 - n);
+                               }
+#endif
+                               break;
+                       }
+                       if (strchr(count, 'D'))
+                               continue; /* deferred */
+                       p = skip_whitespace(p); /* points to pid now */
+                       process = NULL;
+ get_func_name:
+                       p = strchr(p, ' ');
+                       if (!p)
+                               continue;
+                       *p++ = '\0';
+                       p = skip_whitespace(p);
+                       if (process == NULL) {
+                               process = p;
+                               goto get_func_name;
+                       }
+                       func = p;
+
+                       //if (strcmp(process, "swapper") == 0
+                       // && strcmp(func, "hrtimer_start_range_ns (tick_sched_timer)\n") == 0
+                       //) {
+                       //      process = "[kernel scheduler]";
+                       //      func = "Load balancing tick";
+                       //}
+
+                       if (strncmp(func, "tick_nohz_", 10) == 0)
+                               continue;
+                       if (strncmp(func, "tick_setup_sched_timer", 20) == 0)
+                               continue;
+                       //if (strcmp(process, "powertop") == 0)
+                       //      continue;
+
+                       idx = index_in_strings("insmod\0modprobe\0swapper\0", process);
+                       if (idx != -1) {
+                               process = idx < 2 ? "[kernel module]" : "<kernel core>";
+                       }
+
+                       strchrnul(p, '\n')[0] = '\0';
+
+                       // 46D\01136\0kondemand/1\0do_dbs_timer (delayed_work_timer_fn)
+                       // ^          ^            ^
+                       // count      process      func
+
+                       //if (strchr(process, '['))
+                               sprintf(line, "%15.15s : %s", process, func);
+                       //else
+                       //      sprintf(line, "%s", process);
+                       save_line(line, cnt);
+               }
+               fclose(fp);
+       }
+
+       return n;
+}
+
+#ifdef __i386__
+/*
+ * Get information about CPU using CPUID opcode.
+ */
+static void cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
+                               unsigned int *edx)
+{
+       /* EAX value specifies what information to return */
+       __asm__(
+               "       pushl %%ebx\n"     /* Save EBX */
+               "       cpuid\n"
+               "       movl %%ebx, %1\n"  /* Save content of EBX */
+               "       popl %%ebx\n"      /* Restore EBX */
+               : "=a"(*eax), /* Output */
+                 "=r"(*ebx),
+                 "=c"(*ecx),
+                 "=d"(*edx)
+               : "0"(*eax),  /* Input */
+                 "1"(*ebx),
+                 "2"(*ecx),
+                 "3"(*edx)
+               /* No clobbered registers */
+       );
+}
+#endif
+
+#ifdef __i386__
+static NOINLINE void print_intel_cstates(void)
+{
+       int bios_table[8] = { 0 };
+       int nbios = 0;
+       DIR *cpudir;
+       struct dirent *d;
+       int i;
+       unsigned eax, ebx, ecx, edx;
+
+       cpudir = opendir("/sys/devices/system/cpu");
+       if (!cpudir)
+               return;
+
+       /* Loop over cpuN entries */
+       while ((d = readdir(cpudir)) != NULL) {
+               DIR *dir;
+               int len;
+               char fname[sizeof("/sys/devices/system/cpu//cpuidle//desc") + 2*BIG_SYSNAME_LEN];
+
+               len = strlen(d->d_name);
+               if (len < 3 || len > BIG_SYSNAME_LEN)
+                       continue;
+
+               if (!isdigit(d->d_name[3]))
+                       continue;
+
+               len = sprintf(fname, "%s/%s/cpuidle", "/sys/devices/system/cpu", d->d_name);
+               dir = opendir(fname);
+               if (!dir)
+                       continue;
+
+               /*
+                * Every C-state has its own stateN directory, that
+                * contains a 'time' and a 'usage' file.
+                */
+               while ((d = readdir(dir)) != NULL) {
+                       FILE *fp;
+                       char buf[64];
+                       int n;
+
+                       n = strlen(d->d_name);
+                       if (n < 3 || n > BIG_SYSNAME_LEN)
+                               continue;
+
+                       sprintf(fname + len, "/%s/desc", d->d_name);
+                       fp = fopen_for_read(fname);
+                       if (fp) {
+                               char *p = fgets(buf, sizeof(buf), fp);
+                               fclose(fp);
+                               if (!p)
+                                       break;
+                               p = strstr(p, "MWAIT ");
+                               if (p) {
+                                       int pos;
+                                       p += sizeof("MWAIT ") - 1;
+                                       pos = (bb_strtoull(p, NULL, 16) >> 4) + 1;
+                                       if (pos >= ARRAY_SIZE(bios_table))
+                                               continue;
+                                       bios_table[pos]++;
+                                       nbios++;
+                               }
+                       }
+               }
+               closedir(dir);
+       }
+       closedir(cpudir);
+
+       if (!nbios)
+               return;
+
+       eax = 5;
+       ebx = ecx = edx = 0;
+       cpuid(&eax, &ebx, &ecx, &edx);
+       if (!edx || !(ecx & 1))
+               return;
+
+       printf("Your CPU supports the following C-states: ");
+       i = 0;
+       while (edx) {
+               if (edx & 7)
+                       printf("C%u ", i);
+               edx >>= 4;
+               i++;
+       }
+       bb_putchar('\n');
+
+       /* Print BIOS C-States */
+       printf("Your BIOS reports the following C-states: ");
+       for (i = 0; i < ARRAY_SIZE(bios_table); i++)
+               if (bios_table[i])
+                       printf("C%u ", i);
+
+       bb_putchar('\n');
+}
+#else
+# define print_intel_cstates() ((void)0)
+#endif
+
+static void show_timerstats(void)
+{
+       unsigned lines;
+
+       /* Get terminal height */
+       get_terminal_width_height(STDOUT_FILENO, NULL, &lines);
+
+       /* We don't have whole terminal just for timerstats */
+       lines -= 12;
+
+       if (!G.cant_enable_timer_stats) {
+               int i, n = 0;
+               char strbuf6[6];
+
+               puts("\nTop causes for wakeups:");
+               for (i = 0; i < G.lines_cnt; i++) {
+                       if ((G.lines[i].count > 0 /*|| G.lines[i].disk_count > 0*/)
+                        && n++ < lines
+                       ) {
+                               /* NB: upstream powertop prints "(wakeups/sec)",
+                                * we print just "(wakeup counts)".
+                                */
+                               /*char c = ' ';
+                               if (G.lines[i].disk_count)
+                                       c = 'D';*/
+                               smart_ulltoa5(G.lines[i].count, strbuf6, " KMGTPEZY")[0] = '\0';
+                               printf(/*" %5.1f%% (%s)%c  %s\n"*/
+                                       " %5.1f%% (%s)   %s\n",
+                                       G.lines[i].count * 100.0 / G.lines_cumulative_count,
+                                       strbuf6, /*c,*/
+                                       G.lines[i].string);
+                       }
+               }
+       } else {
+               bb_putchar('\n');
+               bb_error_msg("no stats available; run as root or"
+                               " enable the timer_stats module");
+       }
+}
+
+// Example display from powertop version 1.11
+// Cn                Avg residency       P-states (frequencies)
+// C0 (cpu running)        ( 0.5%)         2.00 Ghz     0.0%
+// polling           0.0ms ( 0.0%)         1.67 Ghz     0.0%
+// C1 mwait          0.0ms ( 0.0%)         1333 Mhz     0.1%
+// C2 mwait          0.1ms ( 0.1%)         1000 Mhz    99.9%
+// C3 mwait         12.1ms (99.4%)
+//
+// Wakeups-from-idle per second : 93.6     interval: 15.0s
+// no ACPI power usage estimate available
+//
+// Top causes for wakeups:
+//   32.4% ( 26.7)       <interrupt> : extra timer interrupt
+//   29.0% ( 23.9)     <kernel core> : hrtimer_start_range_ns (tick_sched_timer)
+//    9.0% (  7.5)     <kernel core> : hrtimer_start (tick_sched_timer)
+//    6.5% (  5.3)       <interrupt> : ata_piix
+//    5.0% (  4.1)             inetd : hrtimer_start_range_ns (hrtimer_wakeup)
+
+//usage:#define powertop_trivial_usage
+//usage:       ""
+//usage:#define powertop_full_usage "\n\n"
+//usage:       "Analyze power consumption on Intel-based laptops\n"
+
+int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
+{
+       ullong cur_usage[MAX_CSTATE_COUNT];
+       ullong cur_duration[MAX_CSTATE_COUNT];
+       char cstate_lines[MAX_CSTATE_COUNT + 2][64];
+#if ENABLE_FEATURE_USE_TERMIOS
+       struct termios new_settings;
+       struct pollfd pfd[1];
+
+       pfd[0].fd = 0;
+       pfd[0].events = POLLIN;
+#endif
+
+       INIT_G();
+
+#if ENABLE_FEATURE_POWERTOP_PROCIRQ && BLOATY_HPET_IRQ_NUM_DETECTION
+       G.percpu_hpet_start = INT_MAX;
+       G.percpu_hpet_end = INT_MIN;
+#endif
+
+       /* Print warning when we don't have superuser privileges */
+       if (geteuid() != 0)
+               bb_error_msg("run as root to collect enough information");
+
+       /* Get number of CPUs */
+       G.total_cpus = get_cpu_count();
+
+       printf("Collecting data for "DEFAULT_SLEEP_STR" seconds\n");
+
+#if ENABLE_FEATURE_USE_TERMIOS
+       tcgetattr(0, (void *)&G.init_settings);
+       memcpy(&new_settings, &G.init_settings, sizeof(new_settings));
+       /* Turn on unbuffered input, turn off echoing */
+       new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
+       /* So we don't forget to reset term settings */
+       atexit(reset_term);
+       bb_signals(BB_FATAL_SIGS, sig_handler);
+       tcsetattr_stdin_TCSANOW(&new_settings);
+#endif
+
+       /* Collect initial data */
+       process_irq_counts();
+
+       /* Read initial usage and duration */
+       read_cstate_counts(G.start_usage, G.start_duration);
+
+       /* Copy them to "last" */
+       memcpy(G.last_usage, G.start_usage, sizeof(G.last_usage));
+       memcpy(G.last_duration, G.start_duration, sizeof(G.last_duration));
+
+       /* Display C-states */
+       print_intel_cstates();
+
+       G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
+
+       /* The main loop */
+       for (;;) {
+               //double maxsleep = 0.0;
+               ullong totalticks, totalevents;
+               int i;
+
+               G.cant_enable_timer_stats |= start_timer(); /* 1 on error */
+#if !ENABLE_FEATURE_USE_TERMIOS
+               sleep(DEFAULT_SLEEP);
+#else
+               if (safe_poll(pfd, 1, DEFAULT_SLEEP * 1000) > 0) {
+                       unsigned char c;
+                       if (safe_read(STDIN_FILENO, &c, 1) != 1)
+                               break; /* EOF/error */
+                       if (c == G.init_settings.c_cc[VINTR])
+                               break; /* ^C */
+                       if ((c | 0x20) == 'q')
+                               break;
+               }
+#endif
+               G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
+
+               clear_lines();
+               process_irq_counts();
+
+               /* Clear the stats */
+               memset(cur_duration, 0, sizeof(cur_duration));
+               memset(cur_usage, 0, sizeof(cur_usage));
+
+               /* Read them */
+               read_cstate_counts(cur_usage, cur_duration);
+
+               /* Count totalticks and totalevents */
+               totalticks = totalevents = 0;
+               for (i = 0; i < MAX_CSTATE_COUNT; i++) {
+                       if (cur_usage[i] != 0) {
+                               totalticks += cur_duration[i] - G.last_duration[i];
+                               totalevents += cur_usage[i] - G.last_usage[i];
+                       }
+               }
+
+               /* Clear the screen */
+               printf("\033[H\033[J");
+
+               /* Clear C-state lines */
+               memset(&cstate_lines, 0, sizeof(cstate_lines));
+
+               if (totalevents == 0 && G.maxcstate <= 1) {
+                       /* This should not happen */
+                       strcpy(cstate_lines[0], "C-state information is not available\n");
+               } else {
+                       double percentage;
+                       unsigned newticks;
+
+                       newticks = G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000 - totalticks;
+                       /* Handle rounding errors: do not display negative values */
+                       if ((int)newticks < 0)
+                               newticks = 0;
+
+                       sprintf(cstate_lines[0], "Cn\t\t  Avg residency\n");
+                       percentage = newticks * 100.0 / (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000);
+                       sprintf(cstate_lines[1], "C0 (cpu running)        (%4.1f%%)\n", percentage);
+
+                       /* Compute values for individual C-states */
+                       for (i = 0; i < MAX_CSTATE_COUNT; i++) {
+                               if (cur_usage[i] != 0) {
+                                       double slept;
+                                       slept = (cur_duration[i] - G.last_duration[i])
+                                               / (cur_usage[i] - G.last_usage[i] + 0.1) / FREQ_ACPI;
+                                       percentage = (cur_duration[i] - G.last_duration[i]) * 100
+                                               / (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000);
+                                       sprintf(cstate_lines[i + 2], "C%u\t\t%5.1fms (%4.1f%%)\n",
+                                               i + 1, slept, percentage);
+                                       //if (maxsleep < slept)
+                                       //      maxsleep = slept;
+                               }
+                       }
+               }
+
+               for (i = 0; i < MAX_CSTATE_COUNT + 2; i++)
+                       if (cstate_lines[i][0])
+                               fputs(cstate_lines[i], stdout);
+
+               i = process_timer_stats();
+#if ENABLE_FEATURE_POWERTOP_PROCIRQ
+               if (totalevents == 0) {
+                       /* No C-state info available, use timerstats */
+                       totalevents = i * G.total_cpus + G.total_interrupt;
+                       if (i < 0)
+                               totalevents += G.interrupt_0 - i;
+               }
+#endif
+               /* Upstream powertop prints wakeups per sec per CPU,
+                * we print just raw wakeup counts.
+                */
+//TODO: show real seconds (think about manual refresh)
+               printf("\nWakeups-from-idle in %u seconds: %llu\n",
+                       DEFAULT_SLEEP,
+                       totalevents
+               );
+
+               update_lines_cumulative_count();
+               sort_lines();
+               show_timerstats();
+               fflush(stdout);
+
+               /* Clear the stats */
+               memset(cur_duration, 0, sizeof(cur_duration));
+               memset(cur_usage, 0, sizeof(cur_usage));
+
+               /* Get new values */
+               read_cstate_counts(cur_usage, cur_duration);
+
+               /* Save them */
+               memcpy(G.last_usage, cur_usage, sizeof(G.last_usage));
+               memcpy(G.last_duration, cur_duration, sizeof(G.last_duration));
+       } /* for (;;) */
+
+       bb_putchar('\n');
+
+       return EXIT_SUCCESS;
+}
index a3220a9..c65fa01 100644 (file)
  * Fix for SELinux Support:(c)2007 Hiroshi Shinji <shiroshi@my.email.ne.jp>
  *                         (c)2007 Yuichi Nakamura <ynakam@hitachisoft.jp>
  *
- * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#if ENABLE_DESKTOP
+//usage:
+//usage:#define ps_trivial_usage
+//usage:       "[-o COL1,COL2=HEADER]" IF_FEATURE_SHOW_THREADS(" [-T]")
+//usage:#define ps_full_usage "\n\n"
+//usage:       "Show list of processes\n"
+//usage:     "\n       -o COL1,COL2=HEADER     Select columns for display"
+//usage:       IF_FEATURE_SHOW_THREADS(
+//usage:     "\n       -T                      Show threads"
+//usage:       )
+//usage:
+//usage:#else /* !ENABLE_DESKTOP */
+//usage:
+//usage:#if !ENABLE_SELINUX && !ENABLE_FEATURE_PS_WIDE
+//usage:#define USAGE_PS "\nThis version of ps accepts no options"
+//usage:#else
+//usage:#define USAGE_PS ""
+//usage:#endif
+//usage:
+//usage:#define ps_trivial_usage
+//usage:       ""
+//usage:#define ps_full_usage "\n\n"
+//usage:       "Show list of processes\n"
+//usage:       USAGE_PS
+//usage:       IF_SELINUX(
+//usage:     "\n       -Z      Show selinux context"
+//usage:       )
+//usage:       IF_FEATURE_PS_WIDE(
+//usage:     "\n       w       Wide output"
+//usage:       )
+//usage:       IF_FEATURE_PS_LONG(
+//usage:     "\n       l       Long output"
+//usage:       )
+//usage:       IF_FEATURE_SHOW_THREADS(
+//usage:     "\n       T       Show threads"
+//usage:       )
+//usage:
+//usage:#endif /* ENABLE_DESKTOP */
+//usage:
+//usage:#define ps_example_usage
+//usage:       "$ ps\n"
+//usage:       "  PID  Uid      Gid State Command\n"
+//usage:       "    1 root     root     S init\n"
+//usage:       "    2 root     root     S [kflushd]\n"
+//usage:       "    3 root     root     S [kupdate]\n"
+//usage:       "    4 root     root     S [kpiod]\n"
+//usage:       "    5 root     root     S [kswapd]\n"
+//usage:       "  742 andersen andersen S [bash]\n"
+//usage:       "  743 andersen andersen S -bash\n"
+//usage:       "  745 root     root     S [getty]\n"
+//usage:       " 2990 andersen andersen R ps\n"
+
 #include "libbb.h"
+#ifdef __linux__
+# include <sys/sysinfo.h>
+#endif
 
 /* Absolute maximum on output line length */
 enum { MAX_WIDTH = 2*1024 };
 
+#if ENABLE_FEATURE_PS_TIME || ENABLE_FEATURE_PS_LONG
+static unsigned long get_uptime(void)
+{
+#ifdef __linux__
+       struct sysinfo info;
+       if (sysinfo(&info) < 0)
+               return 0;
+       return info.uptime;
+#elif 1
+       unsigned long uptime;
+       char buf[sizeof(uptime)*3 + 2];
+       /* /proc/uptime is "UPTIME_SEC.NN IDLE_SEC.NN\n"
+        * (where IDLE is cumulative over all CPUs)
+        */
+       if (open_read_close("/proc/uptime", buf, sizeof(buf)) <= 0)
+               bb_perror_msg_and_die("can't read '%s'", "/proc/uptime");
+       buf[sizeof(buf)-1] = '\0';
+       sscanf(buf, "%lu", &uptime);
+       return uptime;
+#else
+       struct timespec ts;
+       if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
+               return 0;
+       return ts.tv_sec;
+#endif
+}
+#endif
+
 #if ENABLE_DESKTOP
 
 #include <sys/times.h> /* for times() */
 #ifndef AT_CLKTCK
-#define AT_CLKTCK 17
-#endif
-
-
-#if ENABLE_SELINUX
-#define SELINUX_O_PREFIX "label,"
-#define DEFAULT_O_STR    (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args")
-#else
-#define DEFAULT_O_STR    ("pid,user" IF_FEATURE_PS_TIME(",time") ",args")
+# define AT_CLKTCK 17
 #endif
 
+/* TODO:
+ * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
+ * specifies (for XSI-conformant systems) following default columns
+ * (l and f mark columns shown with -l and -f respectively):
+ * F     l   Flags (octal and additive) associated with the process (??)
+ * S     l   The state of the process
+ * UID   f,l The user ID; the login name is printed with -f
+ * PID       The process ID
+ * PPID  f,l The parent process
+ * C     f,l Processor utilization
+ * PRI   l   The priority of the process; higher numbers mean lower priority
+ * NI    l   Nice value
+ * ADDR  l   The address of the process
+ * SZ    l   The size in blocks of the core image of the process
+ * WCHAN l   The event for which the process is waiting or sleeping
+ * STIME f   Starting time of the process
+ * TTY       The controlling terminal for the process
+ * TIME      The cumulative execution time for the process
+ * CMD       The command name; the full command line is shown with -f
+ */
 typedef struct {
        uint16_t width;
        char name6[6];
@@ -46,9 +141,8 @@ struct globals {
        unsigned terminal_width;
 #if ENABLE_FEATURE_PS_TIME
        unsigned kernel_HZ;
-       unsigned long long seconds_since_boot;
+       unsigned long seconds_since_boot;
 #endif
-       char default_o[sizeof(DEFAULT_O_STR)];
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
 #define out                (G.out               )
@@ -58,17 +152,16 @@ struct globals {
 #define buffer             (G.buffer            )
 #define terminal_width     (G.terminal_width    )
 #define kernel_HZ          (G.kernel_HZ         )
-#define seconds_since_boot (G.seconds_since_boot)
-#define default_o          (G.default_o         )
 #define INIT_G() do { } while (0)
 
 #if ENABLE_FEATURE_PS_TIME
 /* for ELF executables, notes are pushed before environment and args */
-static ptrdiff_t find_elf_note(ptrdiff_t findme)
+static uintptr_t find_elf_note(uintptr_t findme)
 {
-       ptrdiff_t *ep = (ptrdiff_t *) environ;
+       uintptr_t *ep = (uintptr_t *) environ;
 
-       while (*ep++);
+       while (*ep++)
+               continue;
        while (*ep) {
                if (ep[0] == findme) {
                        return ep[1];
@@ -131,9 +224,6 @@ static inline unsigned get_HZ_by_waiting(void)
 
 static unsigned get_kernel_HZ(void)
 {
-       //char buf[64];
-       struct sysinfo info;
-
        if (kernel_HZ)
                return kernel_HZ;
 
@@ -142,12 +232,7 @@ static unsigned get_kernel_HZ(void)
        if (kernel_HZ == (unsigned)-1)
                kernel_HZ = get_HZ_by_waiting();
 
-       //if (open_read_close("/proc/uptime", buf, sizeof(buf)) <= 0)
-       //      bb_perror_msg_and_die("can't read %s", "/proc/uptime");
-       //buf[sizeof(buf)-1] = '\0';
-       ///sscanf(buf, "%llu", &seconds_since_boot);
-       sysinfo(&info);
-       seconds_since_boot = info.uptime;
+       G.seconds_since_boot = get_uptime();
 
        return kernel_HZ;
 }
@@ -184,6 +269,11 @@ static void func_comm(char *buf, int size, const procps_status_t *ps)
        safe_strncpy(buf, ps->comm, size+1);
 }
 
+static void func_state(char *buf, int size, const procps_status_t *ps)
+{
+       safe_strncpy(buf, ps->state, size+1);
+}
+
 static void func_args(char *buf, int size, const procps_status_t *ps)
 {
        read_cmdline(buf, size+1, ps->pid, ps->comm);
@@ -209,8 +299,7 @@ static void put_lu(char *buf, int size, unsigned long u)
        char buf4[5];
 
        /* see http://en.wikipedia.org/wiki/Tera */
-       smart_ulltoa4(u, buf4, " mgtpezy");
-       buf4[4] = '\0';
+       smart_ulltoa4(u, buf4, " mgtpezy")[0] = '\0';
        sprintf(buf, "%.*s", size, buf4);
 }
 
@@ -261,7 +350,7 @@ static void func_etime(char *buf, int size, const procps_status_t *ps)
 
        mm = ps->start_time / get_kernel_HZ();
        /* must be after get_kernel_HZ()! */
-       mm = seconds_since_boot - mm;
+       mm = G.seconds_since_boot - mm;
        ss = mm % 60;
        mm /= 60;
        snprintf(buf, size+1, "%3lu:%02u", mm, ss);
@@ -300,7 +389,7 @@ static void func_pcpu(char *buf, int size, const procps_status_t *ps)
 */
 
 static const ps_out_t out_spec[] = {
-// Mandated by POSIX:
+/* Mandated by http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html: */
        { 8                  , "user"  ,"USER"   ,func_user  ,PSSCAN_UIDGID  },
        { 8                  , "group" ,"GROUP"  ,func_group ,PSSCAN_UIDGID  },
        { 16                 , "comm"  ,"COMMAND",func_comm  ,PSSCAN_COMM    },
@@ -322,7 +411,8 @@ static const ps_out_t out_spec[] = {
 #endif
        { 6                  , "tty"   ,"TT"     ,func_tty   ,PSSCAN_TTY     },
        { 4                  , "vsz"   ,"VSZ"    ,func_vsz   ,PSSCAN_VSZ     },
-// Not mandated by POSIX, but useful:
+/* Not mandated, but useful: */
+       { 4                  , "stat"  ,"STAT"   ,func_state ,PSSCAN_STATE   },
        { 4                  , "rss"   ,"RSS"    ,func_rss   ,PSSCAN_RSS     },
 #if ENABLE_SELINUX
        { 35                 , "label" ,"LABEL"  ,func_label ,PSSCAN_CONTEXT },
@@ -338,24 +428,16 @@ static ps_out_t* new_out_t(void)
 static const ps_out_t* find_out_spec(const char *name)
 {
        unsigned i;
-#if ENABLE_DESKTOP
        char buf[ARRAY_SIZE(out_spec)*7 + 1];
        char *p = buf;
-#endif
 
        for (i = 0; i < ARRAY_SIZE(out_spec); i++) {
                if (strncmp(name, out_spec[i].name6, 6) == 0)
                        return &out_spec[i];
-#if ENABLE_DESKTOP
                p += sprintf(p, "%.6s,", out_spec[i].name6);
-#endif
        }
-#if ENABLE_DESKTOP
        p[-1] = '\0';
        bb_error_msg_and_die("bad -o argument '%s', supported arguments: %s", name, buf);
-#else
-       bb_error_msg_and_die("bad -o argument '%s'");
-#endif
 }
 
 static void parse_o(char* opt)
@@ -467,11 +549,19 @@ static void format_process(const procps_status_t *ps)
        printf("%.*s\n", terminal_width, buffer);
 }
 
+#if ENABLE_SELINUX
+# define SELINUX_O_PREFIX "label,"
+# define DEFAULT_O_STR    (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args")
+#else
+# define DEFAULT_O_STR    ("pid,user" IF_FEATURE_PS_TIME(",time") ",args")
+#endif
+
 int ps_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int ps_main(int argc UNUSED_PARAM, char **argv)
 {
        procps_status_t *p;
        llist_t* opt_o = NULL;
+       char default_o[sizeof(DEFAULT_O_STR)];
        int opt;
        enum {
                OPT_Z = (1 << 0),
@@ -498,7 +588,7 @@ int ps_main(int argc UNUSED_PARAM, char **argv)
        // -o col1,col2,col3=header
        //     Select which columns to display
        /* We allow (and ignore) most of the above. FIXME.
-        * -T is picked for threads (POSIX hasn't it standardized).
+        * -T is picked for threads (POSIX hasn't standardized it).
         * procps v3.2.7 supports -T and shows tids as SPID column,
         * it also supports -L where it shows tids as LWP column.
         */
@@ -509,7 +599,9 @@ int ps_main(int argc UNUSED_PARAM, char **argv)
                        parse_o(llist_pop(&opt_o));
                } while (opt_o);
        } else {
-               /* Below: parse_o() needs char*, NOT const char*... */
+               /* Below: parse_o() needs char*, NOT const char*,
+                * can't pass it constant string. Need to make a copy first.
+                */
 #if ENABLE_SELINUX
                if (!(opt & OPT_Z) || !is_selinux_enabled()) {
                        /* no -Z or no SELinux: do not show LABEL */
@@ -559,15 +651,21 @@ int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
        enum {
                OPT_Z = (1 << 0) * ENABLE_SELINUX,
                OPT_T = (1 << ENABLE_SELINUX) * ENABLE_FEATURE_SHOW_THREADS,
+               OPT_l = (1 << ENABLE_SELINUX) * (1 << ENABLE_FEATURE_SHOW_THREADS) * ENABLE_FEATURE_PS_LONG,
        };
-       int opts = 0;
+#if ENABLE_FEATURE_PS_LONG
+       time_t now = now;
+       unsigned long uptime;
+#endif
        /* If we support any options, parse argv */
-#if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_PS_WIDE
+#if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_PS_WIDE || ENABLE_FEATURE_PS_LONG
+       int opts = 0;
 # if ENABLE_FEATURE_PS_WIDE
        /* -w is a bit complicated */
        int w_count = 0;
        opt_complementary = "-:ww";
-       opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")"w", &w_count);
+       opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")IF_FEATURE_PS_LONG("l")
+                                       "w", &w_count);
        /* if w is given once, GNU ps sets the width to 132,
         * if w is given more than once, it is "unlimited"
         */
@@ -582,23 +680,51 @@ int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 # else
        /* -w is not supported, only -Z and/or -T */
        opt_complementary = "-";
-       opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T"));
+       opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T")IF_FEATURE_PS_LONG("l"));
 # endif
-#endif
 
-#if ENABLE_SELINUX
+# if ENABLE_SELINUX
        if ((opts & OPT_Z) && is_selinux_enabled()) {
                psscan_flags = PSSCAN_PID | PSSCAN_CONTEXT
                                | PSSCAN_STATE | PSSCAN_COMM;
                puts("  PID CONTEXT                          STAT COMMAND");
        } else
-#endif
-       {
+# endif
+       if (opts & OPT_l) {
+               psscan_flags = PSSCAN_STATE | PSSCAN_UIDGID | PSSCAN_PID | PSSCAN_PPID
+                       | PSSCAN_TTY | PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_COMM
+                       | PSSCAN_VSZ | PSSCAN_RSS;
+/* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
+ * mandates for -l:
+ * -F     Flags (?)
+ * S      State
+ * UID,PID,PPID
+ * -C     CPU usage
+ * -PRI   The priority of the process; higher numbers mean lower priority
+ * -NI    Nice value
+ * -ADDR  The address of the process (?)
+ * SZ     The size in blocks of the core image
+ * -WCHAN The event for which the process is waiting or sleeping
+ * TTY
+ * TIME   The cumulative execution time
+ * CMD
+ * We don't show fields marked with '-'.
+ * We show VSZ and RSS instead of SZ.
+ * We also show STIME (standard says that -f shows it, -l doesn't).
+ */
+               puts("S   UID   PID  PPID   VSZ   RSS TTY   STIME TIME     CMD");
+# if ENABLE_FEATURE_PS_LONG
+               now = time(NULL);
+               uptime = get_uptime();
+# endif
+       }
+       else {
                puts("  PID USER       VSZ STAT COMMAND");
        }
        if (opts & OPT_T) {
                psscan_flags |= PSSCAN_TASKS;
        }
+#endif
 
        p = NULL;
        while ((p = procps_scan(p, psscan_flags)) != NULL) {
@@ -612,15 +738,47 @@ int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
                } else
 #endif
                {
-                       const char *user = get_cached_username(p->uid);
-                       //if (p->vsz == 0)
-                       //      len = printf("%5u %-8.8s        %s ",
-                       //              p->pid, user, p->state);
-                       //else
+                       char buf6[6];
+                       smart_ulltoa5(p->vsz, buf6, " mgtpezy")[0] = '\0';
+#if ENABLE_FEATURE_PS_LONG
+                       if (opts & OPT_l) {
+                               char bufr[6], stime_str[6];
+                               char tty[2 * sizeof(int)*3 + 2];
+                               char *endp;
+                               unsigned sut = (p->stime + p->utime) / 100;
+                               unsigned elapsed = uptime - (p->start_time / 100);
+                               time_t start = now - elapsed;
+                               struct tm *tm = localtime(&start);
+
+                               smart_ulltoa5(p->rss, bufr, " mgtpezy")[0] = '\0';
+
+                               if (p->tty_major == 136)
+                                       /* It should be pts/N, not ptsN, but N > 9
+                                        * will overflow field width...
+                                        */
+                                       endp = stpcpy(tty, "pts");
+                               else
+                               if (p->tty_major == 4) {
+                                       endp = stpcpy(tty, "tty");
+                                       if (p->tty_minor >= 64) {
+                                               p->tty_minor -= 64;
+                                               *endp++ = 'S';
+                                       }
+                               }
+                               else
+                                       endp = tty + sprintf(tty, "%d:", p->tty_major);
+                               strcpy(endp, utoa(p->tty_minor));
+
+                               strftime(stime_str, 6, (elapsed >= (24 * 60 * 60)) ? "%b%d" : "%H:%M", tm);
+                               stime_str[5] = '\0';
+                               //            S  UID PID PPID VSZ RSS TTY STIME TIME        CMD
+                               len = printf("%c %5u %5u %5u %5s %5s %-5s %s %02u:%02u:%02u ",
+                                       p->state[0], p->uid, p->pid, p->ppid, buf6, bufr, tty,
+                                       stime_str, sut / 3600, (sut % 3600) / 60, sut % 60);
+                       } else
+#endif
                        {
-                               char buf6[6];
-                               smart_ulltoa5(p->vsz, buf6, " mgtpezy");
-                               buf6[5] = '\0';
+                               const char *user = get_cached_username(p->uid);
                                len = printf("%5u %-8.8s %s %s  ",
                                        p->pid, user, buf6, p->state);
                        }
diff --git a/procps/pstree.c b/procps/pstree.c
new file mode 100644 (file)
index 0000000..ed1a412
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * pstree.c - display process tree
+ *
+ * Copyright (C) 1993-2002 Werner Almesberger
+ * Copyright (C) 2002-2009 Craig Small
+ * Copyright (C) 2010 Lauri Kasanen
+ *
+ * Based on pstree (PSmisc) 22.13.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//config:config PSTREE
+//config:      bool "pstree"
+//config:      default y
+//config:      help
+//config:        Display a tree of processes.
+
+//applet:IF_PSTREE(APPLET(pstree, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_PSTREE) += pstree.o
+
+//usage:#define pstree_trivial_usage
+//usage:       "[-p] [PID|USER]"
+//usage:#define pstree_full_usage "\n\n"
+//usage:       "Display process tree, optionally start from USER or PID\n"
+//usage:     "\n       -p      Show pids"
+
+#include "libbb.h"
+
+#define PROC_BASE "/proc"
+
+#define OPT_PID  (1 << 0)
+
+struct child;
+
+#ifdef ENABLE_FEATURE_SHOW_THREADS
+/* For threads, we add {...} around the comm, so we need two extra bytes */
+# define COMM_DISP_LEN (COMM_LEN + 2)
+#else
+# define COMM_DISP_LEN COMM_LEN
+#endif
+
+typedef struct proc {
+       char comm[COMM_DISP_LEN + 1];
+//     char flags; - unused, delete?
+       pid_t pid;
+       uid_t uid;
+       struct child *children;
+       struct proc *parent;
+       struct proc *next;
+} PROC;
+
+/* For flags above */
+//#define PFLAG_THREAD  0x01
+
+typedef struct child {
+       PROC *child;
+       struct child *next;
+} CHILD;
+
+#define empty_2  "  "
+#define branch_2 "|-"
+#define vert_2   "| "
+#define last_2   "`-"
+#define single_3 "---"
+#define first_3  "-+-"
+
+struct globals {
+       /* 0-based. IOW: the number of chars we printed on current line */
+       unsigned cur_x;
+       unsigned output_width;
+
+       /* The buffers will be dynamically increased in size as needed */
+       unsigned capacity;
+       unsigned *width;
+       uint8_t *more;
+
+       PROC *list;
+
+       smallint dumped; /* used by dump_by_user */
+};
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+       SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+
+/*
+ * Allocates additional buffer space for width and more as needed.
+ * The first call will allocate the first buffer.
+ *
+ * bufindex  the index that will be used after the call to this function.
+ */
+static void ensure_buffer_capacity(int bufindex)
+{
+       if (bufindex >= G.capacity) {
+               G.capacity += 0x100;
+               G.width = xrealloc(G.width, G.capacity * sizeof(G.width[0]));
+               G.more = xrealloc(G.more, G.capacity * sizeof(G.more[0]));
+       }
+}
+
+/* NB: this function is never called with "bad" chars
+ * (control chars or chars >= 0x7f)
+ */
+static void out_char(char c)
+{
+       G.cur_x++;
+       if (G.cur_x > G.output_width)
+               return;
+       if (G.cur_x == G.output_width)
+               c = '+';
+       putchar(c);
+}
+
+/* NB: this function is never called with "bad" chars
+ * (control chars or chars >= 0x7f)
+ */
+static void out_string(const char *str)
+{
+       while (*str)
+               out_char(*str++);
+}
+
+static void out_newline(void)
+{
+       putchar('\n');
+       G.cur_x = 0;
+}
+
+static PROC *find_proc(pid_t pid)
+{
+       PROC *walk;
+
+       for (walk = G.list; walk; walk = walk->next)
+               if (walk->pid == pid)
+                       break;
+
+       return walk;
+}
+
+static PROC *new_proc(const char *comm, pid_t pid, uid_t uid)
+{
+       PROC *new = xzalloc(sizeof(*new));
+
+       strcpy(new->comm, comm);
+       new->pid = pid;
+       new->uid = uid;
+       new->next = G.list;
+
+       G.list = new;
+       return G.list;
+}
+
+static void add_child(PROC *parent, PROC *child)
+{
+       CHILD *new, **walk;
+       int cmp;
+
+       new = xmalloc(sizeof(*new));
+
+       new->child = child;
+       for (walk = &parent->children; *walk; walk = &(*walk)->next) {
+               cmp = strcmp((*walk)->child->comm, child->comm);
+               if (cmp > 0)
+                       break;
+               if (cmp == 0 && (*walk)->child->uid > child->uid)
+                       break;
+       }
+       new->next = *walk;
+       *walk = new;
+}
+
+static void add_proc(const char *comm, pid_t pid, pid_t ppid,
+                       uid_t uid /*, char isthread*/)
+{
+       PROC *this, *parent;
+
+       this = find_proc(pid);
+       if (!this)
+               this = new_proc(comm, pid, uid);
+       else {
+               strcpy(this->comm, comm);
+               this->uid = uid;
+       }
+
+       if (pid == ppid)
+               ppid = 0;
+//     if (isthread)
+//             this->flags |= PFLAG_THREAD;
+
+       parent = find_proc(ppid);
+       if (!parent)
+               parent = new_proc("?", ppid, 0);
+
+       add_child(parent, this);
+       this->parent = parent;
+}
+
+static int tree_equal(const PROC *a, const PROC *b)
+{
+       const CHILD *walk_a, *walk_b;
+
+       if (strcmp(a->comm, b->comm) != 0)
+               return 0;
+       if ((option_mask32 /*& OPT_PID*/) && a->pid != b->pid)
+               return 0;
+
+       for (walk_a = a->children, walk_b = b->children;
+         walk_a && walk_b;
+         walk_a = walk_a->next, walk_b = walk_b->next
+       ) {
+               if (!tree_equal(walk_a->child, walk_b->child))
+                       return 0;
+       }
+
+       return !(walk_a || walk_b);
+}
+
+static int out_args(const char *mystr)
+{
+       const char *here;
+       int strcount = 0;
+       char tmpstr[5];
+
+       for (here = mystr; *here; here++) {
+               if (*here == '\\') {
+                       out_string("\\\\");
+                       strcount += 2;
+               } else if (*here >= ' ' && *here < 0x7f) {
+                       out_char(*here);
+                       strcount++;
+               } else {
+                       sprintf(tmpstr, "\\%03o", (unsigned char) *here);
+                       out_string(tmpstr);
+                       strcount += 4;
+               }
+       }
+
+       return strcount;
+}
+
+static void
+dump_tree(PROC *current, int level, int rep, int leaf, int last, int closing)
+{
+       CHILD *walk, *next, **scan;
+       int lvl, i, add, offset, count, comm_len, first;
+       char tmp[sizeof(int)*3 + 4];
+
+       if (!current)
+               return;
+
+       if (!leaf) {
+               for (lvl = 0; lvl < level; lvl++) {
+                       i = G.width[lvl] + 1;
+                       while (--i >= 0)
+                               out_char(' ');
+
+                       if (lvl == level - 1) {
+                               if (last) {
+                                       out_string(last_2);
+                               } else {
+                                       out_string(branch_2);
+                               }
+                       } else {
+                               if (G.more[lvl + 1]) {
+                                       out_string(vert_2);
+                               } else {
+                                       out_string(empty_2);
+                               }
+                       }
+               }
+       }
+
+       add = 0;
+       if (rep > 1) {
+               add += sprintf(tmp, "%d*[", rep);
+               out_string(tmp);
+       }
+       comm_len = out_args(current->comm);
+       if (option_mask32 /*& OPT_PID*/) {
+               comm_len += sprintf(tmp, "(%d)", (int)current->pid);
+               out_string(tmp);
+       }
+       offset = G.cur_x;
+
+       if (!current->children) {
+               while (closing--)
+                       out_char(']');
+               out_newline();
+       }
+       ensure_buffer_capacity(level);
+       G.more[level] = !last;
+
+       G.width[level] = comm_len + G.cur_x - offset + add;
+       if (G.cur_x >= G.output_width) {
+               //out_string(first_3); - why? it won't print anything
+               //out_char('+');
+               out_newline();
+               return;
+       }
+
+       first = 1;
+       for (walk = current->children; walk; walk = next) {
+               count = 0;
+               next = walk->next;
+               scan = &walk->next;
+               while (*scan) {
+                       if (!tree_equal(walk->child, (*scan)->child))
+                               scan = &(*scan)->next;
+                       else {
+                               if (next == *scan)
+                                       next = (*scan)->next;
+                               count++;
+                               *scan = (*scan)->next;
+                       }
+               }
+               if (first) {
+                       out_string(next ? first_3 : single_3);
+                       first = 0;
+               }
+
+               dump_tree(walk->child, level + 1, count + 1,
+                               walk == current->children, !next,
+                               closing + (count ? 1 : 0));
+       }
+}
+
+static void dump_by_user(PROC *current, uid_t uid)
+{
+       const CHILD *walk;
+
+       if (!current)
+               return;
+
+       if (current->uid == uid) {
+               if (G.dumped)
+                       putchar('\n');
+               dump_tree(current, 0, 1, 1, 1, 0);
+               G.dumped = 1;
+               return;
+       }
+       for (walk = current->children; walk; walk = walk->next)
+               dump_by_user(walk->child, uid);
+}
+
+#if ENABLE_FEATURE_SHOW_THREADS
+static void handle_thread(const char *comm, pid_t pid, pid_t ppid, uid_t uid)
+{
+       char threadname[COMM_DISP_LEN + 1];
+       sprintf(threadname, "{%.*s}", (int)sizeof(threadname) - 3, comm);
+       add_proc(threadname, pid, ppid, uid/*, 1*/);
+}
+#endif
+
+static void mread_proc(void)
+{
+       procps_status_t *p = NULL;
+       pid_t parent = 0;
+       int flags = PSSCAN_COMM | PSSCAN_PID | PSSCAN_PPID | PSSCAN_UIDGID | PSSCAN_TASKS;
+
+       while ((p = procps_scan(p, flags)) != NULL) {
+#if ENABLE_FEATURE_SHOW_THREADS
+               if (p->pid != p->main_thread_pid)
+                       handle_thread(p->comm, p->pid, parent, p->uid);
+               else
+#endif
+               {
+                       add_proc(p->comm, p->pid, p->ppid, p->uid/*, 0*/);
+                       parent = p->pid;
+               }
+       }
+}
+
+int pstree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pstree_main(int argc UNUSED_PARAM, char **argv)
+{
+       pid_t pid = 1;
+       long uid = 0;
+
+       INIT_G();
+
+       get_terminal_width_height(0, &G.output_width, NULL);
+
+       opt_complementary = "?1";
+       getopt32(argv, "p");
+       argv += optind;
+
+       if (argv[0]) {
+               if (argv[0][0] >= '0' && argv[0][0] <= '9') {
+                       pid = xatoi(argv[0]);
+               } else {
+                       uid = xuname2uid(argv[0]);
+               }
+       }
+
+       mread_proc();
+
+       if (!uid)
+               dump_tree(find_proc(pid), 0, 1, 1, 1, 0);
+       else {
+               dump_by_user(find_proc(1), uid);
+               if (!G.dumped) {
+                       bb_error_msg_and_die("no processes found");
+               }
+       }
+
+       if (ENABLE_FEATURE_CLEAN_UP) {
+               free(G.width);
+               free(G.more);
+       }
+       return 0;
+}
diff --git a/procps/pwdx.c b/procps/pwdx.c
new file mode 100644 (file)
index 0000000..7818104
--- /dev/null
@@ -0,0 +1,60 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pwdx implementation for busybox
+ *
+ * Copyright (c) 2004 Nicholas Miell
+ * ported from procps by Pere Orga <gotrunks@gmail.com> 2011
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//config:config PWDX
+//config:      bool "pwdx"
+//config:      default y
+//config:      help
+//config:        Report current working directory of a process
+
+//applet:IF_PWDX(APPLET(pwdx, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_PWDX) += pwdx.o
+
+//usage:#define pwdx_trivial_usage
+//usage:       "PID..."
+//usage:#define pwdx_full_usage "\n\n"
+//usage:       "Show current directory for PIDs\n"
+
+#include "libbb.h"
+
+int pwdx_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pwdx_main(int argc UNUSED_PARAM, char **argv)
+{
+       opt_complementary = "-1";
+       getopt32(argv, "");
+       argv += optind;
+
+       do {
+               char buf[sizeof("/proc/%u/cwd") + sizeof(int)*3];
+               unsigned pid;
+               char *s;
+               char *arg = *argv;
+
+               // Allowed on the command line:
+               // /proc/NUM
+               // NUM
+               if (strncmp(arg, "/proc/", 6) == 0)
+                       arg += 6;
+
+               pid = bb_strtou(arg, NULL, 10);
+               if (errno)
+                       bb_error_msg_and_die("invalid process id: '%s'", arg);
+
+               sprintf(buf, "/proc/%u/cwd", pid);
+
+               s = xmalloc_readlink(buf);
+               // "pwdx /proc/1" says "/proc/1: DIR", not "1: DIR"
+               printf("%s: %s\n", *argv, s ? s : strerror(errno == ENOENT ? ESRCH : errno));
+               free(s);
+       } while (*++argv);
+
+       return EXIT_SUCCESS;
+}
index ea5fc70..77f400a 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2005  Manuel Novoa III  <mjn3@codepoet.org>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* Notes:
  *   following IDs (if any).  Multiple switches are allowed.
  */
 
+//usage:#define renice_trivial_usage
+//usage:       "{{-n INCREMENT} | PRIORITY} [[-p | -g | -u] ID...]"
+//usage:#define renice_full_usage "\n\n"
+//usage:       "Change scheduling priority for a running process\n"
+//usage:     "\n       -n      Adjust current nice value (smaller is faster)"
+//usage:     "\n       -p      Process id(s) (default)"
+//usage:     "\n       -g      Process group id(s)"
+//usage:     "\n       -u      Process user name(s) and/or id(s)"
+
 #include "libbb.h"
 #include <sys/resource.h>
 
@@ -32,7 +41,7 @@ int renice_main(int argc UNUSED_PARAM, char **argv)
        static const char Xetpriority_msg[] ALIGN1 = "%cetpriority";
 
        int retval = EXIT_SUCCESS;
-       int which = PRIO_PROCESS;       /* Default 'which' value. */
+       int which = PRIO_PROCESS;  /* Default 'which' value. */
        int use_relative = 0;
        int adjustment, new_priority;
        unsigned who;
@@ -57,7 +66,7 @@ int renice_main(int argc UNUSED_PARAM, char **argv)
                        arg += 2;
        }
 
-       if (!arg) {                             /* No args?  Then show usage. */
+       if (!arg) {  /* No args?  Then show usage. */
                bb_show_usage();
        }
 
@@ -91,7 +100,7 @@ int renice_main(int argc UNUSED_PARAM, char **argv)
                } else {
                        who = bb_strtou(arg, NULL, 10);
                        if (errno) {
-                               bb_error_msg("bad value: %s", arg);
+                               bb_error_msg("invalid number '%s'", arg);
                                goto HAD_ERROR;
                        }
                }
@@ -100,7 +109,7 @@ int renice_main(int argc UNUSED_PARAM, char **argv)
                if (use_relative) {
                        int old_priority;
 
-                       errno = 0;       /* Needed for getpriority error detection. */
+                       errno = 0;  /* Needed for getpriority error detection. */
                        old_priority = getpriority(which, who);
                        if (errno) {
                                bb_perror_msg(Xetpriority_msg, 'g');
index 06cf93c..9d1126a 100644 (file)
@@ -8,7 +8,7 @@
  herein by reference.
 */
 
-//applet:IF_SMEMCAP(APPLET(smemcap, _BB_DIR_USR_BIN, _BB_SUID_DROP))
+//applet:IF_SMEMCAP(APPLET(smemcap, BB_DIR_USR_BIN, BB_SUID_DROP))
 
 //kbuild:lib-$(CONFIG_SMEMCAP) += smemcap.o
 
@@ -20,7 +20,7 @@
 //config:        a memory usage statistic tool.
 
 #include "libbb.h"
-#include "unarchive.h"
+#include "bb_archive.h"
 
 struct fileblock {
        struct fileblock *next;
@@ -41,7 +41,7 @@ static void writeheader(const char *path, struct stat *sb, int type)
        sprintf(header.size, "%o", (unsigned)sb->st_size);
        sprintf(header.mtime, "%llo", sb->st_mtime & 077777777777LL);
        header.typeflag = type;
-       //strcpy(header.magic, "ustar  "); - do we want to be standard-compliant?
+       strcpy(header.magic, "ustar  "); /* like GNU tar */
 
        /* Calculate and store the checksum (the sum of all of the bytes of
         * the header). The checksum field must be filled with blanks for the
@@ -125,5 +125,8 @@ int smemcap_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
                }
        }
 
+       if (ENABLE_FEATURE_CLEAN_UP)
+               closedir(d);
+
        return EXIT_SUCCESS;
 }
index fc601d6..c6a1de2 100644 (file)
@@ -4,13 +4,33 @@
  *
  * Copyright 1999 George Staikos
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Changelog:
  * v1.01   - added -p <preload> to preload values from a file
  * v1.01.1 - busybox applet aware by <solar@gentoo.org>
  */
 
+//usage:#define sysctl_trivial_usage
+//usage:       "[OPTIONS] [KEY[=VALUE]]..."
+//usage:#define sysctl_full_usage "\n\n"
+//usage:       "Show/set kernel parameters\n"
+//usage:     "\n       -e      Don't warn about unknown keys"
+//usage:     "\n       -n      Don't show key names"
+//usage:     "\n       -a      Show all values"
+/* Same as -a, no need to show it */
+/* //usage:     "\n    -A      Show all values in table form" */
+//usage:     "\n       -w      Set values"
+//usage:     "\n       -p FILE Set values from FILE (default /etc/sysctl.conf)"
+//usage:     "\n       -q      Set values silently"
+//usage:
+//usage:#define sysctl_example_usage
+//usage:       "sysctl [-n] [-e] variable...\n"
+//usage:       "sysctl [-n] [-e] [-q] -w variable=value...\n"
+//usage:       "sysctl [-n] [-e] -a\n"
+//usage:       "sysctl [-n] [-e] [-q] -p file  (default /etc/sysctl.conf)\n"
+//usage:       "sysctl [-n] [-e] -A\n"
+
 #include "libbb.h"
 
 enum {
@@ -19,9 +39,11 @@ enum {
        FLAG_TABLE_FORMAT    = 1 << 2, /* not implemented */
        FLAG_SHOW_ALL        = 1 << 3,
        FLAG_PRELOAD_FILE    = 1 << 4,
+/* TODO: procps 3.2.8 seems to not require -w for KEY=VAL to work: */
        FLAG_WRITE           = 1 << 5,
+       FLAG_QUIET           = 1 << 6,
 };
-#define OPTION_STR "neAapw"
+#define OPTION_STR "neAapwq"
 
 static void sysctl_dots_to_slashes(char *name)
 {
@@ -91,7 +113,7 @@ static int sysctl_act_on_setting(char *setting)
                        retval = EXIT_FAILURE;
                        goto end;
                }
-               value = cptr + 1;       /* point to the value in name=value */
+               value = cptr + 1;  /* point to the value in name=value */
                if (setting == cptr || !*value) {
                        bb_error_msg("error: malformed setting '%s'", outname);
                        retval = EXIT_FAILURE;
@@ -126,9 +148,11 @@ static int sysctl_act_on_setting(char *setting)
 //TODO: procps 3.2.7 writes "value\n", note trailing "\n"
                xwrite_str(fd, value);
                close(fd);
-               if (option_mask32 & FLAG_SHOW_KEYS)
-                       printf("%s = ", outname);
-               puts(value);
+               if (!(option_mask32 & FLAG_QUIET)) {
+                       if (option_mask32 & FLAG_SHOW_KEYS)
+                               printf("%s = ", outname);
+                       puts(value);
+               }
        } else {
                char c;
 
@@ -181,7 +205,7 @@ static int sysctl_act_recursive(const char *path)
                                continue; /* d_name is "." or ".." */
                        /* if path was ".", drop "./" prefix: */
                        retval |= sysctl_act_recursive((next[0] == '.' && next[1] == '/') ?
-                                           next + 2 : next);
+                                       next + 2 : next);
                        free(next);
                }
                closedir(dirp);
@@ -206,7 +230,7 @@ static int sysctl_handle_preload_file(const char *filename)
        parser = config_open(filename);
        /* Must do it _after_ config_open(): */
        xchdir("/proc/sys");
-       /* xchroot(".") - if you are paranoid */
+       /* xchroot("/proc/sys") - if you are paranoid */
 
 //TODO: ';' is comment char too
 //TODO: comment may be only at line start. "var=1 #abc" - "1 #abc" is the value
@@ -242,7 +266,7 @@ int sysctl_main(int argc UNUSED_PARAM, char **argv)
                return sysctl_handle_preload_file(*argv ? *argv : "/etc/sysctl.conf");
        }
        xchdir("/proc/sys");
-       /* xchroot(".") - if you are paranoid */
+       /* xchroot("/proc/sys") - if you are paranoid */
        if (opt & (FLAG_TABLE_FORMAT | FLAG_SHOW_ALL)) {
                return sysctl_act_recursive(".");
        }
index ec84374..51f1c1a 100644 (file)
  *
  * Sept 2008: Vineet Gupta <vineet.gupta@arc.com>
  * Added Support for reporting SMP Information
- * - CPU where Process was last seen running
+ * - CPU where process was last seen running
  *   (to see effect of sched_setaffinity() etc)
- * - CPU Time Split (idle/IO/wait etc) PER CPU
+ * - CPU time split (idle/IO/wait etc) per CPU
  *
  * Copyright (c) 1992 Branko Lankester
  * Copyright (c) 1992 Roger Binns
  * Copyright (C) 1994-1996 Charles L. Blake.
  * Copyright (C) 1992-1998 Michael K. Johnson
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+/* How to snapshot /proc for debugging top problems:
+ * for f in /proc/[0-9]*""/stat; do
+ *         n=${f#/proc/}
+ *         n=${n%/stat}_stat
+ *         cp $f $n
+ * done
+ * cp /proc/stat /proc/meminfo /proc/loadavg .
+ * top -bn1 >top.out
+ *
+ * ...and how to run top on it on another machine:
+ * rm -rf proc; mkdir proc
+ * for f in [0-9]*_stat; do
+ *         p=${f%_stat}
+ *         mkdir -p proc/$p
+ *         cp $f proc/$p/stat
+ * done
+ * cp stat meminfo loadavg proc
+ * chroot . ./top -bn1 >top1.out
  */
 
+//config:config TOP
+//config:      bool "top"
+//config:      default y
+//config:      help
+//config:        The top program provides a dynamic real-time view of a running
+//config:        system.
+//config:
+//config:config FEATURE_TOP_CPU_USAGE_PERCENTAGE
+//config:      bool "Show CPU per-process usage percentage"
+//config:      default y
+//config:      depends on TOP
+//config:      help
+//config:        Make top display CPU usage for each process.
+//config:        This adds about 2k.
+//config:
+//config:config FEATURE_TOP_CPU_GLOBAL_PERCENTS
+//config:      bool "Show CPU global usage percentage"
+//config:      default y
+//config:      depends on FEATURE_TOP_CPU_USAGE_PERCENTAGE
+//config:      help
+//config:        Makes top display "CPU: NN% usr NN% sys..." line.
+//config:        This adds about 0.5k.
+//config:
+//config:config FEATURE_TOP_SMP_CPU
+//config:      bool "SMP CPU usage display ('c' key)"
+//config:      default y
+//config:      depends on FEATURE_TOP_CPU_GLOBAL_PERCENTS
+//config:      help
+//config:        Allow 'c' key to switch between individual/cumulative CPU stats
+//config:        This adds about 0.5k.
+//config:
+//config:config FEATURE_TOP_DECIMALS
+//config:      bool "Show 1/10th of a percent in CPU/mem statistics"
+//config:      default y
+//config:      depends on FEATURE_TOP_CPU_USAGE_PERCENTAGE
+//config:      help
+//config:        Show 1/10th of a percent in CPU/mem statistics.
+//config:        This adds about 0.3k.
+//config:
+//config:config FEATURE_TOP_SMP_PROCESS
+//config:      bool "Show CPU process runs on ('j' field)"
+//config:      default y
+//config:      depends on TOP
+//config:      help
+//config:        Show CPU where process was last found running on.
+//config:        This is the 'j' field.
+//config:
+//config:config FEATURE_TOPMEM
+//config:      bool "Topmem command ('s' key)"
+//config:      default y
+//config:      depends on TOP
+//config:      help
+//config:        Enable 's' in top (gives lots of memory info).
+
 #include "libbb.h"
 
 
@@ -73,15 +146,20 @@ enum { SORT_DEPTH = 3 };
 struct globals {
        top_status_t *top;
        int ntop;
+       smallint inverted;
 #if ENABLE_FEATURE_TOPMEM
        smallint sort_field;
-       smallint inverted;
 #endif
 #if ENABLE_FEATURE_TOP_SMP_CPU
        smallint smp_cpu_info; /* one/many cpu info lines? */
 #endif
+       unsigned lines;  /* screen height */
 #if ENABLE_FEATURE_USE_TERMIOS
        struct termios initial_settings;
+       int scroll_ofs;
+#define G_scroll_ofs G.scroll_ofs
+#else
+#define G_scroll_ofs 0
 #endif
 #if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
        cmp_funcp sort_function[1];
@@ -99,6 +177,9 @@ struct globals {
        jiffy_counts_t *cpu_jif, *cpu_prev_jif;
        int num_cpus;
 #endif
+#if ENABLE_FEATURE_USE_TERMIOS
+       char kbd_input[KEYCODE_BUFFER_SIZE];
+#endif
        char line_buf[80];
 }; //FIX_ALIASING; - large code growth
 enum { LINE_BUF_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line_buf) };
@@ -107,7 +188,6 @@ struct BUG_bad_size {
        char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
        char BUG_line_buf_too_small[LINE_BUF_SIZE > 80 ? 1 : -1];
 };
-#define INIT_G() do { } while (0)
 #define top              (G.top               )
 #define ntop             (G.ntop              )
 #define sort_field       (G.sort_field        )
@@ -124,6 +204,7 @@ struct BUG_bad_size {
 #define num_cpus         (G.num_cpus          )
 #define total_pcpu       (G.total_pcpu        )
 #define line_buf         (G.line_buf          )
+#define INIT_G() do { } while (0)
 
 enum {
        OPT_d = (1 << 0),
@@ -175,9 +256,9 @@ static int mult_lvl_cmp(void* a, void* b)
        for (i = 0; i < SORT_DEPTH; i++) {
                cmp_val = (*sort_function[i])(a, b);
                if (cmp_val != 0)
-                       return cmp_val;
+                       break;
        }
-       return 0;
+       return inverted ? -cmp_val : cmp_val;
 }
 
 static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
@@ -213,7 +294,7 @@ static void get_jiffy_counts(void)
         * they are used to calculate per process CPU% */
        prev_jif = cur_jif;
        if (read_cpu_jiffy(fp, &cur_jif) < 4)
-               bb_error_msg_and_die("can't read /proc/stat");
+               bb_error_msg_and_die("can't read '%s'", "/proc/stat");
 
 #if !ENABLE_FEATURE_TOP_SMP_CPU
        fclose(fp);
@@ -519,7 +600,7 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width)
 
        /* what info of the processes is shown */
        printf(OPT_BATCH_MODE ? "%.*s" : "\033[7m%.*s\033[0m", scr_width,
-               "  PID  PPID USER     STAT   VSZ %MEM"
+               "  PID  PPID USER     STAT   VSZ %VSZ"
                IF_FEATURE_TOP_SMP_PROCESS(" CPU")
                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
                " COMMAND");
@@ -537,7 +618,7 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width)
 # define FMT "%4u%%"
 #endif
        /*
-        * MEM% = s->vsz/MemTotal
+        * %VSZ = s->vsz/MemTotal
         */
        pmem_shift = BITS_PER_INT-11;
        pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
@@ -546,7 +627,7 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width)
                pmem_scale /= 4;
                pmem_shift -= 2;
        }
-       pmem_half = (1U << pmem_shift) / (ENABLE_FEATURE_TOP_DECIMALS? 20 : 2);
+       pmem_half = (1U << pmem_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
 #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
        busy_jifs = cur_jif.busy - prev_jif.busy;
        /* This happens if there were lots of short-lived processes
@@ -577,15 +658,15 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width)
                pcpu_scale /= 4;
                pcpu_shift -= 2;
        }
-       pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS? 20 : 2);
+       pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
        /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
 #endif
 
        /* Ok, all preliminary data is ready, go through the list */
        scr_width += 2; /* account for leading '\n' and trailing NUL */
-       if (lines_rem > ntop)
-               lines_rem = ntop;
-       s = top;
+       if (lines_rem > ntop - G_scroll_ofs)
+               lines_rem = ntop - G_scroll_ofs;
+       s = top + G_scroll_ofs;
        while (--lines_rem >= 0) {
                unsigned col;
                CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift);
@@ -596,8 +677,8 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width)
                if (s->vsz >= 100000)
                        sprintf(vsz_str_buf, "%6ldm", s->vsz/1024);
                else
-                       sprintf(vsz_str_buf, "%7ld", s->vsz);
-               /* PID PPID USER STAT VSZ %MEM [%CPU] COMMAND */
+                       sprintf(vsz_str_buf, "%7lu", s->vsz);
+               /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */
                col = snprintf(line_buf, scr_width,
                                "\n" "%5u%6u %-8.8s %s%s" FMT
                                IF_FEATURE_TOP_SMP_PROCESS(" %3d")
@@ -630,14 +711,14 @@ static void clearmems(void)
        clear_username_cache();
        free(top);
        top = NULL;
-       ntop = 0;
 }
 
 #if ENABLE_FEATURE_USE_TERMIOS
 
 static void reset_term(void)
 {
-       tcsetattr_stdin_TCSANOW(&initial_settings);
+       if (!OPT_BATCH_MODE)
+               tcsetattr_stdin_TCSANOW(&initial_settings);
        if (ENABLE_FEATURE_CLEAN_UP) {
                clearmems();
 # if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
@@ -646,11 +727,12 @@ static void reset_term(void)
        }
 }
 
-static void sig_catcher(int sig UNUSED_PARAM)
+static void sig_catcher(int sig)
 {
        reset_term();
-       exit(EXIT_FAILURE);
+       kill_myself_with_sig(sig);
 }
+
 #endif /* FEATURE_USE_TERMIOS */
 
 /*
@@ -765,24 +847,23 @@ static void display_topmem_header(int scr_width, int *lines_rem_p)
 static void ulltoa6_and_space(unsigned long long ul, char buf[6])
 {
        /* see http://en.wikipedia.org/wiki/Tera */
-       smart_ulltoa5(ul, buf, " mgtpezy");
-       buf[5] = ' ';
+       smart_ulltoa5(ul, buf, " mgtpezy")[0] = ' ';
 }
 
 static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width)
 {
 #define HDR_STR "  PID   VSZ VSZRW   RSS (SHR) DIRTY (SHR) STACK"
 #define MIN_WIDTH sizeof(HDR_STR)
-       const topmem_status_t *s = topmem;
+       const topmem_status_t *s = topmem + G_scroll_ofs;
 
        display_topmem_header(scr_width, &lines_rem);
        strcpy(line_buf, HDR_STR " COMMAND");
-       line_buf[5 + sort_field * 6] = '*';
+       line_buf[11 + sort_field * 6] = "^_"[inverted];
        printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf);
        lines_rem--;
 
-       if (lines_rem > ntop)
-               lines_rem = ntop;
+       if (lines_rem > ntop - G_scroll_ofs)
+               lines_rem = ntop - G_scroll_ofs;
        while (--lines_rem >= 0) {
                /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */
                ulltoa6_and_space(s->pid     , &line_buf[0*6]);
@@ -830,24 +911,210 @@ enum {
                | PSSCAN_PID
                | PSSCAN_SMAPS
                | PSSCAN_COMM,
+       EXIT_MASK = (unsigned)-1,
 };
 
+#if ENABLE_FEATURE_USE_TERMIOS
+static unsigned handle_input(unsigned scan_mask, unsigned interval)
+{
+       struct pollfd pfd[1];
+
+       if (option_mask32 & OPT_EOF) {
+               /* EOF on stdin ("top </dev/null") */
+               sleep(interval);
+               return scan_mask;
+       }
+
+       pfd[0].fd = 0;
+       pfd[0].events = POLLIN;
+
+       while (1) {
+               int32_t c;
+
+               c = read_key(STDIN_FILENO, G.kbd_input, interval * 1000);
+               if (c == -1 && errno != EAGAIN) {
+                       /* error/EOF */
+                       option_mask32 |= OPT_EOF;
+                       break;
+               }
+               interval = 0;
+
+               if (c == initial_settings.c_cc[VINTR])
+                       return EXIT_MASK;
+               if (c == initial_settings.c_cc[VEOF])
+                       return EXIT_MASK;
+
+               if (c == KEYCODE_UP) {
+                       G_scroll_ofs--;
+                       goto normalize_ofs;
+               }
+               if (c == KEYCODE_DOWN) {
+                       G_scroll_ofs++;
+                       goto normalize_ofs;
+               }
+               if (c == KEYCODE_HOME) {
+                       G_scroll_ofs = 0;
+                       break;
+               }
+               if (c == KEYCODE_END) {
+                       G_scroll_ofs = ntop - G.lines / 2;
+                       goto normalize_ofs;
+               }
+               if (c == KEYCODE_PAGEUP) {
+                       G_scroll_ofs -= G.lines / 2;
+                       goto normalize_ofs;
+               }
+               if (c == KEYCODE_PAGEDOWN) {
+                       G_scroll_ofs += G.lines / 2;
+ normalize_ofs:
+                       if (G_scroll_ofs >= ntop)
+                               G_scroll_ofs = ntop - 1;
+                       if (G_scroll_ofs < 0)
+                               G_scroll_ofs = 0;
+                       break;
+               }
+
+               c |= 0x20; /* lowercase */
+               if (c == 'q')
+                       return EXIT_MASK;
+
+               if (c == 'n') {
+                       IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
+                       sort_function[0] = pid_sort;
+                       continue;
+               }
+               if (c == 'm') {
+                       IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
+                       sort_function[0] = mem_sort;
+# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+                       sort_function[1] = pcpu_sort;
+                       sort_function[2] = time_sort;
+# endif
+                       continue;
+               }
+# if ENABLE_FEATURE_SHOW_THREADS
+               if (c == 'h'
+               IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK)
+               ) {
+                       scan_mask ^= PSSCAN_TASKS;
+                       continue;
+               }
+# endif
+# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+               if (c == 'p') {
+                       IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
+                       sort_function[0] = pcpu_sort;
+                       sort_function[1] = mem_sort;
+                       sort_function[2] = time_sort;
+                       continue;
+               }
+               if (c == 't') {
+                       IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
+                       sort_function[0] = time_sort;
+                       sort_function[1] = mem_sort;
+                       sort_function[2] = pcpu_sort;
+                       continue;
+               }
+#  if ENABLE_FEATURE_TOPMEM
+               if (c == 's') {
+                       scan_mask = TOPMEM_MASK;
+                       free(prev_hist);
+                       prev_hist = NULL;
+                       prev_hist_count = 0;
+                       sort_field = (sort_field + 1) % NUM_SORT_FIELD;
+                       continue;
+               }
+#  endif
+               if (c == 'r') {
+                       inverted ^= 1;
+                       continue;
+               }
+#  if ENABLE_FEATURE_TOP_SMP_CPU
+               /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
+               if (c == 'c' || c == '1') {
+                       /* User wants to toggle per cpu <> aggregate */
+                       if (smp_cpu_info) {
+                               free(cpu_prev_jif);
+                               free(cpu_jif);
+                               cpu_jif = &cur_jif;
+                               cpu_prev_jif = &prev_jif;
+                       } else {
+                               /* Prepare for xrealloc() */
+                               cpu_jif = cpu_prev_jif = NULL;
+                       }
+                       num_cpus = 0;
+                       smp_cpu_info = !smp_cpu_info;
+                       get_jiffy_counts();
+                       continue;
+               }
+#  endif
+# endif
+               break; /* unknown key -> force refresh */
+       }
+
+       return scan_mask;
+}
+#endif
+
+//usage:#if ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_TOP_SMP_CPU
+//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...) __VA_ARGS__
+//usage:#else
+//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...)
+//usage:#endif
+//usage:#define top_trivial_usage
+//usage:       "[-b] [-nCOUNT] [-dSECONDS]" IF_FEATURE_TOPMEM(" [-m]")
+//usage:#define top_full_usage "\n\n"
+//usage:       "Provide a view of process activity in real time."
+//usage:   "\n""Read the status of all processes from /proc each SECONDS"
+//usage:   "\n""and display a screenful of them."
+//usage:   "\n""Keys:"
+//usage:   "\n""       N/M"
+//usage:                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/P")
+//usage:                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/T")
+//usage:           ": " IF_FEATURE_TOPMEM("show CPU usage, ") "sort by pid/mem"
+//usage:                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/cpu")
+//usage:                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/time")
+//usage:       IF_FEATURE_TOPMEM(
+//usage:   "\n""       S: show memory"
+//usage:       )
+//usage:   "\n""       R: reverse sort"
+//usage:       IF_SHOW_THREADS_OR_TOP_SMP(
+//usage:   "\n""       "
+//usage:                IF_FEATURE_SHOW_THREADS("H: toggle threads")
+//usage:                IF_FEATURE_SHOW_THREADS(IF_FEATURE_TOP_SMP_CPU(", "))
+//usage:                IF_FEATURE_TOP_SMP_CPU("1: toggle SMP")
+//usage:       )
+//usage:   "\n""       Q,^C: exit"
+//usage:   "\n"
+//usage:   "\n""Options:"
+//usage:   "\n""       -b      Batch mode"
+//usage:   "\n""       -n N    Exit after N iterations"
+//usage:   "\n""       -d N    Delay between updates"
+//usage:       IF_FEATURE_TOPMEM(
+//usage:   "\n""       -m      Same as 's' key"
+//usage:       )
+
+/* Interactive testing:
+ * echo sss | ./busybox top
+ * - shows memory screen
+ * echo sss | ./busybox top -bn1 >mem
+ * - saves memory screen - the *whole* list, not first NROWS processes!
+ * echo .m.s.s.s.s.s.s.q | ./busybox top -b >z
+ * - saves several different screens, and exits
+ *
+ * TODO: -i STRING param as a better alternative?
+ */
+
 int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int top_main(int argc UNUSED_PARAM, char **argv)
 {
        int iterations;
-       unsigned lines, col;
-       int lines_rem;
+       unsigned col;
        unsigned interval;
        char *str_interval, *str_iterations;
        unsigned scan_mask = TOP_MASK;
 #if ENABLE_FEATURE_USE_TERMIOS
        struct termios new_settings;
-       struct pollfd pfd[1];
-       unsigned char c;
-
-       pfd[0].fd = 0;
-       pfd[0].events = POLLIN;
 #endif
 
        INIT_G();
@@ -884,15 +1151,6 @@ int top_main(int argc UNUSED_PARAM, char **argv)
 
        /* change to /proc */
        xchdir("/proc");
-#if ENABLE_FEATURE_USE_TERMIOS
-       tcgetattr(0, (void *) &initial_settings);
-       memcpy(&new_settings, &initial_settings, sizeof(new_settings));
-       /* unbuffered input, turn off echo */
-       new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
-
-       bb_signals(BB_FATAL_SIGS, sig_catcher);
-       tcsetattr_stdin_TCSANOW(&new_settings);
-#endif
 
 #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
        sort_function[0] = pcpu_sort;
@@ -902,23 +1160,47 @@ int top_main(int argc UNUSED_PARAM, char **argv)
        sort_function[0] = mem_sort;
 #endif
 
-       while (1) {
+       if (OPT_BATCH_MODE) {
+               option_mask32 |= OPT_EOF;
+       }
+#if ENABLE_FEATURE_USE_TERMIOS
+       else {
+               tcgetattr(0, (void *) &initial_settings);
+               memcpy(&new_settings, &initial_settings, sizeof(new_settings));
+               /* unbuffered input, turn off echo */
+               new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
+               tcsetattr_stdin_TCSANOW(&new_settings);
+       }
+
+       bb_signals(BB_FATAL_SIGS, sig_catcher);
+
+       /* Eat initial input, if any */
+       scan_mask = handle_input(scan_mask, 0);
+#endif
+
+       while (scan_mask != EXIT_MASK) {
                procps_status_t *p = NULL;
 
-               lines = 24; /* default */
-               col = 79;
+               if (OPT_BATCH_MODE) {
+                       G.lines = INT_MAX;
+                       col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */
+               } else {
+                       G.lines = 24; /* default */
+                       col = 79;
 #if ENABLE_FEATURE_USE_TERMIOS
-               /* We output to stdout, we need size of stdout (not stdin)! */
-               get_terminal_width_height(STDOUT_FILENO, &col, &lines);
-               if (lines < 5 || col < 10) {
-                       sleep(interval);
-                       continue;
-               }
+                       /* We output to stdout, we need size of stdout (not stdin)! */
+                       get_terminal_width_height(STDOUT_FILENO, &col, &G.lines);
+                       if (G.lines < 5 || col < 10) {
+                               sleep(interval);
+                               continue;
+                       }
 #endif
-               if (col > LINE_BUF_SIZE-2) /* +2 bytes for '\n', NUL, */
-                       col = LINE_BUF_SIZE-2;
+                       if (col > LINE_BUF_SIZE - 2)
+                               col = LINE_BUF_SIZE - 2;
+               }
 
                /* read process IDs & status for all the processes */
+               ntop = 0;
                while ((p = procps_scan(p, scan_mask)) != NULL) {
                        int n;
 #if ENABLE_FEATURE_TOPMEM
@@ -942,20 +1224,20 @@ int top_main(int argc UNUSED_PARAM, char **argv)
                        }
 #if ENABLE_FEATURE_TOPMEM
                        else { /* TOPMEM */
-                               if (!(p->mapped_ro | p->mapped_rw))
+                               if (!(p->smaps.mapped_ro | p->smaps.mapped_rw))
                                        continue; /* kernel threads are ignored */
                                n = ntop;
                                /* No bug here - top and topmem are the same */
                                top = xrealloc_vector(topmem, 6, ntop++);
                                strcpy(topmem[n].comm, p->comm);
                                topmem[n].pid      = p->pid;
-                               topmem[n].vsz      = p->mapped_rw + p->mapped_ro;
-                               topmem[n].vszrw    = p->mapped_rw;
-                               topmem[n].rss_sh   = p->shared_clean + p->shared_dirty;
-                               topmem[n].rss      = p->private_clean + p->private_dirty + topmem[n].rss_sh;
-                               topmem[n].dirty    = p->private_dirty + p->shared_dirty;
-                               topmem[n].dirty_sh = p->shared_dirty;
-                               topmem[n].stack    = p->stack;
+                               topmem[n].vsz      = p->smaps.mapped_rw + p->smaps.mapped_ro;
+                               topmem[n].vszrw    = p->smaps.mapped_rw;
+                               topmem[n].rss_sh   = p->smaps.shared_clean + p->smaps.shared_dirty;
+                               topmem[n].rss      = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh;
+                               topmem[n].dirty    = p->smaps.private_dirty + p->smaps.shared_dirty;
+                               topmem[n].dirty_sh = p->smaps.shared_dirty;
+                               topmem[n].stack    = p->smaps.stack;
                        }
 #endif
                } /* end of "while we read /proc" */
@@ -984,15 +1266,11 @@ int top_main(int argc UNUSED_PARAM, char **argv)
                        qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
                }
 #endif
-               lines_rem = lines;
-               if (OPT_BATCH_MODE) {
-                       lines_rem = INT_MAX;
-               }
                if (scan_mask != TOPMEM_MASK)
-                       display_process_list(lines_rem, col);
+                       display_process_list(G.lines, col);
 #if ENABLE_FEATURE_TOPMEM
                else
-                       display_topmem_process_list(lines_rem, col);
+                       display_topmem_process_list(G.lines, col);
 #endif
                clearmems();
                if (iterations >= 0 && !--iterations)
@@ -1000,84 +1278,9 @@ int top_main(int argc UNUSED_PARAM, char **argv)
 #if !ENABLE_FEATURE_USE_TERMIOS
                sleep(interval);
 #else
-               if (option_mask32 & (OPT_b|OPT_EOF))
-                        /* batch mode, or EOF on stdin ("top </dev/null") */
-                       sleep(interval);
-               else if (safe_poll(pfd, 1, interval * 1000) > 0) {
-                       if (safe_read(STDIN_FILENO, &c, 1) != 1) { /* error/EOF? */
-                               option_mask32 |= OPT_EOF;
-                               continue;
-                       }
-                       if (c == initial_settings.c_cc[VINTR])
-                               break;
-                       c |= 0x20; /* lowercase */
-                       if (c == 'q')
-                               break;
-                       if (c == 'n') {
-                               IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
-                               sort_function[0] = pid_sort;
-                       }
-                       if (c == 'm') {
-                               IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
-                               sort_function[0] = mem_sort;
-# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
-                               sort_function[1] = pcpu_sort;
-                               sort_function[2] = time_sort;
-# endif
-                       }
-# if ENABLE_FEATURE_SHOW_THREADS
-                       if (c == 'h'
-                        IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK)
-                       ) {
-                               scan_mask ^= PSSCAN_TASKS;
-                       }
-# endif
-# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
-                       if (c == 'p') {
-                               IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
-                               sort_function[0] = pcpu_sort;
-                               sort_function[1] = mem_sort;
-                               sort_function[2] = time_sort;
-                       }
-                       if (c == 't') {
-                               IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
-                               sort_function[0] = time_sort;
-                               sort_function[1] = mem_sort;
-                               sort_function[2] = pcpu_sort;
-                       }
-#  if ENABLE_FEATURE_TOPMEM
-                       if (c == 's') {
-                               scan_mask = TOPMEM_MASK;
-                               free(prev_hist);
-                               prev_hist = NULL;
-                               prev_hist_count = 0;
-                               sort_field = (sort_field + 1) % NUM_SORT_FIELD;
-                       }
-                       if (c == 'r')
-                               inverted ^= 1;
-#  endif
-#  if ENABLE_FEATURE_TOP_SMP_CPU
-                       /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
-                       if (c == 'c' || c == '1') {
-                               /* User wants to toggle per cpu <> aggregate */
-                               if (smp_cpu_info) {
-                                       free(cpu_prev_jif);
-                                       free(cpu_jif);
-                                       cpu_jif = &cur_jif;
-                                       cpu_prev_jif = &prev_jif;
-                               } else {
-                                       /* Prepare for xrealloc() */
-                                       cpu_jif = cpu_prev_jif = NULL;
-                               }
-                               num_cpus = 0;
-                               smp_cpu_info = !smp_cpu_info;
-                               get_jiffy_counts();
-                       }
-#  endif
-# endif
-               }
+               scan_mask = handle_input(scan_mask, interval);
 #endif /* FEATURE_USE_TERMIOS */
-       } /* end of "while (1)" */
+       } /* end of "while (not Q)" */
 
        bb_putchar('\n');
 #if ENABLE_FEATURE_USE_TERMIOS
index d9aa1d0..778812a 100644 (file)
@@ -4,31 +4,59 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
-/* This version of uptime doesn't display the number of users on the system,
- * since busybox init doesn't mess with utmp.  For folks using utmp that are
- * just dying to have # of users reported, feel free to write it as some type
- * of CONFIG_FEATURE_UTMP_SUPPORT #define
+/* 2011                Pere Orga <gotrunks@gmail.com>
+ *
+ * Added FEATURE_UPTIME_UTMP_SUPPORT flag.
  */
 
 /* getopt not needed */
 
+//config:config UPTIME
+//config:      bool "uptime"
+//config:      default y
+//config:      select PLATFORM_LINUX #sysinfo()
+//config:      help
+//config:        uptime gives a one line display of the current time, how long
+//config:        the system has been running, how many users are currently logged
+//config:        on, and the system load averages for the past 1, 5, and 15 minutes.
+//config:
+//config:config FEATURE_UPTIME_UTMP_SUPPORT
+//config:      bool "Support for showing the number of users"
+//config:      default y
+//config:      depends on UPTIME && FEATURE_UTMP
+//config:      help
+//config:        Makes uptime display the number of users currently logged on.
+
+//usage:#define uptime_trivial_usage
+//usage:       ""
+//usage:#define uptime_full_usage "\n\n"
+//usage:       "Display the time since the last boot"
+//usage:
+//usage:#define uptime_example_usage
+//usage:       "$ uptime\n"
+//usage:       "  1:55pm  up  2:30, load average: 0.09, 0.04, 0.00\n"
+
 #include "libbb.h"
+#ifdef __linux__
+# include <sys/sysinfo.h>
+#endif
+
 
 #ifndef FSHIFT
 # define FSHIFT 16              /* nr of bits of precision */
 #endif
-#define FIXED_1         (1<<FSHIFT)     /* 1.0 as fixed-point */
-#define LOAD_INT(x) ((x) >> FSHIFT)
-#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
+#define FIXED_1      (1 << FSHIFT)     /* 1.0 as fixed-point */
+#define LOAD_INT(x)  (unsigned)((x) >> FSHIFT)
+#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1 - 1)) * 100)
 
 
 int uptime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int uptime_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 {
-       int updays, uphours, upminutes;
+       unsigned updays, uphours, upminutes;
        struct sysinfo info;
        struct tm *current_time;
        time_t current_secs;
@@ -38,20 +66,32 @@ int uptime_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 
        sysinfo(&info);
 
-       printf(" %02d:%02d:%02d up ",
+       printf(" %02u:%02u:%02u up ",
                        current_time->tm_hour, current_time->tm_min, current_time->tm_sec);
-       updays = (int) info.uptime / (60*60*24);
+       updays = (unsigned) info.uptime / (unsigned)(60*60*24);
        if (updays)
-               printf("%d day%s, ", updays, (updays != 1) ? "s" : "");
-       upminutes = (int) info.uptime / 60;
-       uphours = (upminutes / 60) % 24;
+               printf("%u day%s, ", updays, (updays != 1) ? "s" : "");
+       upminutes = (unsigned) info.uptime / (unsigned)60;
+       uphours = (upminutes / (unsigned)60) % (unsigned)24;
        upminutes %= 60;
        if (uphours)
-               printf("%2d:%02d, ", uphours, upminutes);
+               printf("%2u:%02u", uphours, upminutes);
        else
-               printf("%d min, ", upminutes);
+               printf("%u min", upminutes);
+
+#if ENABLE_FEATURE_UPTIME_UTMP_SUPPORT
+       {
+               struct utmp *ut;
+               unsigned users = 0;
+               while ((ut = getutent()) != NULL) {
+                       if ((ut->ut_type == USER_PROCESS) && (ut->ut_name[0] != '\0'))
+                               users++;
+               }
+               printf(",  %u users", users);
+       }
+#endif
 
-       printf("load average: %ld.%02ld, %ld.%02ld, %ld.%02ld\n",
+       printf(",  load average: %u.%02u, %u.%02u, %u.%02u\n",
                        LOAD_INT(info.loads[0]), LOAD_FRAC(info.loads[0]),
                        LOAD_INT(info.loads[1]), LOAD_FRAC(info.loads[1]),
                        LOAD_INT(info.loads[2]), LOAD_FRAC(info.loads[2]));
index a1cde9e..0397f21 100644 (file)
@@ -5,12 +5,25 @@
  * Copyright (C) 2001 by Michael Habermann <mhabermann@gmx.de>
  * Copyrigjt (C) Mar 16, 2003 Manuel Novoa III   (mjn3@codepoet.org)
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /* BB_AUDIT SUSv3 N/A */
 /* BB_AUDIT GNU defects -- only option -n is supported. */
 
+//usage:#define watch_trivial_usage
+//usage:       "[-n SEC] [-t] PROG ARGS"
+//usage:#define watch_full_usage "\n\n"
+//usage:       "Run PROG periodically\n"
+//usage:     "\n       -n      Loop period in seconds (default 2)"
+//usage:     "\n       -t      Don't print header"
+//usage:
+//usage:#define watch_example_usage
+//usage:       "$ watch date\n"
+//usage:       "Mon Dec 17 10:31:40 GMT 2000\n"
+//usage:       "Mon Dec 17 10:31:42 GMT 2000\n"
+//usage:       "Mon Dec 17 10:31:44 GMT 2000"
+
 #include "libbb.h"
 
 // procps 2.0.18:
@@ -56,7 +69,6 @@ int watch_main(int argc UNUSED_PARAM, char **argv)
                printf("\033[H""\033[J");
                if (!(opt & 0x2)) { // no -t
                        const unsigned time_len = sizeof("1234-67-90 23:56:89");
-                       time_t t;
 
                        // STDERR_FILENO is procps3 compat:
                        // "watch ls 2>/dev/null" does not detect tty size
@@ -66,10 +78,13 @@ int watch_main(int argc UNUSED_PARAM, char **argv)
                                free(header);
                                header = xasprintf("Every %us: %-*s", period, (int)width, cmd);
                        }
-                       time(&t);
-                       if (time_len < width)
-                               strftime(header + width - time_len, time_len,
-                                       "%Y-%m-%d %H:%M:%S", localtime(&t));
+                       if (time_len < width) {
+                               strftime_YYYYMMDDHHMMSS(
+                                       header + width - time_len,
+                                       time_len,
+                                       /*time_t*:*/ NULL
+                               );
+                       }
 
                        // compat: empty line between header and cmd output
                        printf("%s\n\n", header);
index 4d85372..0fce955 100644 (file)
@@ -2,16 +2,16 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
 INSERT
 
-lib-$(CONFIG_RUNSV) += runsv.o runit_lib.o
-lib-$(CONFIG_RUNSVDIR) += runsvdir.o runit_lib.o
-lib-$(CONFIG_SV) += sv.o runit_lib.o
-lib-$(CONFIG_SVLOGD) += svlogd.o runit_lib.o
+lib-$(CONFIG_RUNSV) += runsv.o
+lib-$(CONFIG_RUNSVDIR) += runsvdir.o
+lib-$(CONFIG_SV) += sv.o
+lib-$(CONFIG_SVLOGD) += svlogd.o
 lib-$(CONFIG_CHPST) += chpst.o
 
 lib-$(CONFIG_ENVDIR) += chpst.o
index ad08112..71af29f 100644 (file)
@@ -28,8 +28,70 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
 /* Dependencies on runit_lib.c removed */
 
+//usage:#define chpst_trivial_usage
+//usage:       "[-vP012] [-u USER[:GRP]] [-U USER[:GRP]] [-e DIR]\n"
+//usage:       "       [-/ DIR] [-n NICE] [-m BYTES] [-d BYTES] [-o N]\n"
+//usage:       "       [-p N] [-f BYTES] [-c BYTES] PROG ARGS"
+//usage:#define chpst_full_usage "\n\n"
+//usage:       "Change the process state, run PROG\n"
+//usage:     "\n       -u USER[:GRP]   Set uid and gid"
+//usage:     "\n       -U USER[:GRP]   Set $UID and $GID in environment"
+//usage:     "\n       -e DIR          Set environment variables as specified by files"
+//usage:     "\n                       in DIR: file=1st_line_of_file"
+//usage:     "\n       -/ DIR          Chroot to DIR"
+//usage:     "\n       -n NICE         Add NICE to nice value"
+//usage:     "\n       -m BYTES        Same as -d BYTES -s BYTES -l BYTES"
+//usage:     "\n       -d BYTES        Limit data segment"
+//usage:     "\n       -o N            Limit number of open files per process"
+//usage:     "\n       -p N            Limit number of processes per uid"
+//usage:     "\n       -f BYTES        Limit output file sizes"
+//usage:     "\n       -c BYTES        Limit core file size"
+//usage:     "\n       -v              Verbose"
+//usage:     "\n       -P              Create new process group"
+//usage:     "\n       -0              Close stdin"
+//usage:     "\n       -1              Close stdout"
+//usage:     "\n       -2              Close stderr"
+//usage:
+//usage:#define envdir_trivial_usage
+//usage:       "DIR PROG ARGS"
+//usage:#define envdir_full_usage "\n\n"
+//usage:       "Set various environment variables as specified by files\n"
+//usage:       "in the directory DIR, run PROG"
+//usage:
+//usage:#define envuidgid_trivial_usage
+//usage:       "USER PROG ARGS"
+//usage:#define envuidgid_full_usage "\n\n"
+//usage:       "Set $UID to USER's uid and $GID to USER's gid, run PROG"
+//usage:
+//usage:#define setuidgid_trivial_usage
+//usage:       "USER PROG ARGS"
+//usage:#define setuidgid_full_usage "\n\n"
+//usage:       "Set uid and gid to USER's uid and gid, drop supplementary group ids,\n"
+//usage:       "run PROG"
+//usage:
+//usage:#define softlimit_trivial_usage
+//usage:       "[-a BYTES] [-m BYTES] [-d BYTES] [-s BYTES] [-l BYTES]\n"
+//usage:       "       [-f BYTES] [-c BYTES] [-r BYTES] [-o N] [-p N] [-t N]\n"
+//usage:       "       PROG ARGS"
+//usage:#define softlimit_full_usage "\n\n"
+//usage:       "Set soft resource limits, then run PROG\n"
+//usage:     "\n       -a BYTES        Limit total size of all segments"
+//usage:     "\n       -m BYTES        Same as -d BYTES -s BYTES -l BYTES -a BYTES"
+//usage:     "\n       -d BYTES        Limit data segment"
+//usage:     "\n       -s BYTES        Limit stack segment"
+//usage:     "\n       -l BYTES        Limit locked memory size"
+//usage:     "\n       -o N            Limit number of open files per process"
+//usage:     "\n       -p N            Limit number of processes per uid"
+//usage:     "\nOptions controlling file sizes:"
+//usage:     "\n       -f BYTES        Limit output file sizes"
+//usage:     "\n       -c BYTES        Limit core file size"
+//usage:     "\nEfficiency opts:"
+//usage:     "\n       -r BYTES        Limit resident set size"
+//usage:     "\n       -t N            Limit CPU time, process receives"
+//usage:     "\n                       a SIGXCPU after N seconds"
+
 #include "libbb.h"
-#include <dirent.h>
+#include <sys/resource.h> /* getrlimit */
 
 /*
 Five applets here: chpst, envdir, envuidgid, setuidgid, softlimit.
@@ -123,10 +185,10 @@ static NOINLINE void edir(const char *directory_name)
                        if ((errno == EISDIR) && directory_name) {
                                if (option_mask32 & OPT_v)
                                        bb_perror_msg("warning: %s/%s is a directory",
-                                               directory_name, d->d_name);
+                                               directory_name, d->d_name);
                                continue;
-                       } else
-                               bb_perror_msg_and_die("open %s/%s",
+                       }
+                       bb_perror_msg_and_die("open %s/%s",
                                                directory_name, d->d_name);
                }
                size = full_read(fd, buf, sizeof(buf)-1);
@@ -174,7 +236,6 @@ int chpst_main(int argc UNUSED_PARAM, char **argv)
 {
        struct bb_uidgid_t ugid;
        char *set_user = set_user; /* for compiler */
-       char *env_user = env_user;
        char *env_dir = env_dir;
        char *root;
        char *nicestr;
@@ -202,7 +263,7 @@ int chpst_main(int argc UNUSED_PARAM, char **argv)
                        IF_CHPST("/:n:vP012"),
                        &limita, &limitc, &limitd, &limitf, &limitl,
                        &limitm, &limito, &limitp, &limitr, &limits, &limitt,
-                       &set_user, &env_user, &env_dir
+                       &set_user, &set_user, &env_dir
                        IF_CHPST(, &root, &nicestr));
                argv += optind;
                if (opt & OPT_m) { // -m means -asld
@@ -230,7 +291,7 @@ int chpst_main(int argc UNUSED_PARAM, char **argv)
 
        // envuidgid?
        if (ENABLE_ENVUIDGID && applet_name[0] == 'e' && applet_name[3] == 'u') {
-               env_user = *argv++;
+               set_user = *argv++;
                opt |= OPT_U;
        }
 
@@ -344,22 +405,19 @@ int chpst_main(int argc UNUSED_PARAM, char **argv)
        if (opt & OPT_e)
                edir(env_dir);
 
-       // FIXME: chrooted jail must have /etc/passwd if we move this after chroot!
-       // OTOH chroot fails for non-roots!
-       // SOLUTION: cache uid/gid before chroot, apply uid/gid after
+       if (opt & (OPT_u|OPT_U))
+               xget_uidgid(&ugid, set_user);
+
+       // chrooted jail must have /etc/passwd if we move this after chroot.
+       // OTOH chroot fails for non-roots.
+       // Solution: cache uid/gid before chroot, apply uid/gid after.
        if (opt & OPT_U) {
-               xget_uidgid(&ugid, env_user);
                xsetenv("GID", utoa(ugid.gid));
                xsetenv("UID", utoa(ugid.uid));
        }
 
-       if (opt & OPT_u) {
-               xget_uidgid(&ugid, set_user);
-       }
-
        if (opt & OPT_root) {
-               xchdir(root);
-               xchroot(".");
+               xchroot(root);
        }
 
        if (opt & OPT_u) {
diff --git a/runit/runit_lib.c b/runit/runit_lib.c
deleted file mode 100644 (file)
index ec18b5e..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
-Copyright (c) 2001-2006, Gerrit Pape
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-   1. Redistributions of source code must retain the above copyright notice,
-      this list of conditions and the following disclaimer.
-   2. Redistributions in binary form must reproduce the above copyright
-      notice, this list of conditions and the following disclaimer in the
-      documentation and/or other materials provided with the distribution.
-   3. The name of the author may not be used to endorse or promote products
-      derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
-/* Collected into one file from runit's many tiny files */
-/* TODO: review, eliminate unneeded stuff, move good stuff to libbb */
-
-#include <sys/poll.h>
-#include <sys/file.h>
-#include "libbb.h"
-#include "runit_lib.h"
-
-#ifdef UNUSED
-unsigned byte_chr(char *s,unsigned n,int c)
-{
-       char ch;
-       char *t;
-
-       ch = c;
-       t = s;
-       for (;;) {
-               if (!n) break;
-               if (*t == ch) break;
-               ++t;
-               --n;
-       }
-       return t - s;
-}
-
-static /* as it isn't used anywhere else */
-void tai_pack(char *s, const struct tai *t)
-{
-       uint64_t x;
-
-       x = t->x;
-       s[7] = x & 255; x >>= 8;
-       s[6] = x & 255; x >>= 8;
-       s[5] = x & 255; x >>= 8;
-       s[4] = x & 255; x >>= 8;
-       s[3] = x & 255; x >>= 8;
-       s[2] = x & 255; x >>= 8;
-       s[1] = x & 255; x >>= 8;
-       s[0] = x;
-}
-
-void tai_unpack(const char *s,struct tai *t)
-{
-       uint64_t x;
-
-       x = (unsigned char) s[0];
-       x <<= 8; x += (unsigned char) s[1];
-       x <<= 8; x += (unsigned char) s[2];
-       x <<= 8; x += (unsigned char) s[3];
-       x <<= 8; x += (unsigned char) s[4];
-       x <<= 8; x += (unsigned char) s[5];
-       x <<= 8; x += (unsigned char) s[6];
-       x <<= 8; x += (unsigned char) s[7];
-       t->x = x;
-}
-
-
-void taia_add(struct taia *t,const struct taia *u,const struct taia *v)
-{
-       t->sec.x = u->sec.x + v->sec.x;
-       t->nano = u->nano + v->nano;
-       t->atto = u->atto + v->atto;
-       if (t->atto > 999999999UL) {
-               t->atto -= 1000000000UL;
-               ++t->nano;
-       }
-       if (t->nano > 999999999UL) {
-               t->nano -= 1000000000UL;
-               ++t->sec.x;
-       }
-}
-
-int taia_less(const struct taia *t, const struct taia *u)
-{
-       if (t->sec.x < u->sec.x) return 1;
-       if (t->sec.x > u->sec.x) return 0;
-       if (t->nano < u->nano) return 1;
-       if (t->nano > u->nano) return 0;
-       return t->atto < u->atto;
-}
-
-void taia_now(struct taia *t)
-{
-       struct timeval now;
-       gettimeofday(&now, NULL);
-       tai_unix(&t->sec, now.tv_sec);
-       t->nano = 1000 * now.tv_usec + 500;
-       t->atto = 0;
-}
-
-/* UNUSED
-void taia_pack(char *s, const struct taia *t)
-{
-       unsigned long x;
-
-       tai_pack(s, &t->sec);
-       s += 8;
-
-       x = t->atto;
-       s[7] = x & 255; x >>= 8;
-       s[6] = x & 255; x >>= 8;
-       s[5] = x & 255; x >>= 8;
-       s[4] = x;
-       x = t->nano;
-       s[3] = x & 255; x >>= 8;
-       s[2] = x & 255; x >>= 8;
-       s[1] = x & 255; x >>= 8;
-       s[0] = x;
-}
-*/
-
-void taia_sub(struct taia *t, const struct taia *u, const struct taia *v)
-{
-       unsigned long unano = u->nano;
-       unsigned long uatto = u->atto;
-
-       t->sec.x = u->sec.x - v->sec.x;
-       t->nano = unano - v->nano;
-       t->atto = uatto - v->atto;
-       if (t->atto > uatto) {
-               t->atto += 1000000000UL;
-               --t->nano;
-       }
-       if (t->nano > unano) {
-               t->nano += 1000000000UL;
-               --t->sec.x;
-       }
-}
-
-/* XXX: breaks tai encapsulation */
-void taia_uint(struct taia *t, unsigned s)
-{
-       t->sec.x = s;
-       t->nano = 0;
-       t->atto = 0;
-}
-
-static
-uint64_t taia2millisec(const struct taia *t)
-{
-       return (t->sec.x * 1000) + (t->nano / 1000000);
-}
-
-void iopause(iopause_fd *x, unsigned len, struct taia *deadline, struct taia *stamp)
-{
-       int millisecs;
-       int i;
-
-       if (taia_less(deadline, stamp))
-               millisecs = 0;
-       else {
-               uint64_t m;
-               struct taia t;
-               t = *stamp;
-               taia_sub(&t, deadline, &t);
-               millisecs = m = taia2millisec(&t);
-               if (m > 1000) millisecs = 1000;
-               millisecs += 20;
-       }
-
-       for (i = 0; i < len; ++i)
-               x[i].revents = 0;
-
-       poll(x, len, millisecs);
-       /* XXX: some kernels apparently need x[0] even if len is 0 */
-       /* XXX: how to handle EAGAIN? are kernels really this dumb? */
-       /* XXX: how to handle EINVAL? when exactly can this happen? */
-}
-#endif
-
-int lock_ex(int fd)
-{
-       return flock(fd,LOCK_EX);
-}
-
-int lock_exnb(int fd)
-{
-       return flock(fd,LOCK_EX | LOCK_NB);
-}
-
-int open_append(const char *fn)
-{
-       return open(fn, O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
-}
-
-int open_read(const char *fn)
-{
-       return open(fn, O_RDONLY|O_NDELAY);
-}
-
-int open_trunc(const char *fn)
-{
-       return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644);
-}
-
-int open_write(const char *fn)
-{
-       return open(fn, O_WRONLY|O_NDELAY);
-}
-
-unsigned pmatch(const char *p, const char *s, unsigned len)
-{
-       for (;;) {
-               char c = *p++;
-               if (!c) return !len;
-               switch (c) {
-               case '*':
-                       c = *p;
-                       if (!c) return 1;
-                       for (;;) {
-                               if (!len) return 0;
-                               if (*s == c) break;
-                               ++s;
-                               --len;
-                       }
-                       continue;
-               case '+':
-                       c = *p++;
-                       if (c != *s) return 0;
-                       for (;;) {
-                               if (!len) return 1;
-                               if (*s != c) break;
-                               ++s;
-                               --len;
-                       }
-                       continue;
-                       /*
-               case '?':
-                       if (*p == '?') {
-                               if (*s != '?') return 0;
-                               ++p;
-                       }
-                       ++s; --len;
-                       continue;
-                       */
-               default:
-                       if (!len) return 0;
-                       if (*s != c) return 0;
-                       ++s;
-                       --len;
-                       continue;
-               }
-       }
-       return 0;
-}
index 88d1c9f..c36ea4c 100644 (file)
@@ -27,59 +27,6 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 
-//extern unsigned byte_chr(char *s,unsigned n,int c);
-//
-//struct tai {
-//     uint64_t x;
-//};
-//
-//#define tai_unix(t,u) ((void) ((t)->x = 0x400000000000000aULL + (uint64_t) (u)))
-//
-//#define TAI_PACK 8
-//extern void tai_unpack(const char *,struct tai *);
-//
-//extern void tai_uint(struct tai *,unsigned);
-//
-//struct taia {
-//     struct tai sec;
-//     unsigned long nano; /* 0...999999999 */
-//     unsigned long atto; /* 0...999999999 */
-//};
-//
-//extern void taia_now(struct taia *);
-//
-//extern void taia_add(struct taia *,const struct taia *,const struct taia *);
-//extern void taia_addsec(struct taia *,const struct taia *,int);
-//extern void taia_sub(struct taia *,const struct taia *,const struct taia *);
-//extern void taia_half(struct taia *,const struct taia *);
-//extern int taia_less(const struct taia *,const struct taia *);
-//
-//#define TAIA_PACK 16
-//extern void taia_pack(char *,const struct taia *);
-//
-//extern void taia_uint(struct taia *,unsigned);
-//
-//typedef struct pollfd iopause_fd;
-//#define IOPAUSE_READ POLLIN
-//#define IOPAUSE_WRITE POLLOUT
-//
-//extern void iopause(iopause_fd *,unsigned,struct taia *,struct taia *);
-
-extern int lock_ex(int);
-extern int lock_un(int);
-extern int lock_exnb(int);
-
-extern int open_read(const char *);
-extern int open_excl(const char *);
-extern int open_append(const char *);
-extern int open_trunc(const char *);
-extern int open_write(const char *);
-
-extern unsigned pmatch(const char *, const char *, unsigned);
-
-#define str_diff(s,t) strcmp((s), (t))
-#define str_equal(s,t) (!strcmp((s), (t)))
-
 /*
  * runsv / supervise / sv stuff
  */
index 5b221e9..d941e89 100644 (file)
@@ -28,7 +28,11 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
 /* TODO: depends on runit_lib.c - review and reduce/eliminate */
 
-#include <sys/poll.h>
+//usage:#define runsv_trivial_usage
+//usage:       "DIR"
+//usage:#define runsv_full_usage "\n\n"
+//usage:       "Start and monitor a service and optionally an appendant log service"
+
 #include <sys/file.h>
 #include "libbb.h"
 #include "runit_lib.h"
@@ -139,19 +143,10 @@ static void s_term(int sig_no UNUSED_PARAM)
        write(selfpipe.wr, "", 1); /* XXX */
 }
 
-/* libbb candidate */
-static char *bb_stpcpy(char *p, const char *to_add)
-{
-       while ((*p = *to_add) != '\0') {
-               p++;
-               to_add++;
-       }
-       return p;
-}
-
 static int open_trunc_or_warn(const char *name)
 {
-       int fd = open_trunc(name);
+       /* Why O_NDELAY? */
+       int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
        if (fd < 0)
                bb_perror_msg("%s: warning: cannot open %s",
                                dir, name);
@@ -176,7 +171,7 @@ static void update_status(struct svdir *s)
                }
                close(fd);
                if (rename_or_warn("supervise/pid.new",
-                   s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
+                               s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
                        return;
                pidchanged = 0;
        }
@@ -191,26 +186,26 @@ static void update_status(struct svdir *s)
                char *p = stat_buf;
                switch (s->state) {
                case S_DOWN:
-                       p = bb_stpcpy(p, "down");
+                       p = stpcpy(p, "down");
                        break;
                case S_RUN:
-                       p = bb_stpcpy(p, "run");
+                       p = stpcpy(p, "run");
                        break;
                case S_FINISH:
-                       p = bb_stpcpy(p, "finish");
+                       p = stpcpy(p, "finish");
                        break;
                }
                if (s->ctrl & C_PAUSE)
-                       p = bb_stpcpy(p, ", paused");
+                       p = stpcpy(p, ", paused");
                if (s->ctrl & C_TERM)
-                       p = bb_stpcpy(p, ", got TERM");
+                       p = stpcpy(p, ", got TERM");
                if (s->state != S_DOWN)
                        switch (s->sd_want) {
                        case W_DOWN:
-                               p = bb_stpcpy(p, ", want down");
+                               p = stpcpy(p, ", want down");
                                break;
                        case W_EXIT:
-                               p = bb_stpcpy(p, ", want exit");
+                               p = stpcpy(p, ", want exit");
                                break;
                        }
                *p++ = '\n';
@@ -523,7 +518,7 @@ int runsv_main(int argc UNUSED_PARAM, char **argv)
        }
        svd[0].fdlock = xopen3("log/supervise/lock"+4,
                        O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
-       if (lock_exnb(svd[0].fdlock) == -1)
+       if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
                fatal_cannot("lock supervise/lock");
        close_on_exec_on(svd[0].fdlock);
        if (haslog) {
@@ -547,7 +542,7 @@ int runsv_main(int argc UNUSED_PARAM, char **argv)
                }
                svd[1].fdlock = xopen3("log/supervise/lock",
                                O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
-               if (lock_ex(svd[1].fdlock) == -1)
+               if (flock(svd[1].fdlock, LOCK_EX) == -1)
                        fatal_cannot("lock log/supervise/lock");
                close_on_exec_on(svd[1].fdlock);
        }
@@ -617,7 +612,7 @@ int runsv_main(int argc UNUSED_PARAM, char **argv)
                                pidchanged = 1;
                                svd[0].ctrl &= ~C_TERM;
                                if (svd[0].state != S_FINISH) {
-                                       fd = open_read("finish");
+                                       fd = open("finish", O_RDONLY|O_NDELAY);
                                        if (fd != -1) {
                                                close(fd);
                                                svd[0].state = S_FINISH;
index 71fde75..af7e75b 100644 (file)
@@ -28,7 +28,13 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
 /* TODO: depends on runit_lib.c - review and reduce/eliminate */
 
-#include <sys/poll.h>
+//usage:#define runsvdir_trivial_usage
+//usage:       "[-P] [-s SCRIPT] DIR"
+//usage:#define runsvdir_full_usage "\n\n"
+//usage:       "Start a runsv process for each subdirectory. If it exits, restart it.\n"
+//usage:     "\n       -P              Put each runsv in a new session"
+//usage:     "\n       -s SCRIPT       Run SCRIPT <signo> after signal is processed"
+
 #include <sys/file.h>
 #include "libbb.h"
 #include "runit_lib.h"
@@ -68,8 +74,7 @@ struct globals {
 #define logpipe     (G.logpipe     )
 #define pfd         (G.pfd         )
 #define stamplog    (G.stamplog    )
-#define INIT_G() do { \
-} while (0)
+#define INIT_G() do { } while (0)
 
 static void fatal2_cannot(const char *m1, const char *m2)
 {
@@ -276,7 +281,7 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
        }
  run:
 #endif
-       curdir = open_read(".");
+       curdir = open(".", O_RDONLY|O_NDELAY);
        if (curdir == -1)
                fatal2_cannot("open current directory", "");
        close_on_exec_on(curdir);
@@ -312,8 +317,11 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
                                                last_mtime = s.st_mtime;
                                                last_dev = s.st_dev;
                                                last_ino = s.st_ino;
-                                               //if (now <= mtime)
-                                               //      sleep(1);
+                                               /* if the svdir changed this very second, wait until the
+                                                * next second, because we won't be able to detect more
+                                                * changes within this second */
+                                               while (time(NULL) == last_mtime)
+                                                       usleep(100000);
                                                need_rescan = do_rescan();
                                                while (fchdir(curdir) == -1) {
                                                        warn2_cannot("change directory, pausing", "");
index 3f76a2d..825e9d4 100644 (file)
@@ -153,7 +153,22 @@ Exit Codes
 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
 /* TODO: depends on runit_lib.c - review and reduce/eliminate */
 
-#include <sys/poll.h>
+//usage:#define sv_trivial_usage
+//usage:       "[-v] [-w SEC] CMD SERVICE_DIR..."
+//usage:#define sv_full_usage "\n\n"
+//usage:       "Control services monitored by runsv supervisor.\n"
+//usage:       "Commands (only first character is enough):\n"
+//usage:       "\n"
+//usage:       "status: query service status\n"
+//usage:       "up: if service isn't running, start it. If service stops, restart it\n"
+//usage:       "once: like 'up', but if service stops, don't restart it\n"
+//usage:       "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
+//usage:       "       if it exists. After it stops, don't restart service\n"
+//usage:       "exit: send TERM and CONT signals to service and log service. If they exit,\n"
+//usage:       "       runsv exits too\n"
+//usage:       "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
+//usage:       "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
+
 #include <sys/file.h>
 #include "libbb.h"
 #include "runit_lib.h"
@@ -176,6 +191,9 @@ struct globals {
 #define INIT_G() do { } while (0)
 
 
+#define str_equal(s,t) (!strcmp((s), (t)))
+
+
 static void fatal_cannot(const char *m1) NORETURN;
 static void fatal_cannot(const char *m1)
 {
@@ -221,7 +239,7 @@ static int svstatus_get(void)
 {
        int fd, r;
 
-       fd = open_write("supervise/ok");
+       fd = open("supervise/ok", O_WRONLY|O_NDELAY);
        if (fd == -1) {
                if (errno == ENODEV) {
                        *acts == 'x' ? ok("runsv not running")
@@ -232,7 +250,7 @@ static int svstatus_get(void)
                return -1;
        }
        close(fd);
-       fd = open_read("supervise/status");
+       fd = open("supervise/status", O_RDONLY|O_NDELAY);
        if (fd == -1) {
                warn("can't open supervise/status");
                return -1;
@@ -397,7 +415,7 @@ static int control(const char *a)
        if (svstatus.want == *a)
                return 0;
 */
-       fd = open_write("supervise/control");
+       fd = open("supervise/control", O_WRONLY|O_NDELAY);
        if (fd == -1) {
                if (errno != ENODEV)
                        warn("can't open supervise/control");
@@ -418,7 +436,6 @@ static int control(const char *a)
 int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int sv_main(int argc UNUSED_PARAM, char **argv)
 {
-       unsigned opt;
        char *x;
        char *action;
        const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
@@ -439,14 +456,14 @@ int sv_main(int argc UNUSED_PARAM, char **argv)
        if (x) waitsec = xatou(x);
 
        opt_complementary = "w+:vv"; /* -w N, -v is a counter */
-       opt = getopt32(argv, "w:v", &waitsec, &verbose);
+       getopt32(argv, "w:v", &waitsec, &verbose);
        argv += optind;
        action = *argv++;
        if (!action || !*argv) bb_show_usage();
 
        tnow = time(NULL) + 0x400000000000000aULL;
        tstart = tnow;
-       curdir = open_read(".");
+       curdir = open(".", O_RDONLY|O_NDELAY);
        if (curdir == -1)
                fatal_cannot("open current directory");
 
index 9fe81b9..c080b9a 100644 (file)
@@ -125,7 +125,23 @@ log message, you can use a pattern like this instead
 -*: *: pid *
 */
 
-#include <sys/poll.h>
+//usage:#define svlogd_trivial_usage
+//usage:       "[-ttv] [-r C] [-R CHARS] [-l MATCHLEN] [-b BUFLEN] DIR..."
+//usage:#define svlogd_full_usage "\n\n"
+//usage:       "Continuously read log data from stdin and write to rotated log files in DIRs"
+//usage:   "\n"
+//usage:   "\n""DIR/config file modifies behavior:"
+//usage:   "\n""sSIZE - when to rotate logs"
+//usage:   "\n""nNUM - number of files to retain"
+/*usage:   "\n""NNUM - min number files to retain" - confusing */
+/*usage:   "\n""tSEC - rotate file if it get SEC seconds old" - confusing */
+//usage:   "\n""!PROG - process rotated log with PROG"
+/*usage:   "\n""uIPADDR - send log over UDP" - unsupported */
+/*usage:   "\n""UIPADDR - send log over UDP and DONT log" - unsupported */
+/*usage:   "\n""pPFX - prefix each line with PFX" - unsupported */
+//usage:   "\n""+,-PATTERN - (de)select line for logging"
+//usage:   "\n""E,ePATTERN - (de)select line for stderr"
+
 #include <sys/file.h>
 #include "libbb.h"
 #include "runit_lib.h"
@@ -170,6 +186,7 @@ struct globals {
        unsigned nearest_rotate;
 
        void* (*memRchr)(const void *, int, size_t);
+       char *shell;
 
        smallint exitasap;
        smallint rotateasap;
@@ -261,6 +278,52 @@ static char* wstrdup(const char *str)
        return s;
 }
 
+static unsigned pmatch(const char *p, const char *s, unsigned len)
+{
+       for (;;) {
+               char c = *p++;
+               if (!c) return !len;
+               switch (c) {
+               case '*':
+                       c = *p;
+                       if (!c) return 1;
+                       for (;;) {
+                               if (!len) return 0;
+                               if (*s == c) break;
+                               ++s;
+                               --len;
+                       }
+                       continue;
+               case '+':
+                       c = *p++;
+                       if (c != *s) return 0;
+                       for (;;) {
+                               if (!len) return 1;
+                               if (*s != c) break;
+                               ++s;
+                               --len;
+                       }
+                       continue;
+                       /*
+               case '?':
+                       if (*p == '?') {
+                               if (*s != '?') return 0;
+                               ++p;
+                       }
+                       ++s; --len;
+                       continue;
+                       */
+               default:
+                       if (!len) return 0;
+                       if (*s != c) return 0;
+                       ++s;
+                       --len;
+                       continue;
+               }
+       }
+       return 0;
+}
+
 /*** ex fmt_ptime.[ch] ***/
 
 /* NUL terminated */
@@ -319,6 +382,9 @@ static void processorstart(struct logdir *ld)
        /* vfork'ed child trashes this byte, save... */
        sv_ch = ld->fnsave[26];
 
+       if (!G.shell)
+               G.shell = xstrdup(get_shell_name());
+
        while ((pid = vfork()) == -1)
                pause2cannot("vfork for processor", ld->name);
        if (!pid) {
@@ -342,7 +408,7 @@ static void processorstart(struct logdir *ld)
                ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
                fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
                xmove_fd(fd, 1);
-               fd = open_read("state");
+               fd = open("state", O_RDONLY|O_NDELAY);
                if (fd == -1) {
                        if (errno != ENOENT)
                                bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
@@ -353,8 +419,7 @@ static void processorstart(struct logdir *ld)
                fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
                xmove_fd(fd, 5);
 
-// getenv("SHELL")?
-               execl(DEFAULT_SHELL, DEFAULT_SHELL_SHORT_NAME, "-c", ld->processor, (char*) NULL);
+               execl(G.shell, G.shell, "-c", ld->processor, (char*) NULL);
                bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
        }
        ld->fnsave[26] = sv_ch; /* ...restore */
@@ -535,12 +600,12 @@ static int buffer_pwrite(int n, char *s, unsigned len)
 
                        while (fchdir(ld->fddir) == -1)
                                pause2cannot("change directory, want remove old logfile",
-                                                        ld->name);
+                                                       ld->name);
                        oldest[0] = 'A';
                        oldest[1] = oldest[27] = '\0';
                        while (!(d = opendir(".")))
                                pause2cannot("open directory, want remove old logfile",
-                                                        ld->name);
+                                                       ld->name);
                        errno = 0;
                        while ((f = readdir(d)))
                                if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
@@ -626,7 +691,7 @@ static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
        }
        ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
        if ((ld->fdlock == -1)
-        || (lock_exnb(ld->fdlock) == -1)
+        || (flock(ld->fdlock, LOCK_EX | LOCK_NB) == -1)
        ) {
                logdir_close(ld);
                warn2("can't lock directory", (char*)fn);
@@ -679,19 +744,14 @@ static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
                                ld->inst = new;
                                break;
                        case 's': {
-                               static const struct suffix_mult km_suffixes[] = {
-                                       { "k", 1024 },
-                                       { "m", 1024*1024 },
-                                       { "", 0 }
-                               };
                                ld->sizemax = xatou_sfx(&s[1], km_suffixes);
                                break;
                        }
                        case 'n':
-                               ld->nmax = xatoi_u(&s[1]);
+                               ld->nmax = xatoi_positive(&s[1]);
                                break;
                        case 'N':
-                               ld->nmin = xatoi_u(&s[1]);
+                               ld->nmin = xatoi_positive(&s[1]);
                                break;
                        case 't': {
                                static const struct suffix_mult mh_suffixes[] = {
@@ -981,7 +1041,7 @@ int svlogd_main(int argc, char **argv)
                        linemax = 256;
        }
        ////if (opt & 8) { // -b
-       ////    buflen = xatoi_u(b);
+       ////    buflen = xatoi_positive(b);
        ////    if (buflen == 0) buflen = 1024;
        ////}
        //if (opt & 0x10) timestamp++; // -t
@@ -1068,7 +1128,8 @@ int svlogd_main(int argc, char **argv)
                /* Search for '\n' (in fact, np already holds the result) */
                linelen = stdin_cnt;
                if (np) {
- print_to_nl:          /* NB: starting from here lineptr may point
+ print_to_nl:
+                       /* NB: starting from here lineptr may point
                         * farther out into line[] */
                        linelen = np - lineptr + 1;
                }
index 11ae39e..0eced29 100644 (file)
@@ -9,7 +9,10 @@ objtree                := $(CURDIR)
 src            := $(srctree)
 obj            := $(objtree)
 
-# Look for make include files relative to root of kernel src
+# Make generated files
+DUMMY := $(shell $(Q)$(srctree)/scripts/gen_build_files.sh $(srctree) $(objtree) >&2)
+
+# Look for make include files relative to root of src
 MAKEFLAGS += --include-dir=$(srctree)
 
 default: busybox
@@ -85,8 +88,8 @@ lib-y:=
 include archival/Kbuild
 lib-all-y += $(patsubst %,archival/%,$(sort $(lib-y)))
 lib-y:=
-include archival/libunarchive/Kbuild
-lib-all-y += $(patsubst %,archival/libunarchive/%,$(sort $(lib-y)))
+include archival/libarchive/Kbuild
+lib-all-y += $(patsubst %,archival/libarchive/%,$(sort $(lib-y)))
 lib-y:=
 include applets/Kbuild
 lib-all-y += $(patsubst %,applets/%,$(sort $(lib-y)))
@@ -121,6 +124,9 @@ lib-y:=
 include miscutils/Kbuild
 lib-all-y += $(patsubst %,miscutils/%,$(sort $(lib-y)))
 lib-y:=
+include mailutils/Kbuild
+lib-all-y += $(patsubst %,mailutils/%,$(sort $(lib-y)))
+lib-y:=
 include coreutils/libcoreutils/Kbuild
 lib-all-y += $(patsubst %,coreutils/libcoreutils/%,$(sort $(lib-y)))
 lib-y:=
@@ -168,7 +174,7 @@ lib-all-y += $(patsubst %,libbb/%,$(sort $(lib-y)))
 lib-y:=
 
 comma:=,
-busybox_unstripped.o: $(usage_stuff) include/applet_tables.h include/autoconf.h
+busybox_unstripped.o: $(usage_stuff) include/applet_tables.h include/NUM_APPLETS.h include/autoconf.h
        $(CC) $(CPPFLAGS) $(CFLAGS) $(EXTRA_CFLAGS) \
                $(patsubst %,-Wl$(comma)%,$(LDFLAGS) $(EXTRA_LDFLAGS)) \
                -DGCC_COMBINE=1 \
@@ -194,14 +200,9 @@ busybox: busybox_unstripped.o
 include/autoconf.h: .config
        $(MAKE) -f $(srctree)/Makefile silentoldconfig
 
+# Override rules for host compile
 applets/usage: include/autoconf.h
-       $(HOSTCC) -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -I$(srctree)/include -o applets/usage applets/usage.c
+       $(HOSTCC) -Wall -O2 -I$(srctree)/include -o applets/usage applets/usage.c
 
 applets/applet_tables: include/autoconf.h
-       $(HOSTCC) -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -I$(srctree)/include -o applets/applet_tables applets/applet_tables.c
-
-include/usage_compressed.h: include/usage.h applets/usage
-       $(srctree)/applets/usage_compressed include/usage_compressed.h applets
-
-include/applet_tables.h: include/applets.h
-       applets/applet_tables include/applet_tables.h
+       $(HOSTCC) -Wall -O2 -I$(srctree)/include -o applets/applet_tables applets/applet_tables.c
index 23bd9ff..2e62850 100644 (file)
@@ -153,4 +153,3 @@ $(host-cshlib): %: $(host-cshobjs) FORCE
 
 targets += $(host-csingle)  $(host-cmulti) $(host-cobjs)\
           $(host-cxxmulti) $(host-cxxobjs) $(host-cshlib) $(host-cshobjs)
-
index be679b6..3e54ea7 100644 (file)
@@ -168,5 +168,3 @@ cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@
 
 quiet_cmd_gzip = GZIP    $@
 cmd_gzip = gzip -f -9 < $< > $@
-
-
index 50ef371..b125698 100644 (file)
@@ -39,6 +39,7 @@
 #include <limits.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <alloca.h>
 
 /* exitstatus is used to keep track of any failing calls to kernel-doc,
  * but execution continues. */
@@ -212,7 +213,7 @@ void find_export_symbols(char * filename)
  * Document all external or internal functions in a file.
  * Call kernel-doc with following parameters:
  * kernel-doc -docbook -nofunction function_name1 filename
- * function names are obtained from all the the src files
+ * function names are obtained from all the src files
  * by find_export_symbols.
  * intfunc uses -nofunction
  * extfunc uses -function
@@ -397,4 +398,3 @@ int main(int argc, char **argv)
        fflush(stdout);
        return exitstatus;
 }
-
index 1a5b10f..165a8c3 100644 (file)
 #include <limits.h>
 #include <ctype.h>
 #include <arpa/inet.h>
+#include <alloca.h>
 
 /* bbox: not needed
 #define INT_CONF ntohl(0x434f4e46)
@@ -329,7 +330,7 @@ void parse_dep_file(void *map, size_t len)
        clear_config();
 
        while (m < end) {
-               while (m < end && (*m == ' ' || *m == '\\' || *m == '\n'))
+               while (m < end && (*m == ' ' || *m == '\\' || *m == '\n' || *m == '\r'))
                        m++;
                p = m;
                while (p < end && *p != ' ') p++;
index 95cbbe6..cb861b8 100755 (executable)
@@ -1,17 +1,20 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 #
 # Copyright 2004 Matt Mackall <mpm@selenic.com>
 #
-# inspired by perl Bloat-O-Meter (c) 1997 by Andi Kleen
+# Inspired by perl Bloat-O-Meter (c) 1997 by Andi Kleen
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
-import sys, os#, re
+import sys, os
 
 def usage():
-    sys.stderr.write("usage: %s [-t] file1 file2\n" % sys.argv[0])
-    sys.exit(-1)
+    sys.stderr.write("usage: %s [-t] file1 file2 [-- <readelf options>]\n"
+                        % sys.argv[0])
+    sys.stderr.write("\t-t\tShow time spent on parsing/processing\n")
+    sys.stderr.write("\t--\tPass additional parameters to readelf\n")
+    sys.exit(1)
 
 f1, f2 = (None, None)
 flag_timing, dashes = (False, False)
@@ -31,6 +34,8 @@ for f in sys.argv[1:]:
             f1 = f
         elif f2 is None:
             f2 = f
+        else:
+            usage()
 if flag_timing:
     import time
 if f1 is None or f2 is None:
@@ -39,31 +44,22 @@ if f1 is None or f2 is None:
 sym_args = " ".join(sys.argv[3 + flag_timing + dashes:])
 def getsizes(file):
     sym, alias, lut = {}, {}, {}
-    #dynsym_filter = re.compile("^\d+:\s+[\dA-Fa-f]+\s+\d+\s+\w+\s+\w+\s+\w+\s+\w+\s+\w+$")
     for l in os.popen("readelf -W -s %s %s" % (sym_args, file)).readlines():
-        if True:
-            l = l.strip()
-            if not (len(l) and l[0].isdigit() and len(l.split()) == 8):
-                continue
-            num, value, size, typ, bind, vis, ndx, name = l.split()
-            if ndx == "UND": continue # skip undefined
-            if typ in ["SECTION", "FILES"]: continue # skip sections and files
-        #else:
-        #    l = l.strip()
-        #    match = dynsym_filter.match(l)
-        #    if not match: continue
-        #    x, value, size, typ, bind, x, ndx, name = l.split()
-        #    if ndx == "UND": continue # skip undefined
-        #    if typ in ["SECTION", "FILES"]: continue # skip sections and files
+        l = l.strip()
+        if not (len(l) and l[0].isdigit() and len(l.split()) == 8):
+            continue
+        num, value, size, typ, bind, vis, ndx, name = l.split()
+        if ndx == "UND": continue # skip undefined
+        if typ in ["SECTION", "FILES"]: continue # skip sections and files
         if "." in name: name = "static." + name.split(".")[0]
         value = int(value, 16)
-        size = int(size)
+        size = int(size, 16) if size.startswith('0x') else int(size)
         if vis != "DEFAULT" and bind != "GLOBAL": # see if it is an alias
             alias[(value, size)] = {"name" : name}
         else:
             sym[name] = {"addr" : value, "size":  size}
             lut[(value, size)] = 0
-    for addr, sz in alias.iterkeys():
+    for addr, sz in iter(alias.keys()):
         # If the non-GLOBAL sym has an implementation elsewhere then
         # it's an alias, disregard it.
         if not (addr, sz) in lut:
@@ -92,7 +88,7 @@ if flag_timing:
 grow, shrink, add, remove, up, down = 0, 0, 0, 0, 0, 0
 delta, common = [], {}
 
-for name in old.iterkeys():
+for name in iter(old.keys()):
     if name in new:
         common[name] = 1
 
index 446152e..00193a8 100755 (executable)
@@ -3,7 +3,7 @@
 # Processes current directory recursively:
 # printf("abc\n") -> puts("abc"). Beware of fprintf etc...
 
-# BTW, gcc 4.1.2 already does tha same! Can't believe it...
+# BTW, gcc 4.1.2 already does the same! Can't believe it...
 
 grep -lr 'printf\([^%%]*\\n"\)' . | grep '.[ch]$' | xargs -n1 \
     sed -e 's/\([^A-Za-z0-9_]\)printf(\( *"[^%]*\)\\n")/\1puts(\2")/' -i
index 3c45e33..cb207ae 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (c) 1991, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Original copyright notice is retained at the end of this file.
  */
diff --git a/scripts/find_stray_empty_lines b/scripts/find_stray_empty_lines
new file mode 100755 (executable)
index 0000000..aae18f9
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+grep -n -B1 -r $'^\t*}$' . | grep -A1 '.[ch]-[0-9]*-$'
+grep -n -A1 -r $'^\t*{$' . | grep -B1 '.[ch]-[0-9]*-$'
+# or (less surefire ones):
+grep -n -B1 -r $'^\t*}' . | grep -A1 '.[ch]-[0-9]*-$'
+grep -n -A1 -r $'^\t*{' . | grep -B1 '.[ch]-[0-9]*-$'
+
+# find trailing empty lines
+find -type f | while read file; do
+        test x"$file" = x"" && continue
+        tail -n1 $file | while read lastline
+        do
+          #echo "|$file|$lastline"
+          if test x"$lastline" = x""; then
+                echo "$file"
+          fi
+        done
+done
index 18c172d..0989b2f 100755 (executable)
@@ -1,5 +1,9 @@
 #!/bin/sh
 
+# Note: was using sed OPTS CMD -- FILES
+# but users complain that many sed implementations
+# are misinterpreting --.
+
 test $# -ge 2 || { echo "Syntax: $0 SRCTREE OBJTREE"; exit 1; }
 
 # cd to objtree
@@ -9,86 +13,78 @@ mkdir include 2>/dev/null
 
 srctree="$1"
 
+status() { printf '  %-8s%s\n' "$1" "$2"; }
+gen() { status "GEN" "$@"; }
+chk() { status "CHK" "$@"; }
+
+generate()
+{
+       # NB: data to be inserted at INSERT line is coming on stdin
+       local src="$1" dst="$2" header="$3"
+       #chk "${dst}"
+       {
+               # Need to use printf: different shells have inconsistent
+               # rules re handling of "\n" in echo params.
+               printf "%s\n" "${header}"
+               # print everything up to INSERT line
+               sed -n '/^INSERT$/ q; p' "${src}"
+               # copy stdin to stdout
+               cat
+               # print everything after INSERT line
+               sed -n '/^INSERT$/ { :l; n; p; bl }' "${src}"
+       } >"${dst}.tmp"
+       if ! cmp -s "${dst}" "${dst}.tmp"; then
+               gen "${dst}"
+               mv "${dst}.tmp" "${dst}"
+       else
+               rm -f "${dst}.tmp"
+       fi
+}
+
 # (Re)generate include/applets.h
-src="$srctree/include/applets.src.h"
-dst="include/applets.h"
-s=`sed -n 's@^//applet:@@p' -- "$srctree"/*/*.c "$srctree"/*/*/*.c`
-old=`cat "$dst" 2>/dev/null`
-# Why "IFS='' read -r REPLY"??
-# This atrocity is needed to read lines without mangling.
-# IFS='' prevents whitespace trimming,
-# -r suppresses backslash handling.
-new=`echo "/* DO NOT EDIT. This file is generated from applets.src.h */"
-while IFS='' read -r REPLY; do
-       test x"$REPLY" = x"INSERT" && REPLY="$s"
-       printf "%s\n" "$REPLY"
-done <"$src"`
-if test x"$new" != x"$old"; then
-       echo "  GEN     $dst"
-       printf "%s\n" "$new" >"$dst"
-fi
+sed -n 's@^//applet:@@p' "$srctree"/*/*.c "$srctree"/*/*/*.c \
+| generate \
+       "$srctree/include/applets.src.h" \
+       "include/applets.h" \
+       "/* DO NOT EDIT. This file is generated from applets.src.h */"
 
 # (Re)generate include/usage.h
-src="$srctree/include/usage.src.h"
-dst="include/usage.h"
 # We add line continuation backslash after each line,
 # and insert empty line before each line which doesn't start
 # with space or tab
-# (note: we need to use \\\\ because of ``)
-s=`sed -n -e 's@^//usage:\([ \t].*\)$@\1 \\\\@p' -e 's@^//usage:\([^ \t].*\)$@\n\1 \\\\@p' -- "$srctree"/*/*.c "$srctree"/*/*/*.c`
-old=`cat "$dst" 2>/dev/null`
-new=`echo "/* DO NOT EDIT. This file is generated from usage.src.h */"
-while IFS='' read -r REPLY; do
-       test x"$REPLY" = x"INSERT" && REPLY="$s"
-       printf "%s\n" "$REPLY"
-done <"$src"`
-if test x"$new" != x"$old"; then
-       echo "  GEN     $dst"
-       printf "%s\n" "$new" >"$dst"
-fi
+sed -n -e 's@^//usage:\([ \t].*\)$@\1 \\@p' -e 's@^//usage:\([^ \t].*\)$@\n\1 \\@p' \
+       "$srctree"/*/*.c "$srctree"/*/*/*.c \
+| generate \
+       "$srctree/include/usage.src.h" \
+       "include/usage.h" \
+       "/* DO NOT EDIT. This file is generated from usage.src.h */"
 
 # (Re)generate */Kbuild and */Config.in
-{ cd -- "$srctree" && find -type d; } | while read -r d; do
+# We skip .dotdirs - makes git/svn/etc users happier
+{ cd -- "$srctree" && find . -type d -not '(' -name '.?*' -prune ')'; } \
+| while read -r d; do
        d="${d#./}"
 
        src="$srctree/$d/Kbuild.src"
        dst="$d/Kbuild"
        if test -f "$src"; then
                mkdir -p -- "$d" 2>/dev/null
-               #echo "  CHK     $dst"
-
-               s=`sed -n 's@^//kbuild:@@p' -- "$srctree/$d"/*.c`
 
-               old=`cat "$dst" 2>/dev/null`
-               new=`echo "# DO NOT EDIT. This file is generated from Kbuild.src"
-               while IFS='' read -r REPLY; do
-                       test x"$REPLY" = x"INSERT" && REPLY="$s"
-                       printf "%s\n" "$REPLY"
-               done <"$src"`
-               if test x"$new" != x"$old"; then
-                       echo "  GEN     $dst"
-                       printf "%s\n" "$new" >"$dst"
-               fi
+               sed -n 's@^//kbuild:@@p' "$srctree/$d"/*.c \
+               | generate \
+                       "${src}" "${dst}" \
+                       "# DO NOT EDIT. This file is generated from Kbuild.src"
        fi
 
        src="$srctree/$d/Config.src"
        dst="$d/Config.in"
        if test -f "$src"; then
                mkdir -p -- "$d" 2>/dev/null
-               #echo "  CHK     $dst"
-
-               s=`sed -n 's@^//config:@@p' -- "$srctree/$d"/*.c`
 
-               old=`cat "$dst" 2>/dev/null`
-               new=`echo "# DO NOT EDIT. This file is generated from Config.src"
-               while IFS='' read -r REPLY; do
-                       test x"$REPLY" = x"INSERT" && REPLY="$s"
-                       printf "%s\n" "$REPLY"
-               done <"$src"`
-               if test x"$new" != x"$old"; then
-                       echo "  GEN     $dst"
-                       printf "%s\n" "$new" >"$dst"
-               fi
+               sed -n 's@^//config:@@p' "$srctree/$d"/*.c \
+               | generate \
+                       "${src}" "${dst}" \
+                       "# DO NOT EDIT. This file is generated from Config.src"
        fi
 done
 
diff --git a/scripts/individual b/scripts/individual
deleted file mode 100755 (executable)
index e93ca55..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-#!/bin/sh
-
-# Compile individual versions of each busybox applet.
-
-if [ $# -eq 0 ]
-then
-
-# Clear out the build directory.  (Make clean should do this instead of here.)
-
-rm -rf build
-mkdir build
-
-# Make our prerequisites.
-
-make busybox.links include/bb_config.h $(pwd)/{libbb/libbb.a,archival/libunarchive/libunarchive.a,coreutils/libcoreutils/libcoreutils.a,networking/libiproute/libiproute.a}
-
-else
-# Could very well be that we want to build an individual applet but have no
-# 'build' dir yet..
-
-test -d ./build || mkdir build
-
-fi
-
-# About 3/5 of the applets build from one .c file (with the same name as the
-# corresponding applet), and all it needs to link against.  However, to build
-# them all we need more than that.
-
-# Figure out which applets need extra libraries added to their command line.
-
-function substithing()
-{
-  if [ "${1/ $3 //}" != "$1" ]
-  then
-    echo $2
-  fi
-}
-
-function extra_libraries()
-{
-  # gzip needs gunzip.c (when gunzip is enabled, anyway).
-  substithing " gzip " "archival/gunzip.c archival/uncompress.c" "$1"
-
-  # init needs init_shared.c
-  substithing " init " "init/init_shared.c" "$1"
-
-  # ifconfig needs interface.c
-  substithing " ifconfig " "networking/interface.c" "$1"
-
-  # Applets that need libunarchive.a
-  substithing " ar bunzip2 unlzma cpio dpkg gunzip rpm2cpio rpm tar uncompress unzip dpkg_deb gzip " "archival/libunarchive/libunarchive.a" "$1"
-
-  # Applets that need libcoreutils.a
-  substithing " cp mv " "coreutils/libcoreutils/libcoreutils.a" "$1"
-
-  # Applets that need libiproute.a
-  substithing " ip " "networking/libiproute/libiproute.a" "$1"
-
-  # What needs -libm?
-  substithing " awk dc " "-lm" "$1"
-
-  # What needs -lcrypt?
-  substithing " httpd vlock " "-lcrypt" "$1"
-}
-
-# Query applets.h to figure out which applets need special treatment
-
-strange_names=`sed -rn -e 's/\#.*//' -e 's/.*APPLET_NOUSAGE\(([^,]*),([^,]*),.*/\1 \2/p' -e 's/.*APPLET_ODDNAME\(([^,]*),([^,]*),.*, *([^)]*).*/\1 \2@\3/p' include/applets.h`
-
-function bonkname()
-{
-  while [ $# -gt 0 ]
-  do
-    if [ "$APPLET" == "$1" ]
-    then
-      APPFILT="${2/@*/}"
-      if [ "${APPFILT}" == "$2" ]
-      then
-        HELPNAME='"nousage\n"'   # These should be _fixed_.
-      else
-        HELPNAME="${2/*@/}"_full_usage
-      fi
-      break
-    fi
-    shift 2
-  done
-#echo APPLET=${APPLET} APPFILT=${APPFILT} HELPNAME=${HELPNAME} 2=${2}
-}
-
-# Iterate through every name in busybox.links
-
-function buildit ()
-{
-  export APPLET="$1"
-  export APPFILT=${APPLET}
-  export HELPNAME=${APPLET}_full_usage
-
-  bonkname $strange_names
-
-  j=`find archival console-tools coreutils debianutils editors findutils init loginutils miscutils modutils networking procps shell sysklogd util-linux -name "${APPFILT}.c"`
-  if [ -z "$j" ]
-  then
-    echo no file for $APPLET
-  else
-    echo "Building $APPLET"
-    gcc -Os -o build/$APPLET applets/individual.c $j \
-       `extra_libraries $APPFILT` libbb/libbb.a -Iinclude \
-       -DBUILD_INDIVIDUAL \
-       '-Drun_applet_and_exit(...)' '-Dfind_applet_by_name(...)=0' \
-       -DAPPLET_main=${APPFILT}_main -DAPPLET_full_usage=${HELPNAME}
-    if [ $? -ne 0 ];
-    then
-      echo "Failed $APPLET"
-    fi
-  fi
-}
-
-if [ $# -eq 0 ]
-then
-  for APPLET in `sed 's .*/  ' busybox.links`
-  do
-    buildit "$APPLET"
-  done
-else
-  buildit "$1"
-fi
-
-
-strip build/*
index b5708e2..38bae80 100644 (file)
@@ -87,7 +87,7 @@ endif
        $(MTIME_IS_COARSE) && sleep 1
 
 %_defconfig: $(obj)/conf
-       $(Q)$< -D $@ Config.in
+       $(Q)$< -D configs/$@ Config.in
        $(MTIME_IS_COARSE) && sleep 1
 
 # Help text used by make help
@@ -150,7 +150,7 @@ HOSTCFLAGS_zconf.tab.o      := -I$(src)
 HOSTLOADLIBES_qconf    = $(KC_QT_LIBS) -ldl
 HOSTCXXFLAGS_qconf.o   = $(KC_QT_CFLAGS) -D LKC_DIRECT_LINK
 
-HOSTLOADLIBES_gconf    = `pkg-config --libs gtk+-2.0 gmodule-2.0 libglade-2.0`
+HOSTLOADLIBES_gconf    = `pkg-config --libs gtk+-2.0 gmodule-2.0 libglade-2.0` -ldl
 HOSTCFLAGS_gconf.o     = `pkg-config --cflags gtk+-2.0 gmodule-2.0 libglade-2.0` \
                           -D LKC_DIRECT_LINK
 
index fa59cbf..15fc294 100755 (executable)
@@ -11,4 +11,3 @@ EOF
 if [ ! "$?" -eq "0"  ]; then
        echo -DKBUILD_NO_NLS;
 fi
-
index 9befa2b..ea2446a 100644 (file)
@@ -3,6 +3,8 @@
  * Released under the terms of the GNU GPL v2.0.
  */
 
+#define _XOPEN_SOURCE 700
+
 #include <ctype.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -171,7 +173,7 @@ static void conf_askvalue(struct symbol *sym, const char *def)
 int conf_string(struct menu *menu)
 {
        struct symbol *sym = menu->sym;
-       const char *def, *help;
+       const char *def;
 
        while (1) {
                printf("%*s%s ", indent - 1, "", menu->prompt->text);
@@ -186,10 +188,7 @@ int conf_string(struct menu *menu)
                case '?':
                        /* print help */
                        if (line[1] == '\n') {
-                               help = nohelp_text;
-                               if (menu->sym->help)
-                                       help = menu->sym->help;
-                               printf("\n%s\n", menu->sym->help);
+                               printf("\n%s\n", menu->sym->help ? menu->sym->help : nohelp_text);
                                def = NULL;
                                break;
                        }
@@ -205,7 +204,6 @@ int conf_string(struct menu *menu)
 static int conf_sym(struct menu *menu)
 {
        struct symbol *sym = menu->sym;
-       int type;
        tristate oldval, newval;
        const char *help;
 
@@ -213,7 +211,6 @@ static int conf_sym(struct menu *menu)
                printf("%*s%s ", indent - 1, "", menu->prompt->text);
                if (sym->name)
                        printf("(%s) ", sym->name);
-               type = sym_get_type(sym);
                putchar('[');
                oldval = sym_get_tristate_value(sym);
                switch (oldval) {
@@ -280,11 +277,9 @@ static int conf_choice(struct menu *menu)
 {
        struct symbol *sym, *def_sym;
        struct menu *child;
-       int type;
        bool is_new;
 
        sym = menu->sym;
-       type = sym_get_type(sym);
        is_new = !sym_has_value(sym);
        if (sym_is_changable(sym)) {
                conf_sym(menu);
index bd2d70e..303df0b 100644 (file)
@@ -474,7 +474,11 @@ int conf_write(const char *name)
                                                fprintf(out_h, "#define CONFIG_%s 1\n", sym->name);
                                                /* bbox */
                                                fprintf(out_h, "#define ENABLE_%s 1\n", sym->name);
-                                               fprintf(out_h, "#define IF_%s(...) __VA_ARGS__\n", sym->name);
+                                               fprintf(out_h, "#ifdef MAKE_SUID\n");
+                                               fprintf(out_h, "# define IF_%s(...) __VA_ARGS__ \"CONFIG_%s\"\n", sym->name, sym->name);
+                                               fprintf(out_h, "#else\n");
+                                               fprintf(out_h, "# define IF_%s(...) __VA_ARGS__\n", sym->name);
+                                               fprintf(out_h, "#endif\n");
                                                fprintf(out_h, "#define IF_NOT_%s(...)\n", sym->name);
                                        }
                                        break;
@@ -506,7 +510,11 @@ int conf_write(const char *name)
                                        fputs("\"\n", out_h);
                                        /* bbox */
                                        fprintf(out_h, "#define ENABLE_%s 1\n", sym->name);
-                                       fprintf(out_h, "#define IF_%s(...) __VA_ARGS__\n", sym->name);
+                                       fprintf(out_h, "#ifdef MAKE_SUID\n");
+                                       fprintf(out_h, "# define IF_%s(...) __VA_ARGS__ \"CONFIG_%s\"\n", sym->name, sym->name);
+                                       fprintf(out_h, "#else\n");
+                                       fprintf(out_h, "# define IF_%s(...) __VA_ARGS__\n", sym->name);
+                                       fprintf(out_h, "#endif\n");
                                        fprintf(out_h, "#define IF_NOT_%s(...)\n", sym->name);
                                }
                                break;
@@ -518,7 +526,11 @@ int conf_write(const char *name)
                                                fprintf(out_h, "#define CONFIG_%s 0x%s\n", sym->name, str);
                                                /* bbox */
                                                fprintf(out_h, "#define ENABLE_%s 1\n", sym->name);
-                                               fprintf(out_h, "#define IF_%s(...) __VA_ARGS__\n", sym->name);
+                                               fprintf(out_h, "#ifdef MAKE_SUID\n");
+                                               fprintf(out_h, "# define IF_%s(...) __VA_ARGS__ \"CONFIG_%s\"\n", sym->name, sym->name);
+                                               fprintf(out_h, "#else\n");
+                                               fprintf(out_h, "# define IF_%s(...) __VA_ARGS__\n", sym->name);
+                                               fprintf(out_h, "#endif\n");
                                                fprintf(out_h, "#define IF_NOT_%s(...)\n", sym->name);
                                        }
                                        break;
@@ -532,7 +544,11 @@ int conf_write(const char *name)
                                        fprintf(out_h, "#define CONFIG_%s %s\n", sym->name, str);
                                        /* bbox */
                                        fprintf(out_h, "#define ENABLE_%s 1\n", sym->name);
-                                       fprintf(out_h, "#define IF_%s(...) __VA_ARGS__\n", sym->name);
+                                       fprintf(out_h, "#ifdef MAKE_SUID\n");
+                                       fprintf(out_h, "# define IF_%s(...) __VA_ARGS__ \"CONFIG_%s\"\n", sym->name, sym->name);
+                                       fprintf(out_h, "#else\n");
+                                       fprintf(out_h, "# define IF_%s(...) __VA_ARGS__\n", sym->name);
+                                       fprintf(out_h, "#endif\n");
                                        fprintf(out_h, "#define IF_NOT_%s(...)\n", sym->name);
                                }
                                break;
index 5fc323d..51f15e1 100644 (file)
@@ -2235,13 +2235,14 @@ static void zconf_endhelp(void)
  */
 FILE *zconf_fopen(const char *name)
 {
-       char *env, fullname[PATH_MAX+1];
+       char *env;
        FILE *f;
 
        f = fopen(name, "r");
        if (!f && name[0] != '/') {
                env = getenv(SRCTREE);
                if (env) {
+                       char *fullname = alloca(strlen(env) + strlen(name) + 2);
                        sprintf(fullname, "%s/%s", env, name);
                        f = fopen(fullname, "r");
                }
@@ -2322,4 +2323,3 @@ char *zconf_curname(void)
 {
        return current_pos.file ? current_pos.file->name : "<none>";
 }
-
index 77848bb..de4ae41 100644 (file)
@@ -38,11 +38,8 @@ int dialog_textbox(const char *title, const char *file, int height, int width)
 {
        int i, x, y, cur_x, cur_y, fpos, key = 0;
        int passed_end;
-       char search_term[MAX_LEN + 1];
        WINDOW *dialog, *text;
 
-       search_term[0] = '\0';  /* no search term entered yet */
-
        /* Open input file for reading */
        if ((fd = open(file, O_RDONLY)) == -1) {
                endwin();
@@ -437,7 +434,6 @@ static void print_page(WINDOW * win, int height, int width)
  */
 static void print_line(WINDOW * win, int row, int width)
 {
-       int y, x;
        char *line;
 
        line = get_line();
@@ -446,11 +442,13 @@ static void print_line(WINDOW * win, int row, int width)
        waddch(win, ' ');
        waddnstr(win, line, MIN(strlen(line), width - 2));
 
-       getyx(win, y, x);
        /* Clear 'residue' of previous line */
 #if OLD_NCURSES
        {
                int i;
+               int y, x;
+
+               getyx(win, y, x);
                for (i = 0; i < width - x; i++)
                        waddch(win, ' ');
        }
index 0c548bf..006d037 100644 (file)
@@ -8,6 +8,10 @@
  * i18n, 2005, Arnaldo Carvalho de Melo <acme@conectiva.com.br>
  */
 
+#define _XOPEN_SOURCE 700
+/* On Darwin, this may be needed to get SIGWINCH: */
+#define _DARWIN_C_SOURCE 1
+
 #include <sys/ioctl.h>
 #include <sys/wait.h>
 #include <ctype.h>
@@ -18,6 +22,7 @@
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
+#include <strings.h> /* for strcasecmp */
 #include <termios.h>
 #include <unistd.h>
 #include <locale.h>
@@ -256,7 +261,7 @@ search_help[] = N_(
        "          USB$ => find all CONFIG_ symbols ending with USB\n"
        "\n");
 
-static char buf[4096], *bufptr = buf;
+static char buf[4096*10], *bufptr = buf;
 static char input_buf[4096];
 static const char filename[] = ".config";
 static char *args[1024], **argptr = args;
@@ -440,6 +445,7 @@ static struct gstr get_relations_str(struct symbol **sym_arr)
 
 pid_t pid;
 
+#ifdef SIGWINCH
 static void winch_handler(int sig)
 {
        if (!do_resize) {
@@ -447,11 +453,11 @@ static void winch_handler(int sig)
                do_resize = 1;
        }
 }
+#endif
 
 static int exec_conf(void)
 {
        int pipefd[2], stat, size;
-       struct sigaction sa;
        sigset_t sset, osset;
 
        sigemptyset(&sset);
@@ -460,10 +466,15 @@ static int exec_conf(void)
 
        signal(SIGINT, SIG_DFL);
 
-       sa.sa_handler = winch_handler;
-       sigemptyset(&sa.sa_mask);
-       sa.sa_flags = SA_RESTART;
-       sigaction(SIGWINCH, &sa, NULL);
+#ifdef SIGWINCH
+       {
+               struct sigaction sa;
+               sa.sa_handler = winch_handler;
+               sigemptyset(&sa.sa_mask);
+               sa.sa_flags = SA_RESTART;
+               sigaction(SIGWINCH, &sa, NULL);
+       }
+#endif
 
        *argptr++ = NULL;
 
index 0fce20c..14cf2ea 100644 (file)
@@ -394,4 +394,3 @@ struct menu *menu_get_parent_menu(struct menu *menu)
        }
        return menu;
 }
-
index ef4c832..2630919 100644 (file)
@@ -112,4 +112,3 @@ const char *str_get(struct gstr *gs)
 {
        return gs->s;
 }
-
index 345f0fc..d39cf18 100644 (file)
@@ -228,4 +228,3 @@ kconf_id_lookup (register const char *str, register unsigned int len)
     }
   return 0;
 }
-
index b62724d..a27d256 100644 (file)
@@ -2169,5 +2169,3 @@ void zconfdump(FILE *out)
 #include "expr.c"
 #include "symbol.c"
 #include "menu.c"
-
-
index 2007a4e..bef5e92 100644 (file)
@@ -473,7 +473,7 @@ void conf_parse(const char *name)
        menu_finalize(&rootmenu);
        for_all_symbols(i, sym) {
                sym_check_deps(sym);
-        }
+       }
 
        sym_change_count = 1;
 }
index ef6ae8a..6a26fe1 100755 (executable)
 #      - Retain lines that begin with "# CONFIG_"
 #      - lines that use double-quotes must \\-escape-quote them
 
-config="$1"
-if [ $# -lt 1 ]
-then
-       config=.config
-fi
+config=.config
 
+{
 echo "\
 #ifndef _BBCONFIGOPTS_H
 #define _BBCONFIGOPTS_H
 /*
  * busybox configuration settings.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * This file is generated automatically by scripts/mkconfigs.
  * Do not edit.
  */
-static const char bbconfig_config[] ="
+static const char bbconfig_config[] ALIGN1 ="
 
-sed 's/\"/\\\"/g' $config | grep "^#\? \?CONFIG_" | awk '{print "\"" $0 "\\n\"";}'
+grep -e '^# CONFIG_' -e '^CONFIG_' "$config" \
+| sed -e 's/\"/\\\"/g' -e 's/^/"/' -e 's/$/\\n"/'
 
 echo ";"
-echo "#endif /* _BBCONFIGOPTS_H */"
+echo "#endif"
+} >"$1"
+
+{
+echo "\
+#ifndef _BBCONFIGOPTS_BZ2_H
+#define _BBCONFIGOPTS_BZ2_H
+/*
+ * busybox configuration settings.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * This file is generated automatically by scripts/mkconfigs.
+ * Do not edit.
+ */
+static const char bbconfig_config_bz2[] ALIGN1 = {"
+
+grep -e '^# CONFIG_' -e '^CONFIG_' "$config" \
+| bzip2 -1 | dd bs=2 skip=1 2>/dev/null \
+| od -v -b \
+| sed -e 's/^[^ ]*//' \
+        -e 's/ //g' \
+        -e '/^$/d' \
+        -e 's/\(...\)/0\1,/g'
+
+echo "};"
+echo "#endif"
+} >"$2"
index a6ec5e6..3074748 100755 (executable)
@@ -1,28 +1,35 @@
 #!/bin/sh
 
+usage() {
+       echo "Usage: ${0##*/} DIR1 DIR2"
+       echo
+       echo "Compares all object files recursivelty found in DIR1 and DIR2."
+       echo "Prints diff of their disassembly."
+       echo
+       exit $1
+}
+
 filter() {
        # sed removes " address: " prefixes which mess up diff
        sed $'s/^\\(\t*\\)[ ]*[0-9a-f][0-9a-f]*:[ \t]*/\\1/' \
        | sed 's/__GI_//g'
 }
 
-test -d "$1" || exit 1
-test -d "$2" || exit 1
+test -d "$1" || usage 1
+test -d "$2" || usage 1
 
 {
        (
                cd "$1" || exit 1
-               find -name '*.o' -o -name '*.os' # -o -name '*.so'
+               find -name '*.o' -o -name '*.os' # -o -name '*.so'
        )
        (
                cd "$2" || exit 1
-               find -name '*.o' -o -name '*.os' # -o -name '*.so'
+               find -name '*.o' -o -name '*.os' # -o -name '*.so'
        )
 } | sed 's:^\./::' | sort | uniq | \
-tee LST | \
 (
-IFS=''
-while read -r oname; do
+while IFS='' read -r oname; do
        if ! test -f "$1/$oname"; then
                echo "Only $2/$oname"
                continue
@@ -32,8 +39,8 @@ while read -r oname; do
                continue
        fi
        diff -q -- "$1/$oname" "$2/$oname" >/dev/null && continue
-       (cd "$1"; objdump -dr "$oname" | filter >"$oname.disasm")
-       (cd "$2"; objdump -dr "$oname" | filter >"$oname.disasm")
-       diff -u "$1/$oname.disasm" "$2/$oname.disasm"
+       (cd "$1" && { size "$oname"; objdump -dr "$oname" | filter; } >"$oname.disasm")
+       (cd "$2" && { size "$oname"; objdump -dr "$oname" | filter; } >"$oname.disasm")
+       diff -u -- "$1/$oname.disasm" "$2/$oname.disasm"
 done
 )
index 7f9d544..9fc51a7 100755 (executable)
@@ -31,6 +31,9 @@ all:
 
 Makefile:;
 
-\$(filter-out all Makefile,\$(MAKECMDGOALS)) %/:
+\$(filter-out all Makefile,\$(MAKECMDGOALS)):
+       \$(MAKE) -C \$(KERNELSRC) O=\$(KERNELOUTPUT) \$@
+
+%/:
        \$(MAKE) -C \$(KERNELSRC) O=\$(KERNELOUTPUT) \$@
 EOF
index a102593..d2b26bc 100755 (executable)
@@ -65,6 +65,7 @@ if test x"$LIBC" = x"uclibc"; then
        | grep -v CONFIG_BUILD_LIBBUSYBOX \
        | grep -v CONFIG_PIE \
        \
+       | grep -v CONFIG_FEATURE_TOUCH_NODEREF \
        | grep -v CONFIG_FEATURE_2_4_MODULES \
        >.config.new
        mv .config.new .config
@@ -72,6 +73,7 @@ if test x"$LIBC" = x"uclibc"; then
        echo '# CONFIG_BUILD_LIBBUSYBOX is not set' >>.config
        echo '# CONFIG_PIE is not set' >>.config
        echo '# CONFIG_FEATURE_2_4_MODULES is not set' >>.config
+       echo '# CONFIG_FEATURE_TOUCH_NODEREF is not set' >>.config
 fi
 
 # If STATIC, remove some things.
index 311536d..758a8e8 100755 (executable)
@@ -1,10 +1,12 @@
 #!/bin/sh
 
+run_testsuite=true
+
 test -d "$1" || { echo "'$1' is not a directory"; exit 1; }
 test -x "$1/scripts/randomtest" || { echo "No scripts/randomtest in '$1'"; exit 1; }
 
 export LIBC="uclibc"
-export CROSS_COMPILER_PREFIX="i486-linux-uclibc-"
+export CROSS_COMPILER_PREFIX="i686-"
 export MAKEOPTS="-j9"
 
 cnt=0
@@ -21,7 +23,9 @@ while sleep 1; do
                echo "Failed build in: failed.$dir"
                exit 1 # you may comment this out...
                let fail++
-       else
+               continue
+       fi
+       if $run_testsuite; then
                (
                        cd -- "$dir/testsuite" || exit 1
                        echo "Running testsuite in $dir..."
@@ -29,10 +33,12 @@ while sleep 1; do
                )
                if test $? != 0; then
                        echo "Failed runtest in $dir"
-                       exit 1
+                       exit 1 # you may comment this out...
+                       let fail++
+                       continue
                fi
                tail -n10 -- "$dir/testsuite/runtest.log"
-               rm -rf -- "$dir"
        fi
+       rm -rf -- "$dir"
        let cnt++
 done
index 0464426..b61ab98 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 # Copyright 2006 Rob Landley <rob@landley.net>
-# Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+# Licensed under GPLv2 or later, see file LICENSE in this source tree.
 
 # Dumb little utility function to print out the assembly dump of a single
 # function, or list the functions so dumpable in an executable.  You'd think
@@ -18,4 +18,3 @@ then
 fi
 
 objdump -d $1 | sed -n -e '/./{H;$!d}' -e "x;/^.[0-9a-fA-F]* <$2>:/p"
-
index 5994a75..e471699 100755 (executable)
@@ -117,7 +117,7 @@ LDLIBS=`echo "$LDLIBS" | xargs -n1 | sort | uniq | xargs`
 # First link with all libs. If it fails, bail out
 echo "Trying libraries: $LDLIBS"
 # "lib1 lib2 lib3" -> "-llib1 -llib2 -llib3"
-l_list=`echo "$LDLIBS" | sed -e 's/ / -l/g' -e 's/^/-l/' -e 's/^-l$//'`
+l_list=`echo " $LDLIBS " | sed -e 's: \([^- ][^ ]*\): -l\1:g'`
 test "x$l_list" != "x" && l_list="$START_GROUP $l_list $END_GROUP"
 try $CC $CFLAGS $LDFLAGS \
        -o $EXE \
@@ -141,7 +141,7 @@ while test "$LDLIBS"; do
     for one in $LDLIBS; do
        without_one=`echo " $LDLIBS " | sed "s/ $one / /g" | xargs`
        # "lib1 lib2 lib3" -> "-llib1 -llib2 -llib3"
-       l_list=`echo "$without_one" | sed -e 's/ / -l/g' -e 's/^/-l/' -e 's/^-l$//'`
+       l_list=`echo " $without_one " | sed -e 's: \([^- ][^ ]*\): -l\1:g'`
        test x"$l_list" != x"" && l_list="$START_GROUP $l_list $END_GROUP"
        $debug && echo "Trying -l options: '$l_list'"
        try $CC $CFLAGS $LDFLAGS \
@@ -172,7 +172,7 @@ done
 
 # Make the binary with final, minimal list of libs
 echo "Final link with: ${LDLIBS:-<none>}"
-l_list=`echo "$LDLIBS" | sed -e 's/ / -l/g' -e 's/^/-l/' -e 's/^-l$//'`
+l_list=`echo " $LDLIBS " | sed -e 's: \([^- ][^ ]*\): -l\1:g'`
 test "x$l_list" != "x" && l_list="$START_GROUP $l_list $END_GROUP"
 # --verbose gives us gobs of info to stdout (e.g. linker script used)
 if ! test -f busybox_ldscript; then
@@ -255,6 +255,7 @@ if test "$CONFIG_FEATURE_SHARED_BUSYBOX" = y; then
            $GC_SECTIONS \
            $START_GROUP $O_FILES $END_GROUP \
            -L"$sharedlib_dir" -lbusybox \
+           $l_list \
            $INFO_OPTS \
     || {
        echo "Linking $EXE failed"
index 64a9920..47d15b6 100644 (file)
@@ -122,4 +122,3 @@ config SESTATUS
          Displays the status of SELinux.
 
 endmenu
-
index 6567e3c..cdd5f2a 100644 (file)
@@ -3,7 +3,7 @@
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 # Copyright (C) 2007 by KaiGai Kohei <kaigai@kaigai.gr.jp>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
index c6be37f..f947c2c 100644 (file)
@@ -5,9 +5,41 @@
  *
  * Copyright (C) 2006 - 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
-#include <getopt.h>
+
+//usage:#define chcon_trivial_usage
+//usage:       "[OPTIONS] CONTEXT FILE..."
+//usage:       "\n     chcon [OPTIONS] [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE..."
+//usage:       IF_FEATURE_CHCON_LONG_OPTIONS(
+//usage:       "\n     chcon [OPTIONS] --reference=RFILE FILE..."
+//usage:       )
+//usage:#define chcon_full_usage "\n\n"
+//usage:       "Change the security context of each FILE to CONTEXT\n"
+//usage:       IF_FEATURE_CHCON_LONG_OPTIONS(
+//usage:     "\n       -v,--verbose            Verbose"
+//usage:     "\n       -c,--changes            Report changes made"
+//usage:     "\n       -h,--no-dereference     Affect symlinks instead of their targets"
+//usage:     "\n       -f,--silent,--quiet     Suppress most error messages"
+//usage:     "\n       --reference=RFILE       Use RFILE's group instead of using a CONTEXT value"
+//usage:     "\n       -u,--user=USER          Set user/role/type/range in the target"
+//usage:     "\n       -r,--role=ROLE          security context"
+//usage:     "\n       -t,--type=TYPE"
+//usage:     "\n       -l,--range=RANGE"
+//usage:     "\n       -R,--recursive          Recurse"
+//usage:       )
+//usage:       IF_NOT_FEATURE_CHCON_LONG_OPTIONS(
+//usage:     "\n       -v      Verbose"
+//usage:     "\n       -c      Report changes made"
+//usage:     "\n       -h      Affect symlinks instead of their targets"
+//usage:     "\n       -f      Suppress most error messages"
+//usage:     "\n       -u USER Set user/role/type/range in the target security context"
+//usage:     "\n       -r ROLE"
+//usage:     "\n       -t TYPE"
+//usage:     "\n       -l RNG"
+//usage:     "\n       -R      Recurse"
+//usage:       )
+
 #include <selinux/context.h>
 
 #include "libbb.h"
@@ -60,7 +92,7 @@ static int FAST_FUNC change_filedir_context(
 
        if (specified_context == NULL) {
                context = set_security_context_component(file_context,
-                                                        user, role, type, range);
+                                                       user, role, type, range);
                if (!context) {
                        bb_error_msg("can't compute security context from %s", file_context);
                        goto skip;
@@ -89,15 +121,15 @@ static int FAST_FUNC change_filedir_context(
                }
                if ((option_mask32 & OPT_VERBOSE) || ((option_mask32 & OPT_CHANHES) && !fail)) {
                        printf(!fail
-                              ? "context of %s changed to %s\n"
-                              : "can't change context of %s to %s\n",
-                              fname, context_string);
+                               ? "context of %s changed to %s\n"
+                               : "can't change context of %s to %s\n",
+                               fname, context_string);
                }
                if (!fail) {
                        rc = TRUE;
                } else if ((option_mask32 & OPT_QUIET) == 0) {
                        bb_error_msg("can't change context of %s to %s",
-                                    fname, context_string);
+                                       fname, context_string);
                }
        } else if (option_mask32 & OPT_VERBOSE) {
                printf("context of %s retained as %s\n", fname, context_string);
@@ -149,7 +181,7 @@ int chcon_main(int argc UNUSED_PARAM, char **argv)
 #if ENABLE_FEATURE_CHCON_LONG_OPTIONS
        if (option_mask32 & OPT_REFERENCE) {
                /* FIXME: lgetfilecon() should be used when '-h' is specified.
-                  But current implementation follows the original one. */
+                * But current implementation follows the original one. */
                if (getfilecon(reference_file, &specified_context) < 0)
                        bb_perror_msg_and_die("getfilecon('%s') failed", reference_file);
        } else
@@ -169,10 +201,10 @@ int chcon_main(int argc UNUSED_PARAM, char **argv)
                fname[fname_len] = '\0';
 
                if (recursive_action(fname,
-                                    1<<option_mask32 & OPT_RECURSIVE,
-                                    change_filedir_context,
-                                    change_filedir_context,
-                                    NULL, 0) != TRUE)
+                                       1<<option_mask32 & OPT_RECURSIVE,
+                                       change_filedir_context,
+                                       change_filedir_context,
+                                       NULL, 0) != TRUE)
                        errors = 1;
        }
        return errors;
index 3d3eef1..56611d6 100644 (file)
@@ -4,9 +4,12 @@
  * Based on libselinux 1.33.1
  * Port to BusyBox  Hiroshi Shinji <shiroshi@my.email.ne.jp>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#define getenforce_trivial_usage NOUSAGE_STR
+//usage:#define getenforce_full_usage ""
+
 #include "libbb.h"
 
 int getenforce_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
index 7478b79..e8f0fef 100644 (file)
@@ -4,9 +4,14 @@
  * Based on libselinux 1.33.1
  * Port to BusyBox  Hiroshi Shinji <shiroshi@my.email.ne.jp>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#define getsebool_trivial_usage
+//usage:       "-a or getsebool boolean..."
+//usage:#define getsebool_full_usage "\n\n"
+//usage:       "       -a      Show all selinux booleans"
+
 #include "libbb.h"
 
 int getsebool_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
index ea7c913..ce139db 100644 (file)
@@ -2,8 +2,12 @@
  * load_policy
  * Author: Yuichi Nakamura <ynakam@hitachisoft.jp>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define load_policy_trivial_usage NOUSAGE_STR
+//usage:#define load_policy_full_usage ""
+
 #include "libbb.h"
 
 int load_policy_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
index 1532429..9e5728e 100644 (file)
@@ -3,8 +3,18 @@
  *                  based on libselinux-1.32
  * Port to busybox: KaiGai Kohei <kaigai@kaigai.gr.jp>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define matchpathcon_trivial_usage
+//usage:       "[-n] [-N] [-f file_contexts_file] [-p prefix] [-V]"
+//usage:#define matchpathcon_full_usage "\n\n"
+//usage:       "       -n      Don't display path"
+//usage:     "\n       -N      Don't use translations"
+//usage:     "\n       -f      Use alternate file_context file"
+//usage:     "\n       -p      Use prefix to speed translations"
+//usage:     "\n       -V      Verify file context on disk matches defaults"
+
 #include "libbb.h"
 
 static int print_matchpathcon(char *path, int noprint)
index f8ca9a6..27f2be3 100644 (file)
  * Port to busybox: KaiGai Kohei <kaigai@kaigai.gr.jp>
  *                  - based on coreutils-5.97 (in Fedora Core 6)
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
-#include <getopt.h>
+
+//usage:#define runcon_trivial_usage
+//usage:       "[-c] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] PROG ARGS\n"
+//usage:       "runcon CONTEXT PROG ARGS"
+//usage:#define runcon_full_usage "\n\n"
+//usage:       "Run PROG in a different security context\n"
+//usage:     "\n       CONTEXT         Complete security context\n"
+//usage:       IF_FEATURE_RUNCON_LONG_OPTIONS(
+//usage:     "\n       -c,--compute    Compute process transition context before modifying"
+//usage:     "\n       -t,--type=TYPE  Type (for same role as parent)"
+//usage:     "\n       -u,--user=USER  User identity"
+//usage:     "\n       -r,--role=ROLE  Role"
+//usage:     "\n       -l,--range=RNG  Levelrange"
+//usage:       )
+//usage:       IF_NOT_FEATURE_RUNCON_LONG_OPTIONS(
+//usage:     "\n       -c      Compute process transition context before modifying"
+//usage:     "\n       -t TYPE Type (for same role as parent)"
+//usage:     "\n       -u USER User identity"
+//usage:     "\n       -r ROLE Role"
+//usage:     "\n       -l RNG  Levelrange"
+//usage:       )
+
 #include <selinux/context.h>
 #include <selinux/flask.h>
 
 #include "libbb.h"
 
 static context_t runcon_compute_new_context(char *user, char *role, char *type, char *range,
-                                           char *command, int compute_trans)
+                       char *command, int compute_trans)
 {
        context_t con;
        security_context_t cur_context;
@@ -48,9 +69,9 @@ static context_t runcon_compute_new_context(char *user, char *role, char *type,
 
                if (getfilecon(command, &file_context) < 0)
                        bb_error_msg_and_die("can't retrieve attributes of '%s'",
-                                            command);
+                                       command);
                if (security_compute_create(cur_context, file_context,
-                                           SECCLASS_PROCESS, &new_context))
+                                       SECCLASS_PROCESS, &new_context))
                        bb_error_msg_and_die("unable to compute a new context");
                cur_context = new_context;
        }
@@ -126,12 +147,11 @@ int runcon_main(int argc UNUSED_PARAM, char **argv)
 
        if (security_check_context(context_str(con)))
                bb_error_msg_and_die("'%s' is not a valid context",
-                                    context_str(con));
+                               context_str(con));
 
        if (setexeccon(context_str(con)))
                bb_error_msg_and_die("can't set up security context '%s'",
-                                    context_str(con));
+                               context_str(con));
 
-       execvp(argv[0], argv);
-       bb_perror_msg_and_die("can't execute '%s'", argv[0]);
+       BB_EXECVP_or_die(argv);
 }
index 1cf93c3..ce830dc 100644 (file)
@@ -4,8 +4,12 @@
  * Based on libselinux 1.33.1
  * Port to BusyBox  Hiroshi Shinji <shiroshi@my.email.ne.jp>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define selinuxenabled_trivial_usage NOUSAGE_STR
+//usage:#define selinuxenabled_full_usage ""
+
 #include "libbb.h"
 
 int selinuxenabled_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
index 1a02a6b..e594318 100644 (file)
@@ -5,17 +5,23 @@
  *
  * Copyright (C) KaiGai Kohei <kaigai@ak.jp.nec.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#define sestatus_trivial_usage
+//usage:       "[-vb]"
+//usage:#define sestatus_full_usage "\n\n"
+//usage:       "       -v      Verbose"
+//usage:     "\n       -b      Display current state of booleans"
+
 #include "libbb.h"
 
 extern char *selinux_mnt;
 
-#define OPT_VERBOSE    (1 << 0)
-#define OPT_BOOLEAN    (1 << 1)
+#define OPT_VERBOSE  (1 << 0)
+#define OPT_BOOLEAN  (1 << 1)
 
-#define COL_FMT                "%-31s "
+#define COL_FMT  "%-31s "
 
 static void display_boolean(void)
 {
@@ -35,7 +41,7 @@ static void display_boolean(void)
                if (pending < 0)
                        goto skip;
                printf(COL_FMT "%s",
-                      bools[i], active == 0 ? "off" : "on");
+                               bools[i], active == 0 ? "off" : "on");
                if (active != pending)
                        printf(" (%sactivate pending)", pending == 0 ? "in" : "");
                bb_putchar('\n');
@@ -151,7 +157,7 @@ int sestatus_main(int argc UNUSED_PARAM, char **argv)
        const char *pol_path;
        int rc;
 
-       opt_complementary = "?0";       /* no arguments are required. */
+       opt_complementary = "?0";  /* no arguments are required. */
        opts = getopt32(argv, "vb");
 
        /* SELinux status: line */
index 45f8223..c5bc0a5 100644 (file)
@@ -4,9 +4,13 @@
  * Based on libselinux 1.33.1
  * Port to BusyBox  Hiroshi Shinji <shiroshi@my.email.ne.jp>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#define setenforce_trivial_usage
+//usage:       "[Enforcing | Permissive | 1 | 0]"
+//usage:#define setenforce_full_usage ""
+
 #include "libbb.h"
 
 /* These strings are arranged so that odd ones
index f45e41b..0173db9 100644 (file)
@@ -4,6 +4,46 @@
   Port to BusyBox (c) 2007 by Yuichi Nakamura <ynakam@hitachisoft.jp>
 */
 
+//usage:#define setfiles_trivial_usage
+//usage:       "[-dnpqsvW] [-e DIR]... [-o FILE] [-r alt_root_path]"
+//usage:       IF_FEATURE_SETFILES_CHECK_OPTION(
+//usage:       " [-c policyfile] spec_file"
+//usage:       )
+//usage:       " pathname"
+//usage:#define setfiles_full_usage "\n\n"
+//usage:       "Reset file contexts under pathname according to spec_file\n"
+//usage:       IF_FEATURE_SETFILES_CHECK_OPTION(
+//usage:     "\n       -c FILE Check the validity of the contexts against the specified binary policy"
+//usage:       )
+//usage:     "\n       -d      Show which specification matched each file"
+//usage:     "\n       -l      Log changes in file labels to syslog"
+//usage:     "\n       -n      Don't change any file labels"
+//usage:     "\n       -q      Suppress warnings"
+//usage:     "\n       -r DIR  Use an alternate root path"
+//usage:     "\n       -e DIR  Exclude DIR"
+//usage:     "\n       -F      Force reset of context to match file_context for customizable files"
+//usage:     "\n       -o FILE Save list of files with incorrect context"
+//usage:     "\n       -s      Take a list of files from stdin (instead of command line)"
+//usage:     "\n       -v      Show changes in file labels, if type or role are changing"
+//usage:     "\n       -vv     Show changes in file labels, if type, role, or user are changing"
+//usage:     "\n       -W      Display warnings about entries that had no matching files"
+//usage:
+//usage:#define restorecon_trivial_usage
+//usage:       "[-iFnRv] [-e EXCLUDEDIR]... [-o FILE] [-f FILE]"
+//usage:#define restorecon_full_usage "\n\n"
+//usage:       "Reset security contexts of files in pathname\n"
+//usage:     "\n       -i      Ignore files that don't exist"
+//usage:     "\n       -f FILE File with list of files to process"
+//usage:     "\n       -e DIR  Directory to exclude"
+//usage:     "\n       -R,-r   Recurse"
+//usage:     "\n       -n      Don't change any file labels"
+//usage:     "\n       -o FILE Save list of files with incorrect context"
+//usage:     "\n       -v      Verbose"
+//usage:     "\n       -vv     Show changed labels"
+//usage:     "\n       -F      Force reset of context to match file_context"
+//usage:     "\n               for customizable files, or the user section,"
+//usage:     "\n               if it has changed"
+
 #include "libbb.h"
 #if ENABLE_FEATURE_SETFILES_CHECK_OPTION
 #include <sepol/sepol.h>
@@ -118,7 +158,6 @@ static void add_exclude(const char *directory)
 
        if (directory == NULL || directory[0] != '/') {
                bb_error_msg_and_die("full path required for exclude: %s", directory);
-
        }
        if (lstat(directory, &sb)) {
                bb_error_msg("directory \"%s\" not found, ignoring", directory);
@@ -459,10 +498,11 @@ static int process_one(char *name)
 
        if (S_ISDIR(sb.st_mode) && recurse) {
                if (recursive_action(name,
-                                    ACTION_RECURSE,
-                                    apply_spec,
-                                    apply_spec,
-                                    NULL, 0) != TRUE) {
+                               ACTION_RECURSE,
+                               apply_spec,
+                               apply_spec,
+                               NULL, 0) != TRUE
+               ) {
                        bb_error_msg("error while labeling %s", name);
                        goto err;
                }
@@ -545,7 +585,7 @@ int setfiles_main(int argc UNUSED_PARAM, char **argv)
                flags = getopt32(argv, "de:f:ilnpqr:svo:FW"
                                IF_FEATURE_SETFILES_CHECK_OPTION("c:"),
                        &exclude_dir, &input_filename, &rootpath, &out_filename,
-                                IF_FEATURE_SETFILES_CHECK_OPTION(&policyfile,)
+                               IF_FEATURE_SETFILES_CHECK_OPTION(&policyfile,)
                        &verbose);
        }
        argv += optind;
@@ -561,8 +601,8 @@ int setfiles_main(int argc UNUSED_PARAM, char **argv)
                fclose(policystream);
 
                /* Only process the specified file_contexts file, not
-                  any .homedirs or .local files, and do not perform
-                  context translations. */
+                * any .homedirs or .local files, and do not perform
+                * context translations. */
                set_matchpathcon_flags(MATCHPATHCON_BASEONLY |
                                       MATCHPATHCON_NOTRANS |
                                       MATCHPATHCON_VALIDATE);
@@ -592,8 +632,8 @@ int setfiles_main(int argc UNUSED_PARAM, char **argv)
 
        if (applet_name[0] == 's') { /* setfiles */
                /* Use our own invalid context checking function so that
-                  we can support either checking against the active policy or
-                  checking against a binary policy file. */
+                * we can support either checking against the active policy or
+                * checking against a binary policy file. */
                set_matchpathcon_canoncon(&canoncon);
                if (!argv[0])
                        bb_show_usage();
index b615ce7..ec682e5 100644 (file)
@@ -5,9 +5,14 @@
  * omitted in this version
  * Yuichi Nakamura <ynakam@hitachisoft.jp>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#define setsebool_trivial_usage
+//usage:       "boolean value"
+//usage:#define setsebool_full_usage "\n\n"
+//usage:       "Change boolean setting"
+
 #include "libbb.h"
 
 int setsebool_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
index 8009119..b31e62d 100644 (file)
@@ -7,215 +7,6 @@ menu "Shells"
 
 INSERT
 
-config ASH
-       bool "ash"
-       default y
-       depends on !NOMMU
-       help
-         Tha 'ash' shell adds about 60k in the default configuration and is
-         the most complete and most pedantically correct shell included with
-         busybox. This shell is actually a derivative of the Debian 'dash'
-         shell (by Herbert Xu), which was created by porting the 'ash' shell
-         (written by Kenneth Almquist) from NetBSD.
-
-config ASH_BASH_COMPAT
-       bool "bash-compatible extensions"
-       default y
-       depends on ASH
-       help
-         Enable bash-compatible extensions.
-
-config ASH_JOB_CONTROL
-       bool "Job control"
-       default y
-       depends on ASH
-       help
-         Enable job control in the ash shell.
-
-config ASH_ALIAS
-       bool "alias support"
-       default y
-       depends on ASH
-       help
-         Enable alias support in the ash shell.
-
-config ASH_GETOPTS
-       bool "Builtin getopt to parse positional parameters"
-       default y
-       depends on ASH
-       help
-         Enable getopts builtin in the ash shell.
-
-config ASH_BUILTIN_ECHO
-       bool "Builtin version of 'echo'"
-       default y
-       depends on ASH
-       help
-         Enable support for echo, builtin to ash.
-
-config ASH_BUILTIN_PRINTF
-       bool "Builtin version of 'printf'"
-       default y
-       depends on ASH
-       help
-         Enable support for printf, builtin to ash.
-
-config ASH_BUILTIN_TEST
-       bool "Builtin version of 'test'"
-       default y
-       depends on ASH
-       help
-         Enable support for test, builtin to ash.
-
-config ASH_CMDCMD
-       bool "'command' command to override shell builtins"
-       default y
-       depends on ASH
-       help
-         Enable support for the ash 'command' builtin, which allows
-         you to run the specified command with the specified arguments,
-         even when there is an ash builtin command with the same name.
-
-config ASH_MAIL
-       bool "Check for new mail on interactive shells"
-       default n
-       depends on ASH
-       help
-         Enable "check for new mail" in the ash shell.
-
-config ASH_OPTIMIZE_FOR_SIZE
-       bool "Optimize for size instead of speed"
-       default y
-       depends on ASH
-       help
-         Compile ash for reduced size at the price of speed.
-
-config ASH_RANDOM_SUPPORT
-       bool "Pseudorandom generator and $RANDOM variable"
-       default y
-       depends on ASH
-       help
-         Enable pseudorandom generator and dynamic variable "$RANDOM".
-         Each read of "$RANDOM" will generate a new pseudorandom value.
-         You can reset the generator by using a specified start value.
-         After "unset RANDOM" the generator will switch off and this
-         variable will no longer have special treatment.
-
-config ASH_EXPAND_PRMT
-       bool "Expand prompt string"
-       default y
-       depends on ASH
-       help
-         "PS#" may contain volatile content, such as backquote commands.
-         This option recreates the prompt string from the environment
-         variable each time it is displayed.
-
-config HUSH
-       bool "hush"
-       default y
-       help
-         hush is a small shell (22k). It handles the normal flow control
-         constructs such as if/then/elif/else/fi, for/in/do/done, while loops,
-         case/esac. Redirections, here documents, $((arithmetic))
-         and functions are supported.
-
-         It will compile and work on no-mmu systems.
-
-         It does not handle select, aliases, brace expansion,
-         tilde expansion, &>file and >&file redirection of stdout+stderr.
-
-config HUSH_BASH_COMPAT
-       bool "bash-compatible extensions"
-       default y
-       depends on HUSH
-       help
-         Enable bash-compatible extensions.
-
-config HUSH_HELP
-       bool "help builtin"
-       default y
-       depends on HUSH
-       help
-         Enable help builtin in hush. Code size + ~1 kbyte.
-
-config HUSH_INTERACTIVE
-       bool "Interactive mode"
-       default y
-       depends on HUSH
-       help
-         Enable interactive mode (prompt and command editing).
-         Without this, hush simply reads and executes commands
-         from stdin just like a shell script from a file.
-         No prompt, no PS1/PS2 magic shell variables.
-
-config HUSH_JOB
-       bool "Job control"
-       default y
-       depends on HUSH_INTERACTIVE
-       help
-         Enable job control: Ctrl-Z backgrounds, Ctrl-C interrupts current
-         command (not entire shell), fg/bg builtins work. Without this option,
-         "cmd &" still works by simply spawning a process and immediately
-         prompting for next command (or executing next command in a script),
-         but no separate process group is formed.
-
-config HUSH_TICK
-       bool "Process substitution"
-       default y
-       depends on HUSH
-       help
-         Enable process substitution `command` and $(command) in hush.
-
-config HUSH_IF
-       bool "Support if/then/elif/else/fi"
-       default y
-       depends on HUSH
-       help
-         Enable if/then/elif/else/fi in hush.
-
-config HUSH_LOOPS
-       bool "Support for, while and until loops"
-       default y
-       depends on HUSH
-       help
-         Enable for, while and until loops in hush.
-
-config HUSH_CASE
-       bool "Support case ... esac statement"
-       default y
-       depends on HUSH
-       help
-         Enable case ... esac statement in hush. +400 bytes.
-
-config HUSH_FUNCTIONS
-       bool "Support funcname() { commands; } syntax"
-       default y
-       depends on HUSH
-       help
-         Enable support for shell functions in hush. +800 bytes.
-
-config HUSH_LOCAL
-       bool "Support local builtin"
-       default y
-       depends on HUSH_FUNCTIONS
-       help
-         Enable support for local variables in functions.
-
-config HUSH_EXPORT_N
-       bool "Support export '-n' option"
-       default y
-       depends on HUSH
-       help
-         Enable support for export '-n' option in hush. It is a bash extension.
-
-config HUSH_RANDOM_SUPPORT
-       bool "Pseudorandom generator and $RANDOM variable"
-       default y
-       depends on HUSH
-       help
-         Enable pseudorandom generator and dynamic variable "$RANDOM".
-         Each read of "$RANDOM" will generate a new pseudorandom value.
-
 
 choice
        prompt "Choose which shell is aliased to 'sh' name"
@@ -271,29 +62,6 @@ config FEATURE_BASH_IS_NONE
 endchoice
 
 
-config LASH
-       bool "lash (deprecated: aliased to hush)"
-       default n
-       select HUSH
-       help
-         lash is deprecated and will be removed, please migrate to hush.
-
-config MSH
-       bool "msh (deprecated: please use hush)"
-       default n
-       select HUSH
-       help
-         msh is deprecated and will be removed, please migrate to hush.
-         If there is a feature msh has but hush does not, please let us know.
-
-#        The minix shell (adds just 30k) is quite complete and handles things
-#        like for/do/done, case/esac and all the things you expect a Bourne
-#        shell to do. It is not always pedantically correct about Bourne
-#        shell grammar (try running the shell testscript "tests/sh.testcases"
-#        on it and compare vs bash) but for most things it works quite well.
-#        It uses only vfork, so it can be used on uClinux systems.
-
-
 config SH_MATH_SUPPORT
        bool "POSIX math support"
        default y
@@ -355,9 +123,9 @@ config FEATURE_SH_NOFORK
        default n
        depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS
        help
-         This option causes busybox shells [currently only ash]
-         to not execute typical fork/exec/wait sequence, but call <applet>_main
-         directly, if possible. (Sometimes it is not possible: for example,
+         This option causes busybox shells to not execute typical
+         fork/exec/wait sequence, but call <applet>_main directly,
+         if possible. (Sometimes it is not possible: for example,
          this is not possible in pipes).
 
          This will be done only for some applets (those which are marked
@@ -365,37 +133,17 @@ config FEATURE_SH_NOFORK
 
          This may significantly speed up some shell scripts.
 
-         This feature is relatively new. Use with care.
+         This feature is relatively new. Use with care. Report bugs
+         to project mailing list.
 
-config CTTYHACK
-       bool "cttyhack"
+config FEATURE_SH_HISTFILESIZE
+       bool "Use $HISTFILESIZE"
        default y
+       depends on HUSH || ASH
        help
-         One common problem reported on the mailing list is "can't access tty;
-         job control turned off" error message which typically appears when
-         one tries to use shell with stdin/stdout opened to /dev/console.
-         This device is special - it cannot be a controlling tty.
-
-         Proper solution is to use correct device instead of /dev/console.
-
-         cttyhack provides "quick and dirty" solution to this problem.
-         It analyzes stdin with various ioctls, trying to determine whether
-         it is a /dev/ttyN or /dev/ttySN (virtual terminal or serial line).
-         If it detects one, it closes stdin/out/err and reopens that device.
-         Then it executes given program. Opening the device will make
-         that device a controlling tty. This may require cttyhack
-         to be a session leader.
-
-         Example for /etc/inittab (for busybox init):
-
-         ::respawn:/bin/cttyhack /bin/sh
-
-         Giving controlling tty to shell running with PID 1:
-
-         $ exec cttyhack sh
-
-         Starting an interactive shell from boot shell script:
+         This option makes busybox shells to use $HISTFILESIZE variable
+         to set shell history size. Note that its max value is capped
+         by "History size" setting in library tuning section.
 
-         setsid cttyhack sh
 
 endmenu
index d76b353..c00aec9 100644 (file)
@@ -2,16 +2,10 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
 INSERT
 
-lib-$(CONFIG_ASH)      += ash.o ash_ptr_hack.o shell_common.o
-lib-$(CONFIG_HUSH)     += hush.o match.o shell_common.o
-lib-$(CONFIG_CTTYHACK) += cttyhack.o
-
 lib-$(CONFIG_SH_MATH_SUPPORT) += math.o
-lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
-lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
index 0337a55..71ef9a6 100644 (file)
@@ -13,7 +13,7 @@
  * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
  * was re-ported from NetBSD and debianized.
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*
@@ -23,8 +23,9 @@
  *      define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
  *      define DEBUG=2 to compile in and turn on debugging.
  *
- * When debugging is on, debugging info will be written to ./trace and
- * a quit signal will generate a core dump.
+ * When debugging is on (DEBUG is 1 and "set -o debug" was executed),
+ * debugging info will be written to ./trace and a quit signal
+ * will generate a core dump.
  */
 #define DEBUG 0
 /* Tweak debug output verbosity here */
 
 #define JOBS ENABLE_ASH_JOB_CONTROL
 
-#include "busybox.h" /* for applet_names */
 #include <paths.h>
 #include <setjmp.h>
 #include <fnmatch.h>
 #include <sys/times.h>
 
+#include "busybox.h" /* for applet_names */
+#include "unicode.h"
+
 #include "shell_common.h"
-#include "math.h"
+#if ENABLE_SH_MATH_SUPPORT
+# include "math.h"
+#endif
 #if ENABLE_ASH_RANDOM_SUPPORT
 # include "random.h"
 #else
 # error "Do not even bother, ash will not run on NOMMU machine"
 #endif
 
+//config:config ASH
+//config:      bool "ash"
+//config:      default y
+//config:      depends on !NOMMU
+//config:      help
+//config:        Tha 'ash' shell adds about 60k in the default configuration and is
+//config:        the most complete and most pedantically correct shell included with
+//config:        busybox. This shell is actually a derivative of the Debian 'dash'
+//config:        shell (by Herbert Xu), which was created by porting the 'ash' shell
+//config:        (written by Kenneth Almquist) from NetBSD.
+//config:
+//config:config ASH_BASH_COMPAT
+//config:      bool "bash-compatible extensions"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable bash-compatible extensions.
+//config:
+//config:config ASH_IDLE_TIMEOUT
+//config:      bool "Idle timeout variable"
+//config:      default n
+//config:      depends on ASH
+//config:      help
+//config:        Enables bash-like auto-logout after $TMOUT seconds of idle time.
+//config:
+//config:config ASH_JOB_CONTROL
+//config:      bool "Job control"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable job control in the ash shell.
+//config:
+//config:config ASH_ALIAS
+//config:      bool "Alias support"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable alias support in the ash shell.
+//config:
+//config:config ASH_GETOPTS
+//config:      bool "Builtin getopt to parse positional parameters"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable support for getopts builtin in ash.
+//config:
+//config:config ASH_BUILTIN_ECHO
+//config:      bool "Builtin version of 'echo'"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable support for echo builtin in ash.
+//config:
+//config:config ASH_BUILTIN_PRINTF
+//config:      bool "Builtin version of 'printf'"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable support for printf builtin in ash.
+//config:
+//config:config ASH_BUILTIN_TEST
+//config:      bool "Builtin version of 'test'"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable support for test builtin in ash.
+//config:
+//config:config ASH_CMDCMD
+//config:      bool "'command' command to override shell builtins"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable support for the ash 'command' builtin, which allows
+//config:        you to run the specified command with the specified arguments,
+//config:        even when there is an ash builtin command with the same name.
+//config:
+//config:config ASH_MAIL
+//config:      bool "Check for new mail on interactive shells"
+//config:      default n
+//config:      depends on ASH
+//config:      help
+//config:        Enable "check for new mail" function in the ash shell.
+//config:
+//config:config ASH_OPTIMIZE_FOR_SIZE
+//config:      bool "Optimize for size instead of speed"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Compile ash for reduced size at the price of speed.
+//config:
+//config:config ASH_RANDOM_SUPPORT
+//config:      bool "Pseudorandom generator and $RANDOM variable"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable pseudorandom generator and dynamic variable "$RANDOM".
+//config:        Each read of "$RANDOM" will generate a new pseudorandom value.
+//config:        You can reset the generator by using a specified start value.
+//config:        After "unset RANDOM" the generator will switch off and this
+//config:        variable will no longer have special treatment.
+//config:
+//config:config ASH_EXPAND_PRMT
+//config:      bool "Expand prompt string"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        "PS#" may contain volatile content, such as backquote commands.
+//config:        This option recreates the prompt string from the environment
+//config:        variable each time it is displayed.
+//config:
+
+//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
+//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
+//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash))
+
+//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
+//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
+
 
 /* ============ Hash table sizes. Configurable. */
 
@@ -262,6 +385,9 @@ static void trace_vprintf(const char *fmt, va_list va);
 /* ============ Utility functions */
 #define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
 
+#define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
+#define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
+
 static int isdigit_str9(const char *str)
 {
        int maxlen = 9 + 1; /* max 9 digits: 999999999 */
@@ -280,6 +406,9 @@ static const char *var_end(const char *var)
 
 
 /* ============ Interrupts / exceptions */
+
+static void exitshell(void) NORETURN;
+
 /*
  * These macros allow the user to suspend the handling of interrupt signals
  * over a period of time.  This is similar to SIGHOLD or to sigblock, but
@@ -835,7 +964,8 @@ sharg(union node *arg, FILE *fp)
        for (p = arg->narg.text; *p; p++) {
                switch ((unsigned char)*p) {
                case CTLESC:
-                       putc(*++p, fp);
+                       p++;
+                       putc(*p, fp);
                        break;
                case CTLVAR:
                        putc('$', fp);
@@ -844,8 +974,10 @@ sharg(union node *arg, FILE *fp)
                        if (subtype == VSLENGTH)
                                putc('#', fp);
 
-                       while (*p != '=')
-                               putc(*p++, fp);
+                       while (*p != '=') {
+                               putc(*p, fp);
+                               p++;
+                       }
 
                        if (subtype & VSNUL)
                                putc(':', fp);
@@ -1757,7 +1889,9 @@ change_lc_ctype(const char *value)
 #endif
 #if ENABLE_ASH_MAIL
 static void chkmail(void);
-static void changemail(const char *) FAST_FUNC;
+static void changemail(const char *var_value) FAST_FUNC;
+#else
+# define chkmail()  ((void)0)
 #endif
 static void changepath(const char *) FAST_FUNC;
 #if ENABLE_ASH_RANDOM_SUPPORT
@@ -1769,6 +1903,10 @@ static const struct {
        const char *var_text;
        void (*var_func)(const char *) FAST_FUNC;
 } varinit_data[] = {
+       /*
+        * Note: VEXPORT would not work correctly here for NOFORK applets:
+        * some environment strings may be constant.
+        */
        { VSTRFIXED|VTEXTFIXED       , defifsvar   , NULL            },
 #if ENABLE_ASH_MAIL
        { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL"      , changemail      },
@@ -1864,10 +2002,6 @@ extern struct globals_var *const ash_ptr_to_globals_var;
 # define optindval()    (voptind.var_text + 7)
 #endif
 
-
-#define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
-#define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
-
 #if ENABLE_ASH_GETOPTS
 static void FAST_FUNC
 getoptsreset(const char *value)
@@ -1878,25 +2012,6 @@ getoptsreset(const char *value)
 #endif
 
 /*
- * Return of a legal variable name (a letter or underscore followed by zero or
- * more letters, underscores, and digits).
- */
-static char* FAST_FUNC
-endofname(const char *name)
-{
-       char *p;
-
-       p = (char *) name;
-       if (!is_name(*p))
-               return p;
-       while (*++p) {
-               if (!is_in_name(*p))
-                       break;
-       }
-       return p;
-}
-
-/*
  * Compares two strings up to the first = or '\0'.  The first
  * string must be terminated by '='; the second may be terminated by
  * either '=' or '\0'.
@@ -2077,9 +2192,10 @@ setvareq(char *s, int flags)
 static void
 setvar(const char *name, const char *val, int flags)
 {
-       char *p, *q;
-       size_t namelen;
+       const char *q;
+       char *p;
        char *nameeq;
+       size_t namelen;
        size_t vallen;
 
        q = endofname(name);
@@ -2093,12 +2209,13 @@ setvar(const char *name, const char *val, int flags)
        } else {
                vallen = strlen(val);
        }
+
        INT_OFF;
        nameeq = ckmalloc(namelen + vallen + 2);
-       p = (char *)memcpy(nameeq, name, namelen) + namelen;
+       p = memcpy(nameeq, name, namelen) + namelen;
        if (val) {
                *p++ = '=';
-               p = (char *)memcpy(p, val, vallen) + vallen;
+               p = memcpy(p, val, vallen) + vallen;
        }
        *p = '\0';
        setvareq(nameeq, flags | VNOSAVE);
@@ -2169,7 +2286,7 @@ unsetvar(const char *s)
                        free(vp);
                        INT_ON;
                } else {
-                       setvar(s, 0, 0);
+                       setvar2(s, 0);
                        vp->flags &= ~VEXPORT;
                }
  ok:
@@ -2312,12 +2429,13 @@ static const char *expandstr(const char *ps);
 #endif
 
 static void
-setprompt(int whichprompt)
+setprompt_if(smallint do_set, int whichprompt)
 {
        const char *prompt;
-#if ENABLE_ASH_EXPAND_PRMT
-       struct stackmark smark;
-#endif
+       IF_ASH_EXPAND_PRMT(struct stackmark smark;)
+
+       if (!do_set)
+               return;
 
        needprompt = 0;
 
@@ -3360,13 +3478,18 @@ setsignal(int signo)
        switch (new_act) {
        case S_CATCH:
                act.sa_handler = signal_handler;
-               act.sa_flags = 0; /* matters only if !DFL and !IGN */
-               sigfillset(&act.sa_mask); /* ditto */
                break;
        case S_IGN:
                act.sa_handler = SIG_IGN;
                break;
        }
+
+       /* flags and mask matter only if !DFL and !IGN, but we do it
+        * for all cases for more deterministic behavior:
+        */
+       act.sa_flags = 0;
+       sigfillset(&act.sa_mask);
+
        sigaction_set(signo, &act);
 
        *t = new_act;
@@ -3403,12 +3526,12 @@ set_curjob(struct job *jp, unsigned mode)
 
        /* first remove from list */
        jpp = curp = &curjob;
-       do {
+       while (1) {
                jp1 = *jpp;
                if (jp1 == jp)
                        break;
                jpp = &jp1->prev_job;
-       } while (1);
+       }
        *jpp = jp1->prev_job;
 
        /* Then re-insert in correct position */
@@ -3423,15 +3546,16 @@ set_curjob(struct job *jp, unsigned mode)
                break;
        case CUR_RUNNING:
                /* newly created job or backgrounded job,
-                  put after all stopped jobs. */
-               do {
+                * put after all stopped jobs.
+                */
+               while (1) {
                        jp1 = *jpp;
 #if JOBS
                        if (!jp1 || jp1->state != JOBSTOPPED)
 #endif
                                break;
                        jpp = &jp1->prev_job;
-               } while (1);
+               }
                /* FALLTHROUGH */
 #if JOBS
        case CUR_STOPPED:
@@ -3604,7 +3728,7 @@ setjobctl(int on)
                        goto out;
                /* fd is a tty at this point */
                close_on_exec_on(fd);
-               do { /* while we are in the background */
+               while (1) { /* while we are in the background */
                        pgrp = tcgetpgrp(fd);
                        if (pgrp < 0) {
  out:
@@ -3615,7 +3739,7 @@ setjobctl(int on)
                        if (pgrp == getpgrp())
                                break;
                        killpg(0, SIGTTIN);
-               } while (1);
+               }
                initialpgrp = pgrp;
 
                setsignal(SIGTSTP);
@@ -3647,18 +3771,51 @@ setjobctl(int on)
 static int FAST_FUNC
 killcmd(int argc, char **argv)
 {
-       int i = 1;
        if (argv[1] && strcmp(argv[1], "-l") != 0) {
+               int i = 1;
                do {
                        if (argv[i][0] == '%') {
-                               struct job *jp = getjob(argv[i], 0);
-                               unsigned pid = jp->ps[0].ps_pid;
-                               /* Enough space for ' -NNN<nul>' */
-                               argv[i] = alloca(sizeof(int)*3 + 3);
-                               /* kill_main has matching code to expect
-                                * leading space. Needed to not confuse
-                                * negative pids with "kill -SIGNAL_NO" syntax */
-                               sprintf(argv[i], " -%u", pid);
+                               /*
+                                * "kill %N" - job kill
+                                * Converting to pgrp / pid kill
+                                */
+                               struct job *jp;
+                               char *dst;
+                               int j, n;
+
+                               jp = getjob(argv[i], 0);
+                               /*
+                                * In jobs started under job control, we signal
+                                * entire process group by kill -PGRP_ID.
+                                * This happens, f.e., in interactive shell.
+                                *
+                                * Otherwise, we signal each child via
+                                * kill PID1 PID2 PID3.
+                                * Testcases:
+                                * sh -c 'sleep 1|sleep 1 & kill %1'
+                                * sh -c 'true|sleep 2 & sleep 1; kill %1'
+                                * sh -c 'true|sleep 1 & sleep 2; kill %1'
+                                */
+                               n = jp->nprocs; /* can't be 0 (I hope) */
+                               if (jp->jobctl)
+                                       n = 1;
+                               dst = alloca(n * sizeof(int)*4);
+                               argv[i] = dst;
+                               for (j = 0; j < n; j++) {
+                                       struct procstat *ps = &jp->ps[j];
+                                       /* Skip non-running and not-stopped members
+                                        * (i.e. dead members) of the job
+                                        */
+                                       if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status))
+                                               continue;
+                                       /*
+                                        * kill_main has matching code to expect
+                                        * leading space. Needed to not confuse
+                                        * negative pids with "kill -SIGNAL_NO" syntax
+                                        */
+                                       dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
+                               }
+                               *dst = '\0';
                        }
                } while (argv[++i]);
        }
@@ -3756,6 +3913,7 @@ sprint_status(char *s, int status, int sigonly)
 #endif
                }
                st &= 0x7f;
+//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata
                col = fmtstr(s, 32, strsignal(st));
                if (WCOREDUMP(status)) {
                        col += fmtstr(s + col, 16, " (core dumped)");
@@ -4090,8 +4248,9 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
                                        break;
                                job = job->prev_job;
                        }
-               } else
+               } else {
                        job = getjob(*argv, 0);
+               }
                /* loop until process terminated or stopped */
                while (job->state == JOBRUNNING)
                        blocking_wait_with_raise_on_sig();
@@ -4515,6 +4674,7 @@ clear_traps(void)
                        INT_ON;
                }
        }
+       may_have_traps = 0;
 }
 
 /* Lives far away from here, needed for forkchild */
@@ -4586,7 +4746,7 @@ forkchild(struct job *jp, union node *n, int mode)
 #if JOBS
        /* do job control only in root shell */
        doing_jobctl = 0;
-       if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
+       if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
                pid_t pgrp;
 
                if (jp->nprocs == 0)
@@ -4612,7 +4772,7 @@ forkchild(struct job *jp, union node *n, int mode)
                                ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
                }
        }
-       if (!oldlvl) {
+       if (oldlvl == 0) {
                if (iflag) { /* why if iflag only? */
                        setsignal(SIGINT);
                        setsignal(SIGTERM);
@@ -4819,6 +4979,8 @@ stoppedjobs(void)
  * Code for dealing with input/output redirection.
  */
 
+#undef EMPTY
+#undef CLOSED
 #define EMPTY -2                /* marks an unused slot in redirtab */
 #define CLOSED -3               /* marks a slot of previously-closed fd */
 
@@ -4870,9 +5032,13 @@ noclobberopen(const char *fname)
         * 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)
+       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);
@@ -4927,15 +5093,14 @@ openredirect(union node *redir)
        char *fname;
        int f;
 
+       fname = redir->nfile.expfname;
        switch (redir->nfile.type) {
        case NFROM:
-               fname = redir->nfile.expfname;
                f = open(fname, O_RDONLY);
                if (f < 0)
                        goto eopen;
                break;
        case NFROMTO:
-               fname = redir->nfile.expfname;
                f = open(fname, O_RDWR|O_CREAT, 0666);
                if (f < 0)
                        goto ecreate;
@@ -4946,7 +5111,6 @@ openredirect(union node *redir)
 #endif
                /* Take care of noclobber mode. */
                if (Cflag) {
-                       fname = redir->nfile.expfname;
                        f = noclobberopen(fname);
                        if (f < 0)
                                goto ecreate;
@@ -4954,13 +5118,11 @@ openredirect(union node *redir)
                }
                /* FALLTHROUGH */
        case NCLOBBER:
-               fname = redir->nfile.expfname;
                f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
                if (f < 0)
                        goto ecreate;
                break;
        case NAPPEND:
-               fname = redir->nfile.expfname;
                f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
                if (f < 0)
                        goto ecreate;
@@ -5315,25 +5477,17 @@ redirectsafe(union node *redir, int flags)
 static arith_t
 ash_arith(const char *s)
 {
-       arith_eval_hooks_t math_hooks;
+       arith_state_t math_state;
        arith_t result;
-       int errcode = 0;
 
-       math_hooks.lookupvar = lookupvar;
-       math_hooks.setvar    = setvar2;
-       math_hooks.endofname = endofname;
+       math_state.lookupvar = lookupvar;
+       math_state.setvar    = setvar2;
+       //math_state.endofname = endofname;
 
        INT_OFF;
-       result = arith(s, &errcode, &math_hooks);
-       if (errcode < 0) {
-               if (errcode == -3)
-                       ash_msg_and_raise_error("exponent less than 0");
-               if (errcode == -2)
-                       ash_msg_and_raise_error("divide by zero");
-               if (errcode == -5)
-                       ash_msg_and_raise_error("expression recursion loop detected");
-               raise_error_syntax(s);
-       }
+       result = arith(&math_state, s);
+       if (math_state.errmsg)
+               ash_msg_and_raise_error(math_state.errmsg);
        INT_ON;
 
        return result;
@@ -5391,13 +5545,18 @@ static struct arglist exparg;
 /*
  * Our own itoa().
  */
+#if !ENABLE_SH_MATH_SUPPORT
+/* 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(32, expdest);
-       len = fmtstr(expdest, 32, arith_t_fmt, num);
+       len = fmtstr(expdest, 32, ARITH_FMT, num);
        STADJUST(len, expdest);
        return len;
 }
@@ -5568,7 +5727,7 @@ removerecordregions(int endoff)
                return;
 
        if (ifsfirst.endoff > endoff) {
-               while (ifsfirst.next != NULL) {
+               while (ifsfirst.next) {
                        struct ifsregion *ifsp;
                        INT_OFF;
                        ifsp = ifsfirst.next->next;
@@ -5576,9 +5735,9 @@ removerecordregions(int endoff)
                        ifsfirst.next = ifsp;
                        INT_ON;
                }
-               if (ifsfirst.begoff > endoff)
+               if (ifsfirst.begoff > endoff) {
                        ifslastp = NULL;
-               else {
+               else {
                        ifslastp = &ifsfirst;
                        ifsfirst.endoff = endoff;
                }
@@ -5587,8 +5746,8 @@ removerecordregions(int endoff)
 
        ifslastp = &ifsfirst;
        while (ifslastp->next && ifslastp->next->begoff < endoff)
-               ifslastp=ifslastp->next;
-       while (ifslastp->next != NULL) {
+               ifslastp = ifslastp->next;
+       while (ifslastp->next) {
                struct ifsregion *ifsp;
                INT_OFF;
                ifsp = ifslastp->next->next;
@@ -5743,7 +5902,7 @@ expbackq(union node *cmd, int quoted, int quotes)
  read:
                if (in.fd < 0)
                        break;
-               i = nonblock_safe_read(in.fd, buf, sizeof(buf));
+               i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1);
                TRACE(("expbackq: read returns %d\n", i));
                if (i <= 0)
                        break;
@@ -5765,9 +5924,9 @@ expbackq(union node *cmd, int quoted, int quotes)
 
        if (quoted == 0)
                recordregion(startloc, dest - (char *)stackblock(), 0);
-       TRACE(("evalbackq: size=%d: \"%.*s\"\n",
-               (dest - (char *)stackblock()) - startloc,
-               (dest - (char *)stackblock()) - startloc,
+       TRACE(("evalbackq: size:%d:'%.*s'\n",
+               (int)((dest - (char *)stackblock()) - startloc),
+               (int)((dest - (char *)stackblock()) - startloc),
                stackblock() + startloc));
 }
 
@@ -5795,7 +5954,7 @@ expari(int quotes)
        p = expdest - 1;
        *p = '\0';
        p--;
-       do {
+       while (1) {
                int esc;
 
                while ((unsigned char)*p != CTLARI) {
@@ -5813,7 +5972,7 @@ expari(int quotes)
                }
 
                p -= esc + 1;
-       } while (1);
+       }
 
        begoff = p - start;
 
@@ -5882,7 +6041,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
                flags &= ~EXP_TILDE;
  tilde:
                q = p;
-               if (*q == CTLESC && (flags & EXP_QWORD))
+               if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
                        q++;
                if (*q == '~')
                        p = exptilde(p, q, flags);
@@ -5896,9 +6055,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
                c = p[length];
                if (c) {
                        if (!(c & 0x80)
-#if ENABLE_SH_MATH_SUPPORT
-                        || c == CTLENDARI
-#endif
+                       IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
                        ) {
                                /* c == '=' || c == ':' || c == CTLENDARI */
                                length++;
@@ -5945,8 +6102,8 @@ argstr(char *p, int flags, struct strlist *var_str_list)
                        /* "$@" syntax adherence hack */
                        if (!inquotes
                         && memcmp(p, dolatstr, 4) == 0
-                        && (  p[4] == CTLQUOTEMARK
-                           || (p[4] == CTLENDVAR && p[5] == CTLQUOTEMARK)
+                        && (  p[4] == (char)CTLQUOTEMARK
+                           || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
                            )
                        ) {
                                p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
@@ -5965,7 +6122,9 @@ argstr(char *p, int flags, struct strlist *var_str_list)
                        length++;
                        goto addquote;
                case CTLVAR:
+                       TRACE(("argstr: evalvar('%s')\n", p));
                        p = evalvar(p, flags, var_str_list);
+                       TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock()));
                        goto start;
                case CTLBACKQ:
                        c = '\0';
@@ -5981,47 +6140,20 @@ argstr(char *p, int flags, struct strlist *var_str_list)
 #endif
                }
        }
- breakloop:
-       ;
+ breakloop: ;
 }
 
 static char *
-scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
-       int zero)
-{
-// This commented out code was added by James Simmons <jsimmons@infradead.org>
-// as part of a larger change when he added support for ${var/a/b}.
-// However, it broke # and % operators:
-//
-//var=ababcdcd
-//                 ok       bad
-//echo ${var#ab}   abcdcd   abcdcd
-//echo ${var##ab}  abcdcd   abcdcd
-//echo ${var#a*b}  abcdcd   ababcdcd  (!)
-//echo ${var##a*b} cdcd     cdcd
-//echo ${var#?}    babcdcd  ababcdcd  (!)
-//echo ${var##?}   babcdcd  babcdcd
-//echo ${var#*}    ababcdcd babcdcd   (!)
-//echo ${var##*}
-//echo ${var%cd}   ababcd   ababcd
-//echo ${var%%cd}  ababcd   abab      (!)
-//echo ${var%c*d}  ababcd   ababcd
-//echo ${var%%c*d} abab     ababcdcd  (!)
-//echo ${var%?}    ababcdc  ababcdc
-//echo ${var%%?}   ababcdc  ababcdcd  (!)
-//echo ${var%*}    ababcdcd ababcdcd
-//echo ${var%%*}
-//
-// Commenting it back out helped. Remove it completely if it really
-// is not needed.
-
-       char *loc, *loc2; //, *full;
+scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
+               char *pattern, int quotes, int zero)
+{
+       char *loc, *loc2;
        char c;
 
        loc = startp;
        loc2 = rmesc;
        do {
-               int match; // = strlen(str);
+               int match;
                const char *s = loc2;
 
                c = *loc2;
@@ -6029,35 +6161,22 @@ scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int
                        *loc2 = '\0';
                        s = rmesc;
                }
-               match = pmatch(str, s); // this line was deleted
-
-//             // chop off end if its '*'
-//             full = strrchr(str, '*');
-//             if (full && full != str)
-//                     match--;
-//
-//             // If str starts with '*' replace with s.
-//             if ((*str == '*') && strlen(s) >= match) {
-//                     full = xstrdup(s);
-//                     strncpy(full+strlen(s)-match+1, str+1, match-1);
-//             } else
-//                     full = xstrndup(str, match);
-//             match = strncmp(s, full, strlen(full));
-//             free(full);
-//
+               match = pmatch(pattern, s);
+
                *loc2 = c;
-               if (match) // if (!match)
+               if (match)
                        return loc;
                if (quotes && (unsigned char)*loc == CTLESC)
                        loc++;
                loc++;
                loc2++;
        } while (c);
-       return 0;
+       return NULL;
 }
 
 static char *
-scanright(char *startp, char *rmesc, char *rmescend, char *pattern, int quotes, int match_at_start)
+scanright(char *startp, char *rmesc, char *rmescend,
+               char *pattern, int quotes, int match_at_start)
 {
 #if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
        int try2optimize = match_at_start;
@@ -6123,7 +6242,7 @@ scanright(char *startp, char *rmesc, char *rmescend, char *pattern, int quotes,
                        }
                }
        }
-       return 0;
+       return NULL;
 }
 
 static void varunset(const char *, const char *, const char *, int) NORETURN;
@@ -6143,16 +6262,18 @@ varunset(const char *end, const char *var, const char *umsg, int varflags)
                        msg = umsg;
                }
        }
-       ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
+       ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
 }
 
 #if ENABLE_ASH_BASH_COMPAT
 static char *
-parse_sub_pattern(char *arg, int inquotes)
+parse_sub_pattern(char *arg, int varflags)
 {
        char *idx, *repl = NULL;
        unsigned char c;
 
+       //char *org_arg = arg;
+       //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
        idx = arg;
        while (1) {
                c = *arg;
@@ -6166,31 +6287,48 @@ parse_sub_pattern(char *arg, int inquotes)
                        }
                }
                *idx++ = c;
-               if (!inquotes && c == '\\' && arg[1] == '\\')
-                       arg++; /* skip both \\, not just first one */
                arg++;
+               /*
+                * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
+                * The result is a_\_z_c (not a\_\_z_c)!
+                *
+                * Enable debug prints in this function and you'll see:
+                * ash: arg:'\\b/_\\_z_' varflags:d
+                * ash: pattern:'\\b' repl:'_\_z_'
+                * That is, \\b is interpreted as \\b, but \\_ as \_!
+                * IOW: search pattern and replace string treat backslashes
+                * differently! That is the reason why we check repl below:
+                */
+               if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
+                       arg++; /* skip both '\', not just first one */
        }
        *idx = c; /* NUL */
+       //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
 
        return repl;
 }
 #endif /* ENABLE_ASH_BASH_COMPAT */
 
 static const char *
-subevalvar(char *p, char *str, int strloc, int subtype,
+subevalvar(char *p, char *varname, int strloc, int subtype,
                int startloc, int varflags, int quotes, struct strlist *var_str_list)
 {
        struct nodelist *saveargbackq = argbackq;
        char *startp;
        char *loc;
        char *rmesc, *rmescend;
+       char *str;
        IF_ASH_BASH_COMPAT(const char *repl = NULL;)
        IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
        int saveherefd = herefd;
-       int amount, workloc, resetloc;
+       int amount, resetloc;
+       IF_ASH_BASH_COMPAT(int workloc;)
        int zero;
        char *(*scan)(char*, char*, char*, char*, int, int);
 
+       //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
+       //              p, varname, strloc, subtype, startloc, varflags, quotes);
+
        herefd = -1;
        argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
                        var_str_list);
@@ -6201,11 +6339,15 @@ subevalvar(char *p, char *str, int strloc, int subtype,
 
        switch (subtype) {
        case VSASSIGN:
-               setvar(str, startp, 0);
+               setvar2(varname, startp);
                amount = startp - expdest;
                STADJUST(amount, expdest);
                return startp;
 
+       case VSQUESTION:
+               varunset(p, varname, startp, varflags);
+               /* NOTREACHED */
+
 #if ENABLE_ASH_BASH_COMPAT
        case VSSUBSTR:
                loc = str = stackblock() + strloc;
@@ -6266,11 +6408,8 @@ subevalvar(char *p, char *str, int strloc, int subtype,
                STADJUST(amount, expdest);
                return loc;
 #endif
-
-       case VSQUESTION:
-               varunset(p, str, startp, varflags);
-               /* NOTREACHED */
        }
+
        resetloc = expdest - (char *)stackblock();
 
        /* We'll comeback here if we grow the stack while handling
@@ -6296,21 +6435,22 @@ subevalvar(char *p, char *str, int strloc, int subtype,
        rmescend--;
        str = (char *)stackblock() + strloc;
        preglob(str, varflags & VSQUOTE, 0);
-       workloc = expdest - (char *)stackblock();
 
 #if ENABLE_ASH_BASH_COMPAT
+       workloc = expdest - (char *)stackblock();
        if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
                char *idx, *end;
 
                if (!repl) {
-                       repl = parse_sub_pattern(str, varflags & VSQUOTE);
+                       repl = parse_sub_pattern(str, varflags);
+                       //bb_error_msg("repl:'%s'", repl);
                        if (!repl)
                                repl = nullstr;
                }
 
                /* If there's no pattern to match, return the expansion unmolested */
                if (str[0] == '\0')
-                       return 0;
+                       return NULL;
 
                len = 0;
                idx = startp;
@@ -6318,6 +6458,7 @@ subevalvar(char *p, char *str, int strloc, int subtype,
                while (idx < end) {
  try_to_match:
                        loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
+                       //bb_error_msg("scanright('%s'):'%s'", str, loc);
                        if (!loc) {
                                /* No match, advance */
                                char *restart_detect = stackblock();
@@ -6356,6 +6497,7 @@ subevalvar(char *p, char *str, int strloc, int subtype,
                                idx = loc;
                        }
 
+                       //bb_error_msg("repl:'%s'", repl);
                        for (loc = (char*)repl; *loc; loc++) {
                                char *restart_detect = stackblock();
                                if (quotes && *loc == '\\') {
@@ -6369,12 +6511,9 @@ subevalvar(char *p, char *str, int strloc, int subtype,
                        }
 
                        if (subtype == VSREPLACE) {
+                               //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
                                while (*idx) {
                                        char *restart_detect = stackblock();
-                                       if (quotes && *idx == '\\') {
-                                               STPUTC(CTLESC, expdest);
-                                               len++;
-                                       }
                                        STPUTC(*idx, expdest);
                                        if (stackblock() != restart_detect)
                                                goto restart;
@@ -6391,6 +6530,7 @@ subevalvar(char *p, char *str, int strloc, int subtype,
                STPUTC('\0', expdest);
                startp = (char *)stackblock() + startloc;
                memmove(startp, (char *)stackblock() + workloc, len + 1);
+               //bb_error_msg("startp:'%s'", startp);
                amount = expdest - (startp + len);
                STADJUST(-amount, expdest);
                return startp;
@@ -6620,8 +6760,8 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
  vsplus:
                if (varlen < 0) {
                        argstr(
-                               p, flags | EXP_TILDE |
-                                       (quoted ? EXP_QWORD : EXP_WORD),
+                               p,
+                               flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
                                var_str_list
                        );
                        goto end;
@@ -6691,10 +6831,9 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
                 */
                STPUTC('\0', expdest);
                patloc = expdest - (char *)stackblock();
-               if (NULL == subevalvar(p, /* str: */ NULL, patloc, subtype,
+               if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
                                startloc, varflags,
-//TODO: | EXP_REDIR too? All other such places do it too
-                               /* quotes: */ flags & (EXP_FULL | EXP_CASE),
+                               /* quotes: */ flags & (EXP_FULL | EXP_CASE | EXP_REDIR),
                                var_str_list)
                ) {
                        int amount = expdest - (
@@ -7096,6 +7235,7 @@ expandarg(union node *arg, struct arglist *arglist, int flag)
        STARTSTACKSTR(expdest);
        ifsfirst.next = NULL;
        ifslastp = NULL;
+       TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag));
        argstr(arg->narg.text, flag,
                        /* var_str_list: */ arglist ? arglist->list : NULL);
        p = _STPUTC('\0', expdest);
@@ -7104,6 +7244,7 @@ expandarg(union node *arg, struct arglist *arglist, int flag)
                return;                 /* here document expanded */
        }
        p = grabstackstr(p);
+       TRACE(("expandarg: p:'%s'\n", p));
        exparg.lastp = &exparg.list;
        /*
         * TODO - EXP_REDIR
@@ -7114,8 +7255,10 @@ 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 */
+               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;
@@ -7243,8 +7386,6 @@ 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)
 {
-       int repeated = 0;
-
 #if ENABLE_FEATURE_SH_STANDALONE
        if (applet_no >= 0) {
                if (APPLET_IS_NOEXEC(applet_no)) {
@@ -7268,25 +7409,42 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **
 #else
        execve(cmd, argv, envp);
 #endif
-       if (repeated) {
+       if (cmd == (char*) bb_busybox_exec_path) {
+               /* We already visited ENOEXEC branch below, don't do it again */
+//TODO: try execve(initial_argv0_of_shell, argv, envp) before giving up?
                free(argv);
                return;
        }
        if (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
+                * shall execute a command equivalent to having a shell invoked
+                * with the command name as its first operand,
+                * with any remaining arguments passed to the new shell"
+                *
+                * That is, do not use $SHELL, user's shell, or /bin/sh;
+                * just call ourselves.
+                *
+                * Note that bash reads ~80 chars of the file, and if it sees
+                * a zero byte before it sees newline, it doesn't try to
+                * interpret it, but fails with "cannot execute binary file"
+                * message and exit code 126. For one, this prevents attempts
+                * to interpret foreign ELF binaries as shell scripts.
+                */
                char **ap;
                char **new;
 
                for (ap = argv; *ap; ap++)
                        continue;
-               ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
-               ap[1] = cmd;
-               ap[0] = cmd = (char *)DEFAULT_SHELL;
-               ap += 2;
-               argv++;
-               while ((*ap++ = *argv++) != NULL)
+               new = ckmalloc((ap - argv + 2) * sizeof(new[0]));
+               new[0] = (char*) "ash";
+               new[1] = cmd;
+               ap = new + 2;
+               while ((*ap++ = *++argv) != NULL)
                        continue;
+               cmd = (char*) bb_busybox_exec_path;
                argv = new;
-               repeated++;
                goto repeat;
        }
 }
@@ -7303,9 +7461,7 @@ shellexec(char **argv, const char *path, int idx)
        int e;
        char **envp;
        int exerrno;
-#if ENABLE_FEATURE_SH_STANDALONE
-       int applet_no = -1;
-#endif
+       int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
 
        clearredir(/*drop:*/ 1);
        envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
@@ -7315,8 +7471,16 @@ shellexec(char **argv, const char *path, int idx)
 #endif
        ) {
                tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
+               if (applet_no >= 0) {
+                       /* We tried execing ourself, but it didn't work.
+                        * Maybe /proc/self/exe doesn't exist?
+                        * Try $PATH search.
+                        */
+                       goto try_PATH;
+               }
                e = errno;
        } else {
+ try_PATH:
                e = ENOENT;
                while ((cmdname = path_advance(&path, argv[0])) != NULL) {
                        if (--idx < 0 && pathopt == NULL) {
@@ -7526,7 +7690,7 @@ hashcd(void)
                for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
                        if (cmdp->cmdtype == CMDNORMAL
                         || (cmdp->cmdtype == CMDBUILTIN
-                            && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
+                            && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
                             && builtinloc > 0)
                        ) {
                                cmdp->rehash = 1;
@@ -8117,7 +8281,7 @@ static int evalstring(char *s, int mask);
 
 /* Called to execute a trap.
  * Single callsite - at the end of evaltree().
- * If we return non-zero, exaltree raises EXEXIT exception.
+ * If we return non-zero, evaltree raises EXEXIT exception.
  *
  * Perhaps we should avoid entering new trap handlers
  * while we are executing a trap handler. [is it a TODO?]
@@ -8307,11 +8471,15 @@ evaltree(union node *n, int flags)
 
  out:
        exception_handler = savehandler;
+
  out1:
+       /* Order of checks below is important:
+        * signal handlers trigger before exit caused by "set -e".
+        */
+       if (pending_sig && dotrap())
+               goto exexit;
        if (checkexit & exitstatus)
                evalskip |= SKIPEVAL;
-       else if (pending_sig && dotrap())
-               goto exexit;
 
        if (flags & EV_EXIT) {
  exexit:
@@ -8386,7 +8554,7 @@ evalfor(union node *n, int flags)
        loopnest++;
        flags &= EV_TESTED;
        for (sp = arglist.list; sp; sp = sp->next) {
-               setvar(n->nfor.var, sp->text, 0);
+               setvar2(n->nfor.var, sp->text);
                evaltree(n->nfor.body, flags);
                if (evalskip) {
                        if (evalskip == SKIPCONT && --skipcount <= 0) {
@@ -8487,9 +8655,21 @@ expredir(union node *n)
                case NCLOBBER:
                case NAPPEND:
                        expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
+                       TRACE(("expredir expanded to '%s'\n", fn.list->text));
 #if ENABLE_ASH_BASH_COMPAT
  store_expfname:
 #endif
+#if 0
+// By the design of stack allocator, the loop of this kind:
+//     while true; do while true; do break; done </dev/null; done
+// will look like a memory leak: ash plans to free expfname's
+// of "/dev/null" as soon as it finishes running the loop
+// (in this case, never).
+// This "fix" is wrong:
+                       if (redir->nfile.expfname)
+                               stunalloc(redir->nfile.expfname);
+// It results in corrupted state of stacked allocations.
+#endif
                        redir->nfile.expfname = fn.list->text;
                        break;
                case NFROMFD:
@@ -8643,7 +8823,7 @@ poplocalvars(void)
        while ((lvp = localvars) != NULL) {
                localvars = lvp->next;
                vp = lvp->vp;
-               TRACE(("poplocalvar %s\n", vp ? vp->text : "-"));
+               TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
                if (vp == NULL) {       /* $- saved */
                        memcpy(optlist, lvp->text, sizeof(optlist));
                        free((char*)lvp->text);
@@ -8853,6 +9033,9 @@ static int getoptscmd(int, char **) FAST_FUNC;
 #if !ENABLE_FEATURE_SH_EXTRA_QUIET
 static int helpcmd(int, char **) FAST_FUNC;
 #endif
+#if MAX_HISTORY
+static int historycmd(int, char **) FAST_FUNC;
+#endif
 #if ENABLE_SH_MATH_SUPPORT
 static int letcmd(int, char **) FAST_FUNC;
 #endif
@@ -8926,6 +9109,9 @@ static const struct builtincmd builtintab[] = {
 #if !ENABLE_FEATURE_SH_EXTRA_QUIET
        { BUILTIN_NOSPEC        "help"    , helpcmd    },
 #endif
+#if MAX_HISTORY
+       { BUILTIN_NOSPEC        "history" , historycmd },
+#endif
 #if JOBS
        { BUILTIN_REGULAR       "jobs"    , jobscmd    },
        { BUILTIN_REGULAR       "kill"    , killcmd    },
@@ -9125,11 +9311,11 @@ evalcommand(union node *cmd, int flags)
 
        /* Now locate the command. */
        if (argc) {
-               const char *oldpath;
                int cmd_flag = DO_ERR;
-
+#if ENABLE_ASH_CMDCMD
+               const char *oldpath = path + 5;
+#endif
                path += 5;
-               oldpath = path;
                for (;;) {
                        find_command(argv[0], &cmdentry, cmd_flag, path);
                        if (cmdentry.cmdtype == CMDUNKNOWN) {
@@ -9265,7 +9451,7 @@ evalcommand(union node *cmd, int flags)
                 * '_' in 'vi' command mode during line editing...
                 * However I implemented that within libedit itself.
                 */
-               setvar("_", lastarg, 0);
+               setvar2("_", lastarg);
        }
        popstackmark(&smark);
 }
@@ -9301,7 +9487,7 @@ evalbltin(const struct builtincmd *cmd, int argc, char **argv)
 static int
 goodname(const char *p)
 {
-       return !*endofname(p);
+       return endofname(p)[0] == '\0';
 }
 
 
@@ -9451,12 +9637,33 @@ preadfd(void)
 #if ENABLE_FEATURE_EDITING
  retry:
        if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
-               nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
+               nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
        else {
-#if ENABLE_FEATURE_TAB_COMPLETION
+               int timeout = -1;
+# if ENABLE_ASH_IDLE_TIMEOUT
+               if (iflag) {
+                       const char *tmout_var = lookupvar("TMOUT");
+                       if (tmout_var) {
+                               timeout = atoi(tmout_var) * 1000;
+                               if (timeout <= 0)
+                                       timeout = -1;
+                       }
+               }
+# endif
+# if ENABLE_FEATURE_TAB_COMPLETION
                line_input_state->path_lookup = pathval();
-#endif
-               nr = read_line_input(cmdedit_prompt, buf, IBUFSIZ, line_input_state);
+# endif
+               /* Unicode support should be activated even if LANG is set
+                * _during_ shell execution, not only if it was set when
+                * shell was started. Therefore, re-check LANG every time:
+                */
+               {
+                       const char *s = lookupvar("LC_ALL");
+                       if (!s) s = lookupvar("LC_CTYPE");
+                       if (!s) s = lookupvar("LANG");
+                       reinit_unicode(s);
+               }
+               nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
                if (nr == 0) {
                        /* Ctrl+C pressed */
                        if (trap[SIGINT]) {
@@ -9467,17 +9674,24 @@ preadfd(void)
                        }
                        goto retry;
                }
-               if (nr < 0 && errno == 0) {
-                       /* Ctrl+D pressed */
-                       nr = 0;
+               if (nr < 0) {
+                       if (errno == 0) {
+                               /* Ctrl+D pressed */
+                               nr = 0;
+                       }
+# if ENABLE_ASH_IDLE_TIMEOUT
+                       else if (errno == EAGAIN && timeout > 0) {
+                               printf("\007timed out waiting for input: auto-logout\n");
+                               exitshell();
+                       }
+# endif
                }
        }
 #else
-       nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
+       nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
 #endif
 
-#if 0
-/* nonblock_safe_read() handles this problem */
+#if 0 /* disabled: nonblock_immune_read() handles this problem */
        if (nr < 0) {
                if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
                        int flags = fcntl(0, F_GETFL);
@@ -9996,7 +10210,7 @@ options(int cmdline)
                                        else if (*argptr == NULL)
                                                setparam(argptr);
                                }
-                               break;    /* "-" or  "--" terminates options */
+                               break;    /* "-" or "--" terminates options */
                        }
                }
                /* first char was + or - */
@@ -10098,10 +10312,10 @@ setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 
        if (!argv[1])
                return showvars(nullstr, 0, VUNSET);
+
        INT_OFF;
-       retval = 1;
-       if (!options(0)) { /* if no parse error... */
-               retval = 0;
+       retval = options(/*cmdline:*/ 0);
+       if (retval == 0) { /* if no parse error... */
                optschanged();
                if (*argptr != NULL) {
                        setparam(argptr);
@@ -10932,7 +11146,6 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
        startlinno = g_parsefile->linno;
        bqlist = NULL;
        quotef = 0;
-       oldstyle = 0;
        prevsyntax = 0;
 #if ENABLE_ASH_EXPAND_PRMT
        pssyntax = (syntax == PSSYNTAX);
@@ -10948,160 +11161,156 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
        STARTSTACKSTR(out);
  loop:
        /* For each line, until end of word */
-       {
-               CHECKEND();     /* set c to PEOF if at end of here document */
-               for (;;) {      /* until end of line or end of word */
-                       CHECKSTRSPACE(4, out);  /* permit 4 calls to USTPUTC */
-                       switch (SIT(c, syntax)) {
-                       case CNL:       /* '\n' */
-                               if (syntax == BASESYNTAX)
-                                       goto endword;   /* exit outer loop */
-                               USTPUTC(c, out);
-                               g_parsefile->linno++;
-                               if (doprompt)
-                                       setprompt(2);
-                               c = pgetc();
-                               goto loop;              /* continue outer loop */
-                       case CWORD:
-                               USTPUTC(c, out);
-                               break;
-                       case CCTL:
-                               if (eofmark == NULL || dblquote)
-                                       USTPUTC(CTLESC, out);
+       CHECKEND();     /* set c to PEOF if at end of here document */
+       for (;;) {      /* until end of line or end of word */
+               CHECKSTRSPACE(4, out);  /* permit 4 calls to USTPUTC */
+               switch (SIT(c, syntax)) {
+               case CNL:       /* '\n' */
+                       if (syntax == BASESYNTAX)
+                               goto endword;   /* exit outer loop */
+                       USTPUTC(c, out);
+                       g_parsefile->linno++;
+                       setprompt_if(doprompt, 2);
+                       c = pgetc();
+                       goto loop;              /* continue outer loop */
+               case CWORD:
+                       USTPUTC(c, out);
+                       break;
+               case CCTL:
+                       if (eofmark == NULL || dblquote)
+                               USTPUTC(CTLESC, out);
 #if ENABLE_ASH_BASH_COMPAT
-                               if (c == '\\' && bash_dollar_squote) {
-                                       c = decode_dollar_squote();
-                                       if (c & 0x100) {
-                                               USTPUTC('\\', out);
-                                               c = (unsigned char)c;
-                                       }
+                       if (c == '\\' && bash_dollar_squote) {
+                               c = decode_dollar_squote();
+                               if (c & 0x100) {
+                                       USTPUTC('\\', out);
+                                       c = (unsigned char)c;
                                }
+                       }
 #endif
-                               USTPUTC(c, out);
-                               break;
-                       case CBACK:     /* backslash */
-                               c = pgetc_without_PEOA();
-                               if (c == PEOF) {
+                       USTPUTC(c, out);
+                       break;
+               case CBACK:     /* backslash */
+                       c = pgetc_without_PEOA();
+                       if (c == PEOF) {
+                               USTPUTC(CTLESC, out);
+                               USTPUTC('\\', out);
+                               pungetc();
+                       } else if (c == '\n') {
+                               setprompt_if(doprompt, 2);
+                       } else {
+#if ENABLE_ASH_EXPAND_PRMT
+                               if (c == '$' && pssyntax) {
                                        USTPUTC(CTLESC, out);
                                        USTPUTC('\\', out);
-                                       pungetc();
-                               } else if (c == '\n') {
-                                       if (doprompt)
-                                               setprompt(2);
-                               } else {
-#if ENABLE_ASH_EXPAND_PRMT
-                                       if (c == '$' && pssyntax) {
-                                               USTPUTC(CTLESC, out);
-                                               USTPUTC('\\', out);
-                                       }
+                               }
 #endif
-                                       if (dblquote && c != '\\'
-                                        && c != '`' && c != '$'
-                                        && (c != '"' || eofmark != NULL)
-                                       ) {
-                                               USTPUTC(CTLESC, out);
-                                               USTPUTC('\\', out);
-                                       }
-                                       if (SIT(c, SQSYNTAX) == CCTL)
-                                               USTPUTC(CTLESC, out);
-                                       USTPUTC(c, out);
-                                       quotef = 1;
+                               /* Backslash is retained if we are in "str" and next char isn't special */
+                               if (dblquote
+                                && c != '\\'
+                                && c != '`'
+                                && c != '$'
+                                && (c != '"' || eofmark != NULL)
+                               ) {
+                                       USTPUTC(CTLESC, out);
+                                       USTPUTC('\\', out);
                                }
-                               break;
-                       case CSQUOTE:
-                               syntax = SQSYNTAX;
+                               if (SIT(c, SQSYNTAX) == CCTL)
+                                       USTPUTC(CTLESC, out);
+                               USTPUTC(c, out);
+                               quotef = 1;
+                       }
+                       break;
+               case CSQUOTE:
+                       syntax = SQSYNTAX;
  quotemark:
-                               if (eofmark == NULL) {
-                                       USTPUTC(CTLQUOTEMARK, out);
+                       if (eofmark == NULL) {
+                               USTPUTC(CTLQUOTEMARK, out);
+                       }
+                       break;
+               case CDQUOTE:
+                       syntax = DQSYNTAX;
+                       dblquote = 1;
+                       goto quotemark;
+               case CENDQUOTE:
+                       IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
+                       if (eofmark != NULL && arinest == 0
+                        && varnest == 0
+                       ) {
+                               USTPUTC(c, out);
+                       } else {
+                               if (dqvarnest == 0) {
+                                       syntax = BASESYNTAX;
+                                       dblquote = 0;
                                }
-                               break;
-                       case CDQUOTE:
-                               syntax = DQSYNTAX;
-                               dblquote = 1;
+                               quotef = 1;
                                goto quotemark;
-                       case CENDQUOTE:
-                               IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
-                               if (eofmark != NULL && arinest == 0
-                                && varnest == 0
-                               ) {
-                                       USTPUTC(c, out);
-                               } else {
-                                       if (dqvarnest == 0) {
-                                               syntax = BASESYNTAX;
-                                               dblquote = 0;
-                                       }
-                                       quotef = 1;
-                                       goto quotemark;
-                               }
-                               break;
-                       case CVAR:      /* '$' */
-                               PARSESUB();             /* parse substitution */
-                               break;
-                       case CENDVAR:   /* '}' */
-                               if (varnest > 0) {
-                                       varnest--;
-                                       if (dqvarnest > 0) {
-                                               dqvarnest--;
-                                       }
-                                       USTPUTC(CTLENDVAR, out);
-                               } else {
-                                       USTPUTC(c, out);
+                       }
+                       break;
+               case CVAR:      /* '$' */
+                       PARSESUB();             /* parse substitution */
+                       break;
+               case CENDVAR:   /* '}' */
+                       if (varnest > 0) {
+                               varnest--;
+                               if (dqvarnest > 0) {
+                                       dqvarnest--;
                                }
-                               break;
+                               c = CTLENDVAR;
+                       }
+                       USTPUTC(c, out);
+                       break;
 #if ENABLE_SH_MATH_SUPPORT
-                       case CLP:       /* '(' in arithmetic */
-                               parenlevel++;
-                               USTPUTC(c, out);
-                               break;
-                       case CRP:       /* ')' in arithmetic */
-                               if (parenlevel > 0) {
-                                       USTPUTC(c, out);
-                                       --parenlevel;
-                               } else {
-                                       if (pgetc() == ')') {
-                                               if (--arinest == 0) {
-                                                       USTPUTC(CTLENDARI, out);
-                                                       syntax = prevsyntax;
-                                                       dblquote = (syntax == DQSYNTAX);
-                                               } else
-                                                       USTPUTC(')', out);
-                                       } else {
-                                               /*
-                                                * unbalanced parens
-                                                *  (don't 2nd guess - no error)
-                                                */
-                                               pungetc();
-                                               USTPUTC(')', out);
+               case CLP:       /* '(' in arithmetic */
+                       parenlevel++;
+                       USTPUTC(c, out);
+                       break;
+               case CRP:       /* ')' in arithmetic */
+                       if (parenlevel > 0) {
+                               parenlevel--;
+                       } else {
+                               if (pgetc() == ')') {
+                                       if (--arinest == 0) {
+                                               syntax = prevsyntax;
+                                               dblquote = (syntax == DQSYNTAX);
+                                               c = CTLENDARI;
                                        }
+                               } else {
+                                       /*
+                                        * unbalanced parens
+                                        * (don't 2nd guess - no error)
+                                        */
+                                       pungetc();
                                }
-                               break;
+                       }
+                       USTPUTC(c, out);
+                       break;
 #endif
-                       case CBQUOTE:   /* '`' */
-                               PARSEBACKQOLD();
-                               break;
-                       case CENDFILE:
-                               goto endword;           /* exit outer loop */
-                       case CIGN:
-                               break;
-                       default:
-                               if (varnest == 0) {
+               case CBQUOTE:   /* '`' */
+                       PARSEBACKQOLD();
+                       break;
+               case CENDFILE:
+                       goto endword;           /* exit outer loop */
+               case CIGN:
+                       break;
+               default:
+                       if (varnest == 0) {
 #if ENABLE_ASH_BASH_COMPAT
-                                       if (c == '&') {
-                                               if (pgetc() == '>')
-                                                       c = 0x100 + '>'; /* flag &> */
-                                               pungetc();
-                                       }
-#endif
-                                       goto endword;   /* exit outer loop */
+                               if (c == '&') {
+                                       if (pgetc() == '>')
+                                               c = 0x100 + '>'; /* flag &> */
+                                       pungetc();
                                }
-                               IF_ASH_ALIAS(if (c != PEOA))
-                                       USTPUTC(c, out);
-
+#endif
+                               goto endword;   /* exit outer loop */
                        }
-                       c = pgetc_fast();
-               } /* for (;;) */
-       }
+                       IF_ASH_ALIAS(if (c != PEOA))
+                               USTPUTC(c, out);
+               }
+               c = pgetc_fast();
+       } /* for (;;) */
  endword:
+
 #if ENABLE_SH_MATH_SUPPORT
        if (syntax == ARISYNTAX)
                raise_error_syntax("missing '))'");
@@ -11262,8 +11471,6 @@ parsesub: {
        unsigned char subtype;
        int typeloc;
        int flags;
-       char *p;
-       static const char types[] ALIGN1 = "}-+?=";
 
        c = pgetc();
        if (c > 255 /* PEOA or PEOF */
@@ -11276,7 +11483,8 @@ parsesub: {
 #endif
                        USTPUTC('$', out);
                pungetc();
-       } else if (c == '(') {  /* $(command) or $((arith)) */
+       } else if (c == '(') {
+               /* $(command) or $((arith)) */
                if (pgetc() == '(') {
 #if ENABLE_SH_MATH_SUPPORT
                        PARSEARITH();
@@ -11288,6 +11496,7 @@ parsesub: {
                        PARSEBACKQNEW();
                }
        } else {
+               /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
                USTPUTC(CTLVAR, out);
                typeloc = out - (char *)stackblock();
                USTPUTC(VSNORMAL, out);
@@ -11297,76 +11506,90 @@ parsesub: {
                        if (c == '#') {
                                c = pgetc();
                                if (c == '}')
-                                       c = '#';
+                                       c = '#'; /* ${#} - same as $# */
                                else
-                                       subtype = VSLENGTH;
-                       } else
+                                       subtype = VSLENGTH; /* ${#VAR} */
+                       } else {
                                subtype = 0;
+                       }
                }
                if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
+                       /* $[{[#]]NAME[}] */
                        do {
                                STPUTC(c, out);
                                c = pgetc();
                        } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
                } else if (isdigit(c)) {
+                       /* $[{[#]]NUM[}] */
                        do {
                                STPUTC(c, out);
                                c = pgetc();
                        } while (isdigit(c));
                } else if (is_special(c)) {
+                       /* $[{[#]]<specialchar>[}] */
                        USTPUTC(c, out);
                        c = pgetc();
                } else {
  badsub:
                        raise_error_syntax("bad substitution");
                }
-               if (c != '}' && subtype == VSLENGTH)
+               if (c != '}' && subtype == VSLENGTH) {
+                       /* ${#VAR didn't end with } */
                        goto badsub;
+               }
 
                STPUTC('=', out);
                flags = 0;
                if (subtype == 0) {
+                       /* ${VAR...} but not $VAR or ${#VAR} */
+                       /* c == first char after VAR */
                        switch (c) {
                        case ':':
                                c = pgetc();
 #if ENABLE_ASH_BASH_COMPAT
                                if (c == ':' || c == '$' || isdigit(c)) {
-                                       pungetc();
+//TODO: support more general format ${v:EXPR:EXPR},
+// where EXPR follows $(()) rules
                                        subtype = VSSUBSTR;
-                                       break;
+                                       pungetc();
+                                       break; /* "goto do_pungetc" is bigger (!) */
                                }
 #endif
                                flags = VSNUL;
                                /*FALLTHROUGH*/
-                       default:
-                               p = strchr(types, c);
+                       default: {
+                               static const char types[] ALIGN1 = "}-+?=";
+                               const char *p = strchr(types, c);
                                if (p == NULL)
                                        goto badsub;
                                subtype = p - types + VSNORMAL;
                                break;
+                       }
                        case '%':
                        case '#': {
                                int cc = c;
-                               subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
+                               subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
                                c = pgetc();
-                               if (c == cc)
-                                       subtype++;
-                               else
-                                       pungetc();
+                               if (c != cc)
+                                       goto do_pungetc;
+                               subtype++;
                                break;
                        }
 #if ENABLE_ASH_BASH_COMPAT
                        case '/':
+                               /* ${v/[/]pattern/repl} */
+//TODO: encode pattern and repl separately.
+// Currently ${v/$var_with_slash/repl} is horribly broken
                                subtype = VSREPLACE;
                                c = pgetc();
-                               if (c == '/')
-                                       subtype++; /* VSREPLACEALL */
-                               else
-                                       pungetc();
+                               if (c != '/')
+                                       goto do_pungetc;
+                               subtype++; /* VSREPLACEALL */
                                break;
 #endif
                        }
                } else {
+ do_pungetc:
                        pungetc();
                }
                if (dblquote || arinest)
@@ -11420,19 +11643,18 @@ parsebackq: {
        INT_ON;
        if (oldstyle) {
                /* We must read until the closing backquote, giving special
-                  treatment to some slashes, and then push the string and
-                  reread it as input, interpreting it normally.  */
+                * treatment to some slashes, and then push the string and
+                * reread it as input, interpreting it normally.
+                */
                char *pout;
-               int pc;
                size_t psavelen;
                char *pstr;
 
-
                STARTSTACKSTR(pout);
                for (;;) {
-                       if (needprompt) {
-                               setprompt(2);
-                       }
+                       int pc;
+
+                       setprompt_if(needprompt, 2);
                        pc = pgetc();
                        switch (pc) {
                        case '`':
@@ -11442,8 +11664,7 @@ parsebackq: {
                                pc = pgetc();
                                if (pc == '\n') {
                                        g_parsefile->linno++;
-                                       if (doprompt)
-                                               setprompt(2);
+                                       setprompt_if(doprompt, 2);
                                        /*
                                         * If eating a newline, avoid putting
                                         * the newline into the new character
@@ -11606,9 +11827,7 @@ xxreadtoken(void)
                tokpushback = 0;
                return lasttoken;
        }
-       if (needprompt) {
-               setprompt(2);
-       }
+       setprompt_if(needprompt, 2);
        startlinno = g_parsefile->linno;
        for (;;) {                      /* until token or start of word found */
                c = pgetc_fast();
@@ -11625,8 +11844,7 @@ xxreadtoken(void)
                                break; /* return readtoken1(...) */
                        }
                        startlinno = ++g_parsefile->linno;
-                       if (doprompt)
-                               setprompt(2);
+                       setprompt_if(doprompt, 2);
                } else {
                        const char *p;
 
@@ -11672,9 +11890,7 @@ xxreadtoken(void)
                tokpushback = 0;
                return lasttoken;
        }
-       if (needprompt) {
-               setprompt(2);
-       }
+       setprompt_if(needprompt, 2);
        startlinno = g_parsefile->linno;
        for (;;) {      /* until token or start of word found */
                c = pgetc_fast();
@@ -11690,8 +11906,7 @@ xxreadtoken(void)
                case '\\':
                        if (pgetc() == '\n') {
                                startlinno = ++g_parsefile->linno;
-                               if (doprompt)
-                                       setprompt(2);
+                               setprompt_if(doprompt, 2);
                                continue;
                        }
                        pungetc();
@@ -11817,8 +12032,7 @@ parsecmd(int interact)
 
        tokpushback = 0;
        doprompt = interact;
-       if (doprompt)
-               setprompt(doprompt);
+       setprompt_if(doprompt, doprompt);
        needprompt = 0;
        t = readtoken();
        if (t == TEOF)
@@ -11842,10 +12056,8 @@ parseheredoc(void)
        heredoclist = NULL;
 
        while (here) {
-               if (needprompt) {
-                       setprompt(2);
-               }
-               readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
+               setprompt_if(needprompt, 2);
+               readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
                                here->eofmark, here->striptabs);
                n = stzalloc(sizeof(struct narg));
                n->narg.type = NARG;
@@ -11936,7 +12148,6 @@ evalcmd(int argc UNUSED_PARAM, char **argv)
                        p = grabstackstr(concat);
                }
                evalstring(p, ~SKIPEVAL);
-
        }
        return exitstatus;
 }
@@ -11966,9 +12177,7 @@ cmdloop(int top)
                inter = 0;
                if (iflag && top) {
                        inter++;
-#if ENABLE_ASH_MAIL
                        chkmail();
-#endif
                }
                n = parsecmd(inter);
 #if DEBUG
@@ -12060,8 +12269,10 @@ dotcmd(int argc, char **argv)
        /* "false; . empty_file; echo $?" should print 0, not 1: */
        exitstatus = 0;
 
+       /* This aborts if file isn't found, which is POSIXly correct.
+        * bash returns exitcode 1 instead.
+        */
        fullname = find_dot_file(argv[1]);
-
        argv += 2;
        argc -= 2;
        if (argc) { /* argc > 0, argv[0] != NULL */
@@ -12071,6 +12282,9 @@ dotcmd(int argc, char **argv)
                shellparam.p = argv;
        };
 
+       /* This aborts if file can't be opened, which is POSIXly correct.
+        * bash returns exitcode 1 instead.
+        */
        setinputfile(fullname, INPUT_PUSH_FILE);
        commandname = fullname;
        cmdloop(0);
@@ -12228,7 +12442,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
                        }
                        if ((act & DO_NOFUNC)
                         || !prefix(pathopt, "func")
-                       ) {     /* ignore unimplemented options */
+                       ) {     /* ignore unimplemented options */
                                continue;
                        }
                }
@@ -12417,6 +12631,15 @@ helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 }
 #endif /* FEATURE_SH_EXTRA_QUIET */
 
+#if MAX_HISTORY
+static int FAST_FUNC
+historycmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+       show_history(line_input_state);
+       return EXIT_SUCCESS;
+}
+#endif
+
 /*
  * The export and readonly commands.
  */
@@ -12427,9 +12650,27 @@ exportcmd(int argc UNUSED_PARAM, char **argv)
        char *name;
        const char *p;
        char **aptr;
-       int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
+       char opt;
+       int flag;
+       int flag_off;
+
+       /* "readonly" in bash accepts, but ignores -n.
+        * We do the same: it saves a conditional in nextopt's param.
+        */
+       flag_off = 0;
+       while ((opt = nextopt("np")) != '\0') {
+               if (opt == 'n')
+                       flag_off = VEXPORT;
+       }
+       flag = VEXPORT;
+       if (argv[0][0] == 'r') {
+               flag = VREADONLY;
+               flag_off = 0; /* readonly ignores -n */
+       }
+       flag_off = ~flag_off;
 
-       if (nextopt("p") != 'p') {
+       /*if (opt_p_not_specified) - bash doesnt check this. Try "export -p NAME" */
+       {
                aptr = argptr;
                name = *aptr;
                if (name) {
@@ -12440,15 +12681,19 @@ exportcmd(int argc UNUSED_PARAM, char **argv)
                                } else {
                                        vp = *findvar(hashvar(name), name);
                                        if (vp) {
-                                               vp->flags |= flag;
+                                               vp->flags = ((vp->flags | flag) & flag_off);
                                                continue;
                                        }
                                }
-                               setvar(name, p, flag);
+                               setvar(name, p, (flag & flag_off));
                        } while ((name = *++aptr) != NULL);
                        return 0;
                }
        }
+
+       /* No arguments. Show the list of exported or readonly vars.
+        * -n is ignored.
+        */
        showvars(argv[0], flag, 0);
        return 0;
 }
@@ -12601,6 +12846,10 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
                }
        }
 
+       /* "read -s" needs to save/restore termios, can't allow ^C
+        * to jump out of it.
+        */
+       INT_OFF;
        r = shell_builtin_read(setvar2,
                argptr,
                bltinlookup("IFS"), /* can be NULL */
@@ -12610,6 +12859,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
                opt_t,
                opt_u
        );
+       INT_ON;
 
        if ((uintptr_t)r > 1)
                ash_msg_and_raise_error(r);
@@ -12698,7 +12948,6 @@ ulimitcmd(int argc UNUSED_PARAM, char **argv)
 /*
  * Called to exit the shell.
  */
-static void exitshell(void) NORETURN;
 static void
 exitshell(void)
 {
@@ -12706,6 +12955,10 @@ exitshell(void)
        char *p;
        int status;
 
+#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
+       save_history(line_input_state);
+#endif
+
        status = exitstatus;
        TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
        if (setjmp(loc.loc)) {
@@ -12743,7 +12996,7 @@ init(void)
        /* bash re-enables SIGHUP which is SIG_IGNed on entry.
         * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
         */
-        signal(SIGHUP, SIG_DFL);
+       signal(SIGHUP, SIG_DFL);
 
        /* from var.c: */
        {
@@ -12758,17 +13011,38 @@ init(void)
                        }
                }
 
-               setvar("PPID", utoa(getppid()), 0);
-
+               setvar2("PPID", utoa(getppid()));
+#if ENABLE_ASH_BASH_COMPAT
+               p = lookupvar("SHLVL");
+               setvar2("SHLVL", utoa(p ? atoi(p) + 1 : 1));
+#endif
                p = lookupvar("PWD");
-               if (p)
+               if (p) {
                        if (*p != '/' || stat(p, &st1) || stat(".", &st2)
-                        || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
+                        || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
+                       ) {
                                p = '\0';
+                       }
+               }
                setpwd(p, 0);
        }
 }
 
+
+//usage:#define ash_trivial_usage
+//usage:       "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
+//usage:#define ash_full_usage "\n\n"
+//usage:       "Unix shell interpreter"
+
+//usage:#if ENABLE_FEATURE_SH_IS_ASH
+//usage:# define sh_trivial_usage ash_trivial_usage
+//usage:# define sh_full_usage    ash_full_usage
+//usage:#endif
+//usage:#if ENABLE_FEATURE_BASH_IS_ASH
+//usage:# define bash_trivial_usage ash_trivial_usage
+//usage:# define bash_full_usage    ash_full_usage
+//usage:#endif
+
 /*
  * Process the shell command line arguments.
  */
@@ -12786,7 +13060,7 @@ procargs(char **argv)
        for (i = 0; i < NOPTS; i++)
                optlist[i] = 2;
        argptr = xargv;
-       if (options(1)) {
+       if (options(/*cmdline:*/ 1)) {
                /* it already printed err message */
                raise_exception(EXERROR);
        }
@@ -12916,10 +13190,12 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
                if (e == EXERROR)
                        exitstatus = 2;
                s = state;
-               if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
+               if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
                        exitshell();
-               if (e == EXINT)
+               }
+               if (e == EXINT) {
                        outcslow('\n', stderr);
+               }
 
                popstackmark(&smark);
                FORCE_INT_ON; /* enable interrupts */
@@ -12943,28 +13219,21 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
        setstackmark(&smark);
        procargs(argv);
 
-#if ENABLE_FEATURE_EDITING_SAVEHISTORY
-       if (iflag) {
-               const char *hp = lookupvar("HISTFILE");
-
-               if (hp == NULL) {
-                       hp = lookupvar("HOME");
-                       if (hp != NULL) {
-                               char *defhp = concat_path_file(hp, ".ash_history");
-                               setvar("HISTFILE", defhp, 0);
-                               free(defhp);
-                       }
-               }
-       }
-#endif
-       if (/* argv[0] && */ argv[0][0] == '-')
+       if (argv[0] && argv[0][0] == '-')
                isloginsh = 1;
        if (isloginsh) {
+               const char *hp;
+
                state = 1;
                read_profile("/etc/profile");
  state1:
                state = 2;
-               read_profile(".profile");
+               hp = lookupvar("HOME");
+               if (hp) {
+                       hp = concat_path_file(hp, ".profile");
+                       read_profile(hp);
+                       free((char*)hp);
+               }
        }
  state2:
        state = 3;
@@ -12993,11 +13262,24 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
        }
 
        if (sflag || minusc == NULL) {
-#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
+#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
                if (iflag) {
                        const char *hp = lookupvar("HISTFILE");
+                       if (!hp) {
+                               hp = lookupvar("HOME");
+                               if (hp) {
+                                       hp = concat_path_file(hp, ".ash_history");
+                                       setvar2("HISTFILE", hp);
+                                       free((char*)hp);
+                                       hp = lookupvar("HISTFILE");
+                               }
+                       }
                        if (hp)
                                line_input_state->hist_file = hp;
+# if ENABLE_FEATURE_SH_HISTFILESIZE
+                       hp = lookupvar("HISTFILESIZE");
+                       line_input_state->max_history = size_from_HISTFILESIZE(hp);
+# endif
                }
 #endif
  state4: /* XXX ??? - why isn't this before the "if" statement */
@@ -13012,6 +13294,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
                _mcleanup();
        }
 #endif
+       TRACE(("End of main reached\n"));
        exitshell();
        /* NOTREACHED */
 }
index 68d9072..f698408 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2008 by Denys Vlasenko <vda.linux@googlemail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 struct globals_misc;
index 3ea7ce6..9b9ca8e 100644 (file)
@@ -55,28 +55,28 @@ Format: 'expected actual'
 30 30
 20 20
 30 30
-./arith.tests: line 117: syntax error: 1 ? 20 : x+=2
+./arith.tests: line 117: arithmetic syntax error
 6 6
 6,5,3 6,5,3
 263 263
 255 255
 40 40
-./arith.tests: line 163: syntax error:  7 = 43 
+./arith.tests: line 163: arithmetic syntax error
 ./arith.tests: line 165: divide by zero
-./arith.tests: let: line 166: syntax error: jv += $iv
-./arith.tests: line 167: syntax error:  jv += $iv 
-./arith.tests: let: line 168: syntax error: rv = 7 + (43 * 6
+./arith.tests: let: line 166: arithmetic syntax error
+./arith.tests: line 167: arithmetic syntax error
+./arith.tests: let: line 168: arithmetic syntax error
 abc
 def
 ghi
-./arith.tests: line 191: syntax error:  ( 4 + A ) + 4 
+./arith.tests: line 191: arithmetic syntax error
 16 16
-./arith.tests: line 196: syntax error:  4 ? : 3 + 5 
-./arith.tests: line 197: syntax error:  1 ? 20 
-./arith.tests: line 198: syntax error:  4 ? 20 : 
+./arith.tests: line 196: arithmetic syntax error
+./arith.tests: line 197: malformed ?: operator
+./arith.tests: line 198: arithmetic syntax error
 9 9
-./arith.tests: line 205: syntax error:  0 && B=42 
-./arith.tests: line 208: syntax error:  1 || B=88 
+./arith.tests: line 205: arithmetic syntax error
+./arith.tests: line 208: arithmetic syntax error
 9 9
 9 9
 9 9
@@ -97,18 +97,18 @@ ghi
 3 3
 4 4
 4 4
-./arith.tests: line 257: syntax error:  7-- 
-./arith.tests: line 259: syntax error:  --x=7 
-./arith.tests: line 260: syntax error:  ++x=7 
-./arith.tests: line 262: syntax error:  x++=7 
-./arith.tests: line 263: syntax error:  x--=7 
+./arith.tests: line 257: arithmetic syntax error
+./arith.tests: line 259: arithmetic syntax error
+./arith.tests: line 260: arithmetic syntax error
+./arith.tests: line 262: arithmetic syntax error
+./arith.tests: line 263: arithmetic syntax error
 4 4
 7 7
 -7 -7
-./arith1.sub: line 2: syntax error:  4-- 
-./arith1.sub: line 3: syntax error:  4++ 
-./arith1.sub: line 4: syntax error:  4 -- 
-./arith1.sub: line 5: syntax error:  4 ++ 
+./arith1.sub: line 2: arithmetic syntax error
+./arith1.sub: line 3: arithmetic syntax error
+./arith1.sub: line 4: arithmetic syntax error
+./arith1.sub: line 5: arithmetic syntax error
 6 6
 3 3
 7 7
@@ -119,19 +119,19 @@ ghi
 2 2
 -2 -2
 1 1
-./arith1.sub: line 37: syntax error:  +++7 
-./arith2.sub: line 2: syntax error:  --7 
-./arith2.sub: line 3: syntax error:  ++7 
-./arith2.sub: line 4: syntax error:  -- 7 
-./arith2.sub: line 5: syntax error:  ++ 7 
+./arith1.sub: line 37: arithmetic syntax error
+./arith2.sub: line 2: arithmetic syntax error
+./arith2.sub: line 3: arithmetic syntax error
+./arith2.sub: line 4: arithmetic syntax error
+./arith2.sub: line 5: arithmetic syntax error
 5 5
 1 1
 4 4
 0 0
-./arith2.sub: line 42: syntax error:  -- - 7 
-./arith2.sub: line 47: syntax error:  ++ + 7 
+./arith2.sub: line 42: arithmetic syntax error
+./arith2.sub: line 47: arithmetic syntax error
 8 12
-./arith.tests: line 290: syntax error: a b
+./arith.tests: line 290: arithmetic syntax error
 42
 42
 42
diff --git a/shell/ash_test/ash-misc/echo_write_error.right b/shell/ash_test/ash-misc/echo_write_error.right
new file mode 100644 (file)
index 0000000..3e91a13
--- /dev/null
@@ -0,0 +1,2 @@
+ash: write error: Broken pipe
+Ok: 1
diff --git a/shell/ash_test/ash-misc/echo_write_error.tests b/shell/ash_test/ash-misc/echo_write_error.tests
new file mode 100644 (file)
index 0000000..0a40c9f
--- /dev/null
@@ -0,0 +1,7 @@
+trap "" PIPE
+
+{
+sleep 1
+echo Cant write this - get EPIPE
+echo Ok: $? >&2
+} | { true; }
old mode 100644 (file)
new mode 100755 (executable)
diff --git a/shell/ash_test/ash-redir/redirA.right b/shell/ash_test/ash-redir/redirA.right
new file mode 100644 (file)
index 0000000..31406e3
--- /dev/null
@@ -0,0 +1,2 @@
+tmp11
+tmp11
diff --git a/shell/ash_test/ash-redir/redirA.tests b/shell/ash_test/ash-redir/redirA.tests
new file mode 100755 (executable)
index 0000000..56833f9
--- /dev/null
@@ -0,0 +1,11 @@
+x="tmp11:tmp22"
+
+# Bug was incorrectly expanding variables in >redir
+echo "${x%:*}" >"${x%:*}"
+echo tmp1*
+rm tmp1*
+
+# Also try unquoted
+echo "${x%:*}" >${x%:*}
+echo tmp1*
+rm tmp1*
diff --git a/shell/ash_test/ash-signals/sigint1.right b/shell/ash_test/ash-signals/sigint1.right
new file mode 100644 (file)
index 0000000..a9094b0
--- /dev/null
@@ -0,0 +1 @@
+Sending SIGINT to main shell PID
diff --git a/shell/ash_test/ash-signals/sigint1.tests b/shell/ash_test/ash-signals/sigint1.tests
new file mode 100755 (executable)
index 0000000..3d483d3
--- /dev/null
@@ -0,0 +1,41 @@
+# What should happen if non-interactive shell gets SIGINT?
+
+(sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) &
+
+# We create a child which exits with 0 even on SIGINT
+# (The complex command is necessary only if SIGINT is generated by ^C,
+# in this testcase even bare "sleep 2" would do because
+# in the testcase we don't send SIGINT *to the child*...)
+$THIS_SH -c 'trap "exit 0" SIGINT; sleep 2'
+
+# In one second, we (main shell) get SIGINT here.
+# The question is whether we should, or should not, exit.
+
+# bash will not stop here. It will execute next command(s).
+
+# The rationale for this is described here:
+# http://www.cons.org/cracauer/sigint.html
+#
+# Basically, bash will not exit on SIGINT immediately if it waits
+# for a child. It will wait for the child to exit.
+# If child exits NOT by dying on SIGINT, then bash will not exit.
+#
+# The idea is that the following script:
+# | emacs file.txt
+# | more cmds
+# User may use ^C to interrupt editor's ops like search. But then
+# emacs exits normally. User expects that script doesn't stop.
+#
+# This is a nice idea, but detecting "did process really exit
+# with SIGINT?" is racy. Consider:
+# | bash -c 'while true; do /bin/true; done'
+# When ^C is pressed while bash waits for /bin/true to exit,
+# it may happen that /bin/true exits with exitcode 0 before
+# ^C is delivered to it as SIGINT. bash will see SIGINT, then
+# it will see that child exited with 0, and bash will NOT EXIT.
+
+# Therefore we do not implement bash behavior.
+# I'd say that emacs need to put itself into a separate pgrp
+# to isolate shell from getting stray SIGINTs from ^C.
+
+echo Next command after SIGINT was executed
diff --git a/shell/ash_test/ash-signals/signal7.right b/shell/ash_test/ash-signals/signal7.right
new file mode 100644 (file)
index 0000000..ba7453e
--- /dev/null
@@ -0,0 +1 @@
+Bug detected: 0
diff --git a/shell/ash_test/ash-signals/signal7.tests b/shell/ash_test/ash-signals/signal7.tests
new file mode 100755 (executable)
index 0000000..c2b1381
--- /dev/null
@@ -0,0 +1,18 @@
+bug() {
+       trap : exit
+       # Bug was causing sh to be run in subshell,
+       # as if this line is replaced with (sh -c ...; exit $?) &
+       # here:
+       sh -c 'echo REAL_CHILD=$$' &
+       echo PARENTS_IDEA_OF_CHILD=$!
+       wait  # make sure bkgd shell completes
+}
+
+bug | {
+while read varval; do
+       eval $varval
+done
+test x"$REAL_CHILD" != x"" \
+&& test x"$REAL_CHILD" = x"$PARENTS_IDEA_OF_CHILD"
+echo "Bug detected: $?"
+}
diff --git a/shell/ash_test/ash-signals/signal8.right b/shell/ash_test/ash-signals/signal8.right
new file mode 100644 (file)
index 0000000..39572f3
--- /dev/null
@@ -0,0 +1,3 @@
+Removing traps
+End of exit_func
+Done: 0
diff --git a/shell/ash_test/ash-signals/signal8.tests b/shell/ash_test/ash-signals/signal8.tests
new file mode 100755 (executable)
index 0000000..731af74
--- /dev/null
@@ -0,0 +1,18 @@
+"$THIS_SH" -c '
+exit_func() {
+    echo "Removing traps"
+    trap - EXIT TERM INT
+    echo "End of exit_func"
+}
+set -e
+trap exit_func EXIT TERM INT
+sleep 2
+exit 77
+' &
+
+sleep 1
+# BUG: ash kills -PGRP, but in non-interactive shell we do not create pgrps!
+# In this case, bash kills by PID, not PGRP.
+kill -TERM %1
+wait
+echo Done: $?
diff --git a/shell/ash_test/ash-signals/signal9.right b/shell/ash_test/ash-signals/signal9.right
new file mode 100644 (file)
index 0000000..39572f3
--- /dev/null
@@ -0,0 +1,3 @@
+Removing traps
+End of exit_func
+Done: 0
diff --git a/shell/ash_test/ash-signals/signal9.tests b/shell/ash_test/ash-signals/signal9.tests
new file mode 100755 (executable)
index 0000000..18e7101
--- /dev/null
@@ -0,0 +1,21 @@
+# Note: the inner script is a test which checks for a different bug
+# (ordering between INT handler and exit on "set -e"),
+# but so far I did not figure out how to simulate it non-interactively.
+
+"$THIS_SH" -c '
+exit_func() {
+    echo "Removing traps"
+    trap - EXIT TERM INT
+    echo "End of exit_func"
+}
+set -e
+trap exit_func EXIT TERM INT
+sleep 2
+exit 77
+' &
+
+child=$!
+sleep 1
+kill -TERM $child
+wait
+echo Done: $?
index f7f1479..a97c850 100644 (file)
@@ -1,20 +1,20 @@
-a041#c
-a041#c
-a\041#c
-a\041#c
-a\041#c
-a\041#c
-a\041#c
-a\041#c
-a\041#c
-a\c
-a\c
-a\c
-a\\c
-a\\c
-a\\c
-a\tc
-a\tc
-a\tc
-atc
-a\tc
+a041#c
+a041#c
+a\041#c
+a\041#c
+a\041#c
+a\041#c
+a\041#c
+a\041#c
+a\041#c
+10 a\c
+11 a\c
+12 a\c
+13 a\\c
+14 a\\c
+15 a\\c
+16 a\tc
+17 a\tc
+18 a\tc
+19 atc
+20 a\tc
index b905027..146dbb6 100755 (executable)
@@ -1,41 +1,41 @@
 a='abc'
 r=${a//b/\041#}
-echo $r
-echo ${a//b/\041#}
-echo "${a//b/\041#}"
+echo $r
+echo ${a//b/\041#}
+echo "${a//b/\041#}"
 
 a='abc'
 r=${a//b/\\041#}
-echo $r
-echo ${a//b/\\041#}
-echo "${a//b/\\041#}"
+echo $r
+echo ${a//b/\\041#}
+echo "${a//b/\\041#}"
 
 a='abc'
 b='\041#'
 r=${a//b/$b}
-echo $r
-echo ${a//b/$b}
-echo "${a//b/$b}"
+echo $r
+echo ${a//b/$b}
+echo "${a//b/$b}"
 
 a='abc'
 b='\'
 r="${a//b/$b}"
-echo $r
-echo ${a//b/$b}
-echo "${a//b/$b}"
+echo 10 $r
+echo 11 ${a//b/$b}
+echo 12 "${a//b/$b}"
 
 a='abc'
 b='\\'
 r="${a//b/$b}"
-echo $r
-echo ${a//b/$b}
-echo "${a//b/$b}"
+echo 13 $r
+echo 14 ${a//b/$b}
+echo 15 "${a//b/$b}"
 
 a='abc'
 b='\t'
 r="${a//b/$b}"
-echo $r
-echo ${a//b/$b}
-echo "${a//b/$b}"
-echo ${a//b/\t}
-echo "${a//b/\t}"
+echo 16 $r
+echo 17 ${a//b/$b}
+echo 18 "${a//b/$b}"
+echo 19 ${a//b/\t}
+echo 20 "${a//b/\t}"
diff --git a/shell/ash_test/ash-vars/var_bash4.right b/shell/ash_test/ash-vars/var_bash4.right
new file mode 100644 (file)
index 0000000..600e853
--- /dev/null
@@ -0,0 +1,23 @@
+Source:        a*b\*c
+Replace str:   _\\_\z_
+Pattern:       single backslash and star: "replace literal star"
+In assignment: a_\_z_b\*c
+Unquoted:      a_\_z_b\*c
+Quoted:        a_\_\z_b\*c
+Pattern:       double backslash and star: "replace backslash and everything after it"
+In assignment: a*b_\_z_
+Unquoted:      a*b_\_z_
+Quoted:        a*b_\_\z_
+
+Source:        a\bc
+Replace str:   _\\_\z_
+Pattern:       single backslash and b: "replace b"
+In assignment: a\_\_z_c
+Unquoted:      a\_\_z_c
+Quoted:        a\_\_\z_c
+Pattern:       double backslash and b: "replace backslash and b"
+In assignment: a_\_z_c
+Unquoted:      a_\_z_c
+Quoted:        a_\_\z_c
+
+Done: 0
diff --git a/shell/ash_test/ash-vars/var_bash4.tests b/shell/ash_test/ash-vars/var_bash4.tests
new file mode 100755 (executable)
index 0000000..d547061
--- /dev/null
@@ -0,0 +1,47 @@
+# This testcase demonstrates that backslashes are treated differently
+# in 1st and 2nd parts of ${var/search/repl}:
+# if quoted ("${var/search/repl}"), and repl contains \a (a non-special char),
+# the backslash in repl stays; if unquoted, backslash is removed.
+# But search part does not act like that: \a is always converted to just a,
+# even in quotes.
+#
+# bash4 (and probably bash3 too): "Quoted:" results are different from
+# unquoted and assignment expansions - they have a backslash before z.
+
+v='a*b\*c'
+echo 'Source:       ' "$v"
+echo 'Replace str:  ' '_\\_\z_'
+
+echo 'Pattern:      ' 'single backslash and star: "replace literal star"'
+r=${v/\*/_\\_\z_}
+echo 'In assignment:' "$r"
+echo 'Unquoted:     ' ${v/\*/_\\_\z_}
+echo 'Quoted:       ' "${v/\*/_\\_\z_}"
+
+echo 'Pattern:      ' 'double backslash and star: "replace backslash and everything after it"'
+r=${v/\\*/_\\_\z_}
+echo 'In assignment:' "$r"
+echo 'Unquoted:     '  ${v/\\*/_\\_\z_}
+echo 'Quoted:       ' "${v/\\*/_\\_\z_}"
+
+echo
+
+v='a\bc'
+echo 'Source:       ' "$v"
+echo 'Replace str:  ' '_\\_\z_'
+
+echo 'Pattern:      ' 'single backslash and b: "replace b"'
+r=${v/\b/_\\_\z_}
+echo 'In assignment:' "$r"
+echo 'Unquoted:     '  ${v/\b/_\\_\z_}
+echo 'Quoted:       ' "${v/\b/_\\_\z_}"
+
+echo 'Pattern:      ' 'double backslash and b: "replace backslash and b"'
+r=${v/\\b/_\\_\z_}
+echo 'In assignment:' "$r"
+echo 'Unquoted:     '  ${v/\\b/_\\_\z_}
+echo 'Quoted:       ' "${v/\\b/_\\_\z_}"
+
+echo
+
+echo Done: $?
diff --git a/shell/ash_test/ash-vars/var_bash5.right b/shell/ash_test/ash-vars/var_bash5.right
new file mode 100644 (file)
index 0000000..278ed32
--- /dev/null
@@ -0,0 +1,4 @@
+a/
+a/d
+a/e/f
+Done: 0
diff --git a/shell/ash_test/ash-vars/var_bash5.tests b/shell/ash_test/ash-vars/var_bash5.tests
new file mode 100755 (executable)
index 0000000..7f482a5
--- /dev/null
@@ -0,0 +1,11 @@
+# This testcase checks whether slashes in ${v/a/b} are parsed before
+# or after expansions
+
+v='a/b/c'
+s='b/c'
+r='e/f'
+echo "${v/$s}"
+echo "${v/$s/d}"
+echo "${v/$s/$r}"
+
+echo Done: $?
index fb48d9c..42a5fea 100644 (file)
 
 void strprint();
 
-int
-main(argc, argv)
-int    argc;
-char   **argv;
+int main(int argc, char **argv)
 {
-       register int    i;
+       int i;
 
        for (i = 1; i < argc; i++) {
                printf("argv[%d] = <", i);
@@ -44,11 +41,9 @@ char **argv;
        exit(EXIT_SUCCESS);
 }
 
-void
-strprint(str)
-char   *str;
+void strprint(char *str)
 {
-       register unsigned char *s;
+       unsigned char *s;
 
        for (s = (unsigned char *)str; s && *s; s++) {
                if (*s < ' ') {
index bf876f6..cbaa59b 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 
-int
-main(argc, argv)
-int    argc;
-char   **argv;
+int main(int argc, char **argv)
 {
        argv++;
 
diff --git a/shell/bbsh.c b/shell/bbsh.c
deleted file mode 100644 (file)
index 83132f9..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-/* vi: set ts=4 :
- *
- * bbsh - busybox shell
- *
- * Copyright 2006 Rob Landley <rob@landley.net>
- *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
- */
-
-// A section of code that gets repeatedly or conditionally executed is stored
-// as a string and parsed each time it's run.
-
-
-
-// Wheee, debugging.
-
-// Terminal control
-#define ENABLE_BBSH_TTY        0
-
-// &, fg, bg, jobs.  (ctrl-z with tty.)
-#define ENABLE_BBSH_JOBCTL     0
-
-// Flow control (if, while, for, functions { })
-#define ENABLE_BBSH_FLOWCTL    0
-
-#define ENABLE_BBSH_ENVVARS    0  // Environment variable support
-
-// Local and synthetic variables, fancy prompts, set, $?, etc.
-#define ENABLE_BBSH_LOCALVARS  0
-
-// Pipes and redirects: | > < >> << && || & () ;
-#define ENABLE_BBSH_PIPES      0
-
-/* Fun:
-
-  echo `echo hello#comment " woot` and more
-*/
-
-#include "libbb.h"
-
-// A single executable, its arguments, and other information we know about it.
-#define BBSH_FLAG_EXIT    1
-#define BBSH_FLAG_SUSPEND 2
-#define BBSH_FLAG_PIPE    4
-#define BBSH_FLAG_AND     8
-#define BBSH_FLAG_OR      16
-#define BBSH_FLAG_AMP     32
-#define BBSH_FLAG_SEMI    64
-#define BBSH_FLAG_PAREN   128
-
-// What we know about a single process.
-struct command {
-       struct command *next;
-       int flags;              // exit, suspend, && ||
-       int pid;                // pid (or exit code)
-       int argc;
-       char *argv[];
-};
-
-// A collection of processes piped into/waiting on each other.
-struct pipeline {
-       struct pipeline *next;
-       int job_id;
-       struct command *cmd;
-       char *cmdline;
-       int cmdlinelen;
-};
-
-static void free_list(void *list, void (*freeit)(void *data))
-{
-       while (list) {
-               void **next = (void **)list;
-               void *list_next = *next;
-               freeit(list);
-               free(list);
-               list = list_next;
-       }
-}
-
-// Parse one word from the command line, appending one or more argv[] entries
-// to struct command.  Handles environment variable substitution and
-// substrings.  Returns pointer to next used byte, or NULL if it
-// hit an ending token.
-static char *parse_word(char *start, struct command **cmd)
-{
-       char *end;
-
-       // Detect end of line (and truncate line at comment)
-       if (ENABLE_BBSH_PIPES && strchr("><&|(;", *start)) return 0;
-
-       // Grab next word.  (Add dequote and envvar logic here)
-       end = start;
-       end = skip_non_whitespace(end);
-       (*cmd)->argv[(*cmd)->argc++] = xstrndup(start, end-start);
-
-       // Allocate more space if there's no room for NULL terminator.
-
-       if (!((*cmd)->argc & 7))
-                       *cmd = xrealloc(*cmd,
-                                       sizeof(struct command) + ((*cmd)->argc+8)*sizeof(char *));
-       (*cmd)->argv[(*cmd)->argc] = 0;
-       return end;
-}
-
-// Parse a line of text into a pipeline.
-// Returns a pointer to the next line.
-
-static char *parse_pipeline(char *cmdline, struct pipeline *line)
-{
-       struct command **cmd = &(line->cmd);
-       char *start = line->cmdline = cmdline;
-
-       if (!cmdline) return 0;
-
-       if (ENABLE_BBSH_JOBCTL) line->cmdline = cmdline;
-
-       // Parse command into argv[]
-       for (;;) {
-               char *end;
-
-               // Skip leading whitespace and detect end of line.
-               start = skip_whitespace(start);
-               if (!*start || *start=='#') {
-                       if (ENABLE_BBSH_JOBCTL) line->cmdlinelen = start-cmdline;
-                       return 0;
-               }
-
-               // Allocate next command structure if necessary
-               if (!*cmd) *cmd = xzalloc(sizeof(struct command)+8*sizeof(char *));
-
-               // Parse next argument and add the results to argv[]
-               end = parse_word(start, cmd);
-
-               // If we hit the end of this command, how did it end?
-               if (!end) {
-                       if (ENABLE_BBSH_PIPES && *start) {
-                               if (*start==';') {
-                                       start++;
-                                       break;
-                               }
-                               // handle | & < > >> << || &&
-                       }
-                       break;
-               }
-               start = end;
-       }
-
-       if (ENABLE_BBSH_JOBCTL) line->cmdlinelen = start-cmdline;
-
-       return start;
-}
-
-// Execute the commands in a pipeline
-static int run_pipeline(struct pipeline *line)
-{
-       struct command *cmd = line->cmd;
-       if (!cmd || !cmd->argc) return 0;
-
-       // Handle local commands.  This is totally fake and plastic.
-       if (cmd->argc==2 && !strcmp(cmd->argv[0],"cd"))
-               chdir(cmd->argv[1]);
-       else if (!strcmp(cmd->argv[0],"exit"))
-               exit(cmd->argc>1 ? atoi(cmd->argv[1]) : 0);
-       else {
-               int status;
-               pid_t pid=fork();
-               if (!pid) {
-                       run_applet_and_exit(cmd->argv[0],cmd->argc,cmd->argv);
-                       execvp(cmd->argv[0],cmd->argv);
-                       printf("No %s", cmd->argv[0]);
-                       exit(EXIT_FAILURE);
-               } else waitpid(pid, &status, 0);
-       }
-
-       return 0;
-}
-
-static void free_cmd(void *data)
-{
-       struct command *cmd=(struct command *)data;
-
-       while (cmd->argc) free(cmd->argv[--cmd->argc]);
-}
-
-
-static void handle(char *command)
-{
-       struct pipeline line;
-       char *start = command;
-
-       for (;;) {
-               memset(&line,0,sizeof(struct pipeline));
-               start = parse_pipeline(start, &line);
-               if (!line.cmd) break;
-
-               run_pipeline(&line);
-               free_list(line.cmd, free_cmd);
-       }
-}
-
-int bbsh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int bbsh_main(int argc UNUSED_PARAM, char **argv)
-{
-       char *command=NULL;
-       FILE *f;
-
-       getopt32(argv, "c:", &command);
-
-       f = argv[optind] ? xfopen_for_read(argv[optind]) : NULL;
-       if (command) handle(command);
-       else {
-               unsigned cmdlen=0;
-               for (;;) {
-                       if (!f) putchar('$');
-                       if (1 > getline(&command, &cmdlen, f ? f : stdin)) break;
-
-                       handle(command);
-               }
-               if (ENABLE_FEATURE_CLEAN_UP) free(command);
-       }
-
-       return 1;
-}
index 67736ad..f9b59c2 100644 (file)
@@ -1,11 +1,74 @@
 /* vi: set sw=4 ts=4: */
 /*
- * Licensed under GPLv2
- *
  * Copyright (c) 2007 Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 
+//applet:IF_CTTYHACK(APPLET(cttyhack, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_CTTYHACK) += cttyhack.o
+
+//config:config CTTYHACK
+//config:      bool "cttyhack"
+//config:      default y
+//config:      help
+//config:        One common problem reported on the mailing list is the "can't
+//config:        access tty; job control turned off" error message, which typically
+//config:        appears when one tries to use a shell with stdin/stdout on
+//config:        /dev/console.
+//config:        This device is special - it cannot be a controlling tty.
+//config:
+//config:        The proper solution is to use the correct device instead of
+//config:        /dev/console.
+//config:
+//config:        cttyhack provides a "quick and dirty" solution to this problem.
+//config:        It analyzes stdin with various ioctls, trying to determine whether
+//config:        it is a /dev/ttyN or /dev/ttySN (virtual terminal or serial line).
+//config:        On Linux it also checks sysfs for a pointer to the active console.
+//config:        If cttyhack is able to find the real console device, it closes
+//config:        stdin/out/err and reopens that device.
+//config:        Then it executes the given program. Opening the device will make
+//config:        that device a controlling tty. This may require cttyhack
+//config:        to be a session leader.
+//config:
+//config:        Example for /etc/inittab (for busybox init):
+//config:
+//config:        ::respawn:/bin/cttyhack /bin/sh
+//config:
+//config:        Starting an interactive shell from boot shell script:
+//config:
+//config:        setsid cttyhack sh
+//config:
+//config:        Giving controlling tty to shell running with PID 1:
+//config:
+//config:        # exec cttyhack sh
+//config:
+//config:        Without cttyhack, you need to know exact tty name,
+//config:        and do something like this:
+//config:
+//config:        # exec setsid sh -c 'exec sh </dev/tty1 >/dev/tty1 2>&1'
+//config:
+//config:        Starting getty on a controlling tty from a shell script:
+//config:
+//config:        # getty 115200 $(cttyhack)
+
+//usage:#define cttyhack_trivial_usage
+//usage:       "[PROG ARGS]"
+//usage:#define cttyhack_full_usage "\n\n"
+//usage:       "Give PROG a controlling tty if possible."
+//usage:     "\nExample for /etc/inittab (for busybox init):"
+//usage:     "\n       ::respawn:/bin/cttyhack /bin/sh"
+//usage:     "\nGiving controlling tty to shell running with PID 1:"
+//usage:     "\n       $ exec cttyhack sh"
+//usage:     "\nStarting interactive shell from boot shell script:"
+//usage:     "\n       setsid cttyhack sh"
+
+#if !defined(__linux__) && !defined(TIOCGSERIAL) && !ENABLE_WERROR
+# warning cttyhack will not be able to detect a controlling tty on this system
+#endif
+
 /* From <linux/vt.h> */
 struct vt_stat {
        unsigned short v_active;        /* active vt */
@@ -48,38 +111,78 @@ int cttyhack_main(int argc UNUSED_PARAM, char **argv)
                char paranoia[sizeof(struct serial_struct) * 3];
        } u;
 
-       if (!*++argv) {
-               bb_show_usage();
-       }
-
        strcpy(console, "/dev/tty");
        fd = open(console, O_RDWR);
-       if (fd >= 0) {
-               /* We already have ctty, nothing to do */
-               close(fd);
-       } else {
+       if (fd < 0) {
                /* We don't have ctty (or don't have "/dev/tty" node...) */
-               if (ioctl(0, TIOCGSERIAL, &u.sr) == 0) {
-                       /* this is a serial console */
-                       sprintf(console + 8, "S%d", u.sr.line);
-               } else if (ioctl(0, VT_GETSTATE, &u.vt) == 0) {
-                       /* this is linux virtual tty */
-                       sprintf(console + 8, "S%d" + 1, u.vt.v_active);
-               }
-               if (console[8]) {
-                       fd = xopen(console, O_RDWR);
-                       //bb_error_msg("switching to '%s'", console);
-                       dup2(fd, 0);
-                       dup2(fd, 1);
-                       dup2(fd, 2);
-                       while (fd > 2)
-                               close(fd--);
-                       /* Some other session may have it as ctty,
-                        * steal it from them:
+               do {
+#ifdef __linux__
+                       /* Note that this method does not use _stdin_.
+                        * Thus, "cttyhack </dev/something" can't be used.
+                        * However, this method is more reliable than
+                        * TIOCGSERIAL check, which assumes that all
+                        * serial lines follow /dev/ttySn convention -
+                        * which is not always the case.
+                        * Therefore, we use this method first:
                         */
-                       ioctl(0, TIOCSCTTY, 1);
-               }
+                       int s = open_read_close("/sys/class/tty/console/active",
+                               console + 5, sizeof(console) - 5);
+                       if (s > 0) {
+                               char *last;
+                               /* Found active console via sysfs (Linux 2.6.38+).
+                                * It looks like "[tty0 ]ttyS0\n" so zap the newline:
+                                */
+                               console[4 + s] = '\0';
+                               /* If there are multiple consoles,
+                                * take the last one:
+                                */
+                               last = strrchr(console + 5, ' ');
+                               if (last)
+                                       overlapping_strcpy(console + 5, last + 1);
+                               break;
+                       }
+
+                       if (ioctl(0, VT_GETSTATE, &u.vt) == 0) {
+                               /* this is linux virtual tty */
+                               sprintf(console + 8, "S%u" + 1, (int)u.vt.v_active);
+                               break;
+                       }
+#endif
+#ifdef TIOCGSERIAL
+                       if (ioctl(0, TIOCGSERIAL, &u.sr) == 0) {
+                               /* this is a serial console; assuming it is named /dev/ttySn */
+                               sprintf(console + 8, "S%u", (int)u.sr.line);
+                               break;
+                       }
+#endif
+                       /* nope, could not find it */
+                       console[0] = '\0';
+               } while (0);
        }
 
+       argv++;
+       if (!argv[0]) {
+               if (!console[0])
+                       return EXIT_FAILURE;
+               puts(console);
+               return EXIT_SUCCESS;
+       }
+
+       if (fd < 0) {
+               fd = open_or_warn(console, O_RDWR);
+               if (fd < 0)
+                       goto ret;
+       }
+       //bb_error_msg("switching to '%s'", console);
+       dup2(fd, 0);
+       dup2(fd, 1);
+       dup2(fd, 2);
+       while (fd > 2)
+               close(fd--);
+       /* Some other session may have it as ctty,
+        * try to steal it from them:
+        */
+       ioctl(0, TIOCSCTTY, 1);
+ ret:
        BB_EXECVP_or_die(argv);
 }
index 31ca22a..9271934 100644 (file)
@@ -8,6 +8,8 @@
  * Copyright (C) 2000,2001  Larry Doolittle <larry@doolittle.boa.org>
  * Copyright (C) 2008,2009  Denys Vlasenko <vda.linux@googlemail.com>
  *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
  * Credits:
  *      The parser routines proper are all original material, first
  *      written Dec 2000 and Jan 2001 by Larry Doolittle.  The
@@ -50,8 +52,6 @@
  *
  * Bash compat TODO:
  *      redirection of stdout+stderr: &> and >&
- *      subst operator: ${var/[/]expr/expr}
- *      brace expansion: one/{two,three,four}
  *      reserved words: function select
  *      advanced test: [[ ]]
  *      process substitution: <(list) and >(list)
@@ -64,7 +64,9 @@
  *          The EXPR is evaluated according to ARITHMETIC EVALUATION.
  *          This is exactly equivalent to let "EXPR".
  *      $[EXPR]: synonym for $((EXPR))
- *      export builtin should be special, its arguments are assignments
+ *
+ * Won't do:
+ *      In bash, export builtin is special, its arguments are assignments
  *          and therefore expansion of them should be "one-word" expansion:
  *              $ export i=`echo 'a  b'` # export has one arg: "i=a  b"
  *          compare with:
  *              aaa  bbb
  *              $ "export" i=`echo 'aaa  bbb'`; echo "$i"
  *              aaa
- *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
  */
-#include "busybox.h"  /* for APPLET_IS_NOFORK/NOEXEC */
-#include <malloc.h>   /* for malloc_trim */
+#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
+       || defined(__APPLE__) \
+    )
+# include <malloc.h>   /* for malloc_trim */
+#endif
 #include <glob.h>
 /* #include <dmalloc.h> */
 #if ENABLE_HUSH_CASE
 # include <fnmatch.h>
 #endif
 
+#include "busybox.h"  /* for APPLET_IS_NOFORK/NOEXEC */
+#include "unicode.h"
 #include "shell_common.h"
 #include "math.h"
 #include "match.h"
 # define PIPE_BUF 4096  /* amount of buffering in a pipe */
 #endif
 
+//config:config HUSH
+//config:      bool "hush"
+//config:      default y
+//config:      help
+//config:        hush is a small shell (25k). It handles the normal flow control
+//config:        constructs such as if/then/elif/else/fi, for/in/do/done, while loops,
+//config:        case/esac. Redirections, here documents, $((arithmetic))
+//config:        and functions are supported.
+//config:
+//config:        It will compile and work on no-mmu systems.
+//config:
+//config:        It does not handle select, aliases, tilde expansion,
+//config:        &>file and >&file redirection of stdout+stderr.
+//config:
+//config:config HUSH_BASH_COMPAT
+//config:      bool "bash-compatible extensions"
+//config:      default y
+//config:      depends on HUSH
+//config:      help
+//config:        Enable bash-compatible extensions.
+//config:
+//config:config HUSH_BRACE_EXPANSION
+//config:      bool "Brace expansion"
+//config:      default y
+//config:      depends on HUSH_BASH_COMPAT
+//config:      help
+//config:        Enable {abc,def} extension.
+//config:
+//config:config HUSH_HELP
+//config:      bool "help builtin"
+//config:      default y
+//config:      depends on HUSH
+//config:      help
+//config:        Enable help builtin in hush. Code size + ~1 kbyte.
+//config:
+//config:config HUSH_INTERACTIVE
+//config:      bool "Interactive mode"
+//config:      default y
+//config:      depends on HUSH
+//config:      help
+//config:        Enable interactive mode (prompt and command editing).
+//config:        Without this, hush simply reads and executes commands
+//config:        from stdin just like a shell script from a file.
+//config:        No prompt, no PS1/PS2 magic shell variables.
+//config:
+//config:config HUSH_SAVEHISTORY
+//config:      bool "Save command history to .hush_history"
+//config:      default y
+//config:      depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY
+//config:      help
+//config:        Enable history saving in hush.
+//config:
+//config:config HUSH_JOB
+//config:      bool "Job control"
+//config:      default y
+//config:      depends on HUSH_INTERACTIVE
+//config:      help
+//config:        Enable job control: Ctrl-Z backgrounds, Ctrl-C interrupts current
+//config:        command (not entire shell), fg/bg builtins work. Without this option,
+//config:        "cmd &" still works by simply spawning a process and immediately
+//config:        prompting for next command (or executing next command in a script),
+//config:        but no separate process group is formed.
+//config:
+//config:config HUSH_TICK
+//config:      bool "Process substitution"
+//config:      default y
+//config:      depends on HUSH
+//config:      help
+//config:        Enable process substitution `command` and $(command) in hush.
+//config:
+//config:config HUSH_IF
+//config:      bool "Support if/then/elif/else/fi"
+//config:      default y
+//config:      depends on HUSH
+//config:      help
+//config:        Enable if/then/elif/else/fi in hush.
+//config:
+//config:config HUSH_LOOPS
+//config:      bool "Support for, while and until loops"
+//config:      default y
+//config:      depends on HUSH
+//config:      help
+//config:        Enable for, while and until loops in hush.
+//config:
+//config:config HUSH_CASE
+//config:      bool "Support case ... esac statement"
+//config:      default y
+//config:      depends on HUSH
+//config:      help
+//config:        Enable case ... esac statement in hush. +400 bytes.
+//config:
+//config:config HUSH_FUNCTIONS
+//config:      bool "Support funcname() { commands; } syntax"
+//config:      default y
+//config:      depends on HUSH
+//config:      help
+//config:        Enable support for shell functions in hush. +800 bytes.
+//config:
+//config:config HUSH_LOCAL
+//config:      bool "Support local builtin"
+//config:      default y
+//config:      depends on HUSH_FUNCTIONS
+//config:      help
+//config:        Enable support for local variables in functions.
+//config:
+//config:config HUSH_RANDOM_SUPPORT
+//config:      bool "Pseudorandom generator and $RANDOM variable"
+//config:      default y
+//config:      depends on HUSH
+//config:      help
+//config:        Enable pseudorandom generator and dynamic variable "$RANDOM".
+//config:        Each read of "$RANDOM" will generate a new pseudorandom value.
+//config:
+//config:config HUSH_EXPORT_N
+//config:      bool "Support 'export -n' option"
+//config:      default y
+//config:      depends on HUSH
+//config:      help
+//config:        export -n unexports variables. It is a bash extension.
+//config:
+//config:config HUSH_MODE_X
+//config:      bool "Support 'hush -x' option and 'set -x' command"
+//config:      default y
+//config:      depends on HUSH
+//config:      help
+//config:        This instructs hush to print commands before execution.
+//config:        Adds ~300 bytes.
+//config:
+//config:config MSH
+//config:      bool "msh (deprecated: aliased to hush)"
+//config:      default n
+//config:      select HUSH
+//config:      help
+//config:        msh is deprecated and will be removed, please migrate to hush.
+//config:
+
+//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP))
+//applet:IF_MSH(APPLET(msh, BB_DIR_BIN, BB_SUID_DROP))
+//applet:IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, sh))
+//applet:IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, bash))
+
+//kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o
+//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
+
+/* -i (interactive) and -s (read stdin) are also accepted,
+ * but currently do nothing, therefore aren't shown in help.
+ * NOMMU-specific options are not meant to be used by users,
+ * therefore we don't show them either.
+ */
+//usage:#define hush_trivial_usage
+//usage:       "[-nxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
+//usage:#define hush_full_usage "\n\n"
+//usage:       "Unix shell interpreter"
+
+//usage:#define msh_trivial_usage hush_trivial_usage
+//usage:#define msh_full_usage hush_full_usage
+
+//usage:#if ENABLE_FEATURE_SH_IS_HUSH
+//usage:# define sh_trivial_usage hush_trivial_usage
+//usage:# define sh_full_usage    hush_full_usage
+//usage:#endif
+//usage:#if ENABLE_FEATURE_BASH_IS_HUSH
+//usage:# define bash_trivial_usage hush_trivial_usage
+//usage:# define bash_full_usage    hush_full_usage
+//usage:#endif
+
 
 /* Build knobs */
 #define LEAK_HUNTING 0
 # define ENABLE_FEATURE_EDITING 0
 # undef ENABLE_FEATURE_EDITING_FANCY_PROMPT
 # define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0
+# undef ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
+# define ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 0
 #endif
 
 /* Do we support ANY keywords? */
 #define _SPECIAL_VARS_STR     "_*@$!?#"
 #define SPECIAL_VARS_STR     ("_*@$!?#" + 1)
 #define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3)
+#if ENABLE_HUSH_BASH_COMPAT
+/* Support / and // replace ops */
+/* Note that // is stored as \ in "encoded" string representation */
+# define VAR_ENCODED_SUBST_OPS      "\\/%#:-=+?"
+# define VAR_SUBST_OPS             ("\\/%#:-=+?" + 1)
+# define MINUS_PLUS_EQUAL_QUESTION ("\\/%#:-=+?" + 5)
+#else
+# define VAR_ENCODED_SUBST_OPS      "%#:-=+?"
+# define VAR_SUBST_OPS              "%#:-=+?"
+# define MINUS_PLUS_EQUAL_QUESTION ("%#:-=+?" + 3)
+#endif
 
 #define SPECIAL_VAR_SYMBOL   3
 
@@ -204,7 +388,7 @@ typedef struct nommu_save_t {
 } nommu_save_t;
 #endif
 
-typedef enum reserved_style {
+enum {
        RES_NONE  = 0,
 #if ENABLE_HUSH_IF
        RES_IF    ,
@@ -233,42 +417,54 @@ typedef enum reserved_style {
 #endif
        RES_XXXX  ,
        RES_SNTX
-} reserved_style;
+};
 
 typedef struct o_string {
        char *data;
        int length; /* position where data is appended */
        int maxlen;
-       /* Protect newly added chars against globbing
-        * (by prepending \ to *, ?, [, \) */
-       smallint o_escape;
-       smallint o_glob;
+       int o_expflags;
        /* At least some part of the string was inside '' or "",
         * possibly empty one: word"", wo''rd etc. */
-       smallint o_quoted;
+       smallint has_quoted_part;
        smallint has_empty_slot;
        smallint o_assignment; /* 0:maybe, 1:yes, 2:no */
 } o_string;
 enum {
-       MAYBE_ASSIGNMENT = 0,
+       EXP_FLAG_SINGLEWORD     = 0x80, /* must be 0x80 */
+       EXP_FLAG_GLOB           = 0x2,
+       /* Protect newly added chars against globbing
+        * by prepending \ to *, ?, [, \ */
+       EXP_FLAG_ESC_GLOB_CHARS = 0x1,
+};
+enum {
+       MAYBE_ASSIGNMENT      = 0,
        DEFINITELY_ASSIGNMENT = 1,
-       NOT_ASSIGNMENT = 2,
-       WORD_IS_KEYWORD = 3, /* not assigment, but next word may be: "if v=xyz cmd;" */
+       NOT_ASSIGNMENT        = 2,
+       /* Not an assigment, but next word may be: "if v=xyz cmd;" */
+       WORD_IS_KEYWORD       = 3,
 };
 /* Used for initialization: o_string foo = NULL_O_STRING; */
 #define NULL_O_STRING { NULL }
 
-/* I can almost use ordinary FILE*.  Is open_memstream() universally
- * available?  Where is it documented? */
+#ifndef debug_printf_parse
+static const char *const assignment_flag[] = {
+       "MAYBE_ASSIGNMENT",
+       "DEFINITELY_ASSIGNMENT",
+       "NOT_ASSIGNMENT",
+       "WORD_IS_KEYWORD",
+};
+#endif
+
 typedef struct in_str {
        const char *p;
        /* eof_flag=1: last char in ->p is really an EOF */
        char eof_flag; /* meaningless if ->p == NULL */
        char peek_buf[2];
 #if ENABLE_HUSH_INTERACTIVE
-       smallint promptme;
        smallint promptmode; /* 0: PS1, 1: PS2 */
 #endif
+       int last_char;
        FILE *file;
        int (*get) (struct in_str *) FAST_FUNC;
        int (*peek) (struct in_str *) FAST_FUNC;
@@ -326,28 +522,18 @@ typedef enum redir_type {
 struct command {
        pid_t pid;                  /* 0 if exited */
        int assignment_cnt;         /* how many argv[i] are assignments? */
-       smallint is_stopped;        /* is the command currently running? */
        smallint cmd_type;          /* CMD_xxx */
 #define CMD_NORMAL   0
 #define CMD_SUBSHELL 1
-
-/* used for "[[ EXPR ]]" */
 #if ENABLE_HUSH_BASH_COMPAT
+/* used for "[[ EXPR ]]" */
 # define CMD_SINGLEWORD_NOGLOB 2
 #endif
-
-/* used for "export noglob=* glob* a=`echo a b`" */
-//#define CMD_SINGLEWORD_NOGLOB_COND 3
-// It is hard to implement correctly, it adds significant amounts of tricky code,
-// and all this is only useful for really obscure export statements
-// almost nobody would use anyway. #ifdef CMD_SINGLEWORD_NOGLOB_COND
-// guards the code which implements it, but I have doubts it works
-// in all cases (especially with mixed globbed/non-globbed arguments)
-
 #if ENABLE_HUSH_FUNCTIONS
 # define CMD_FUNCDEF 3
 #endif
 
+       smalluint cmd_exitcode;
        /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */
        struct pipe *group;
 #if !BB_MMU
@@ -383,7 +569,6 @@ struct command {
 #define IS_NULL_CMD(cmd) \
        (!(cmd)->group && !(cmd)->argv && !(cmd)->redirects)
 
-
 struct pipe {
        struct pipe *next;
        int num_cmds;               /* total number of commands in pipe */
@@ -478,6 +663,53 @@ struct function {
 #endif
 
 
+/* set -/+o OPT support. (TODO: make it optional)
+ * bash supports the following opts:
+ * allexport       off
+ * braceexpand     on
+ * emacs           on
+ * errexit         off
+ * errtrace        off
+ * functrace       off
+ * hashall         on
+ * histexpand      off
+ * history         on
+ * ignoreeof       off
+ * interactive-comments    on
+ * keyword         off
+ * monitor         on
+ * noclobber       off
+ * noexec          off
+ * noglob          off
+ * nolog           off
+ * notify          off
+ * nounset         off
+ * onecmd          off
+ * physical        off
+ * pipefail        off
+ * posix           off
+ * privileged      off
+ * verbose         off
+ * vi              off
+ * xtrace          off
+ */
+static const char o_opt_strings[] ALIGN1 =
+       "pipefail\0"
+       "noexec\0"
+#if ENABLE_HUSH_MODE_X
+       "xtrace\0"
+#endif
+       ;
+enum {
+       OPT_O_PIPEFAIL,
+       OPT_O_NOEXEC,
+#if ENABLE_HUSH_MODE_X
+       OPT_O_XTRACE,
+#endif
+       NUM_OPT_O
+};
+
+
 /* "Globals" within this file */
 /* Sorted roughly by size (smaller offsets == smaller code) */
 struct globals {
@@ -520,6 +752,12 @@ struct globals {
 #else
 # define G_saved_tty_pgrp 0
 #endif
+       char o_opt[NUM_OPT_O];
+#if ENABLE_HUSH_MODE_X
+# define G_x_mode (G.o_opt[OPT_O_XTRACE])
+#else
+# define G_x_mode 0
+#endif
        smallint flag_SIGINT;
 #if ENABLE_HUSH_LOOPS
        smallint flag_break_continue;
@@ -531,13 +769,11 @@ struct globals {
         */
        smallint flag_return_in_progress;
 #endif
-       smallint fake_mode;
        smallint exiting; /* used to prevent EXIT trap recursion */
        /* These four support $?, $#, and $1 */
        smalluint last_exitcode;
        /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
        smalluint global_args_malloced;
-       smalluint inherited_set_is_saved;
        /* how many non-NULL argv's we have. NB: $# + 1 */
        int global_argc;
        char **global_argv;
@@ -550,8 +786,8 @@ struct globals {
 #endif
        const char *ifs;
        const char *cwd;
-       struct variable *top_var; /* = &G.shell_ver (set in main()) */
-       struct variable shell_ver;
+       struct variable *top_var;
+       char **expanded_assignments;
 #if ENABLE_HUSH_FUNCTIONS
        struct function *top_func;
 # if ENABLE_HUSH_LOCAL
@@ -565,15 +801,27 @@ struct globals {
        unsigned handled_SIGCHLD;
        smallint we_have_children;
 #endif
-       /* which signals have non-DFL handler (even with no traps set)? */
-       unsigned non_DFL_mask;
+       /* Which signals have non-DFL handler (even with no traps set)?
+        * Set at the start to:
+        * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS)
+        * SPECIAL_INTERACTIVE_SIGS are cleared after fork.
+        * The rest is cleared right before execv syscalls.
+        * Other than these two times, never modified.
+        */
+       unsigned special_sig_mask;
+#if ENABLE_HUSH_JOB
+       unsigned fatal_sig_mask;
+# define G_fatal_sig_mask G.fatal_sig_mask
+#else
+# define G_fatal_sig_mask 0
+#endif
        char **traps; /* char *traps[NSIG] */
-       sigset_t blocked_set;
-       sigset_t inherited_set;
+       sigset_t pending_set;
 #if HUSH_DEBUG
        unsigned long memleak_value;
        int debug_indent;
 #endif
+       struct sigaction sa;
        char user_input_buf[ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 2];
 };
 #define G (*ptr_to_globals)
@@ -582,6 +830,9 @@ struct globals {
  * is global, thus "G." prefix is a useful hint */
 #define INIT_G() do { \
        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+       /* memset(&G.sa, 0, sizeof(G.sa)); */  \
+       sigfillset(&G.sa.sa_mask); \
+       G.sa.sa_flags = SA_RESTART; \
 } while (0)
 
 
@@ -599,6 +850,9 @@ static int builtin_jobs(char **argv) FAST_FUNC;
 #if ENABLE_HUSH_HELP
 static int builtin_help(char **argv) FAST_FUNC;
 #endif
+#if MAX_HISTORY && ENABLE_FEATURE_EDITING
+static int builtin_history(char **argv) FAST_FUNC;
+#endif
 #if ENABLE_HUSH_LOCAL
 static int builtin_local(char **argv) FAST_FUNC;
 #endif
@@ -668,6 +922,9 @@ static const struct built_in_command bltins1[] = {
 #if ENABLE_HUSH_HELP
        BLTIN("help"     , builtin_help    , NULL),
 #endif
+#if MAX_HISTORY && ENABLE_FEATURE_EDITING
+       BLTIN("history"  , builtin_history , "Show command history"),
+#endif
 #if ENABLE_HUSH_JOB
        BLTIN("jobs"     , builtin_jobs    , "List jobs"),
 #endif
@@ -710,7 +967,7 @@ static const struct built_in_command bltins2[] = {
  */
 #if HUSH_DEBUG
 /* prevent disasters with G.debug_indent < 0 */
-# define indent() fprintf(stderr, "%*s", (G.debug_indent * 2) & 0xff, "")
+# define indent() fdprintf(2, "%*s", (G.debug_indent * 2) & 0xff, "")
 # define debug_enter() (G.debug_indent++)
 # define debug_leave() (G.debug_indent--)
 #else
@@ -720,56 +977,56 @@ static const struct built_in_command bltins2[] = {
 #endif
 
 #ifndef debug_printf
-# define debug_printf(...) (indent(), fprintf(stderr, __VA_ARGS__))
+# define debug_printf(...) (indent(), fdprintf(2, __VA_ARGS__))
 #endif
 
 #ifndef debug_printf_parse
-# define debug_printf_parse(...) (indent(), fprintf(stderr, __VA_ARGS__))
+# define debug_printf_parse(...) (indent(), fdprintf(2, __VA_ARGS__))
 #endif
 
 #ifndef debug_printf_exec
-#define debug_printf_exec(...) (indent(), fprintf(stderr, __VA_ARGS__))
+#define debug_printf_exec(...) (indent(), fdprintf(2, __VA_ARGS__))
 #endif
 
 #ifndef debug_printf_env
-# define debug_printf_env(...) (indent(), fprintf(stderr, __VA_ARGS__))
+# define debug_printf_env(...) (indent(), fdprintf(2, __VA_ARGS__))
 #endif
 
 #ifndef debug_printf_jobs
-# define debug_printf_jobs(...) (indent(), fprintf(stderr, __VA_ARGS__))
+# define debug_printf_jobs(...) (indent(), fdprintf(2, __VA_ARGS__))
 # define DEBUG_JOBS 1
 #else
 # define DEBUG_JOBS 0
 #endif
 
 #ifndef debug_printf_expand
-# define debug_printf_expand(...) (indent(), fprintf(stderr, __VA_ARGS__))
+# define debug_printf_expand(...) (indent(), fdprintf(2, __VA_ARGS__))
 # define DEBUG_EXPAND 1
 #else
 # define DEBUG_EXPAND 0
 #endif
 
 #ifndef debug_printf_varexp
-# define debug_printf_varexp(...) (indent(), fprintf(stderr, __VA_ARGS__))
+# define debug_printf_varexp(...) (indent(), fdprintf(2, __VA_ARGS__))
 #endif
 
 #ifndef debug_printf_glob
-# define debug_printf_glob(...) (indent(), fprintf(stderr, __VA_ARGS__))
+# define debug_printf_glob(...) (indent(), fdprintf(2, __VA_ARGS__))
 # define DEBUG_GLOB 1
 #else
 # define DEBUG_GLOB 0
 #endif
 
 #ifndef debug_printf_list
-# define debug_printf_list(...) (indent(), fprintf(stderr, __VA_ARGS__))
+# define debug_printf_list(...) (indent(), fdprintf(2, __VA_ARGS__))
 #endif
 
 #ifndef debug_printf_subst
-# define debug_printf_subst(...) (indent(), fprintf(stderr, __VA_ARGS__))
+# define debug_printf_subst(...) (indent(), fdprintf(2, __VA_ARGS__))
 #endif
 
 #ifndef debug_printf_clean
-# define debug_printf_clean(...) (indent(), fprintf(stderr, __VA_ARGS__))
+# define debug_printf_clean(...) (indent(), fdprintf(2, __VA_ARGS__))
 # define DEBUG_CLEAN 1
 #else
 # define DEBUG_CLEAN 0
@@ -779,9 +1036,9 @@ static const struct built_in_command bltins2[] = {
 static void debug_print_strings(const char *prefix, char **vv)
 {
        indent();
-       fprintf(stderr, "%s:\n", prefix);
+       fdprintf(2, "%s:\n", prefix);
        while (*vv)
-               fprintf(stderr, " '%s'\n", *vv++);
+               fdprintf(2, " '%s'\n", *vv++);
 }
 #else
 # define debug_print_strings(prefix, vv) ((void)0)
@@ -849,43 +1106,36 @@ static void die_if_script(unsigned lineno, const char *fmt, ...)
                xfunc_die();
 }
 
-static void syntax_error(unsigned lineno, const char *msg)
+static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg)
 {
        if (msg)
-               die_if_script(lineno, "syntax error: %s", msg);
+               bb_error_msg("syntax error: %s", msg);
        else
-               die_if_script(lineno, "syntax error", NULL);
+               bb_error_msg("syntax error");
 }
 
-static void syntax_error_at(unsigned lineno, const char *msg)
+static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg)
 {
-       die_if_script(lineno, "syntax error at '%s'", msg);
+       bb_error_msg("syntax error at '%s'", msg);
 }
 
-static void syntax_error_unterm_str(unsigned lineno, const char *s)
+static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s)
 {
-       die_if_script(lineno, "syntax error: unterminated %s", s);
+       bb_error_msg("syntax error: unterminated %s", s);
 }
 
-/* It so happens that all such cases are totally fatal
- * even if shell is interactive: EOF while looking for closing
- * delimiter. There is nowhere to read stuff from after that,
- * it's EOF! The only choice is to terminate.
- */
-static void syntax_error_unterm_ch(unsigned lineno, char ch) NORETURN;
 static void syntax_error_unterm_ch(unsigned lineno, char ch)
 {
        char msg[2] = { ch, '\0' };
        syntax_error_unterm_str(lineno, msg);
-       xfunc_die();
 }
 
-static void syntax_error_unexpected_ch(unsigned lineno, int ch)
+static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
 {
        char msg[2];
        msg[0] = ch;
        msg[1] = '\0';
-       die_if_script(lineno, "syntax error: unexpected %s", ch == EOF ? "EOF" : msg);
+       bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg);
 }
 
 #if HUSH_DEBUG < 2
@@ -1060,7 +1310,7 @@ static void restore_G_args(save_arg_t *sv, char **argv)
  * backgrounds (i.e. stops) or kills all members of currently running
  * pipe.
  *
- * Wait builtin in interruptible by signals for which user trap is set
+ * Wait builtin is interruptible by signals for which user trap is set
  * or by SIGINT in interactive shell.
  *
  * Trap handlers will execute even within trap handlers. (right?)
@@ -1099,12 +1349,14 @@ static void restore_G_args(save_arg_t *sv, char **argv)
  *    "echo $$; sleep 5 & wait; ls -l" + "kill -INT <pid>"
  *    Example 3: this does not wait 5 sec, but executes ls:
  *    "sleep 5; ls -l" + press ^C
+ *    Example 4: this does not wait and does not execute ls:
+ *    "sleep 5 & wait; ls -l" + press ^C
  *
  * (What happens to signals which are IGN on shell start?)
  * (What happens with signal mask on shell start?)
  *
- * Implementation in hush
- * ======================
+ * Old implementation
+ * ==================
  * We use in-kernel pending signal mask to determine which signals were sent.
  * We block all signals which we don't want to take action immediately,
  * i.e. we block all signals which need to have special handling as described
@@ -1112,11 +1364,11 @@ static void restore_G_args(save_arg_t *sv, char **argv)
  * After each pipe execution, we extract any pending signals via sigtimedwait()
  * and act on them.
  *
- * unsigned non_DFL_mask: a mask of such "special" signals
+ * unsigned special_sig_mask: a mask of such "special" signals
  * sigset_t blocked_set:  current blocked signal set
  *
  * "trap - SIGxxx":
- *    clear bit in blocked_set unless it is also in non_DFL_mask
+ *    clear bit in blocked_set unless it is also in special_sig_mask
  * "trap 'cmd' SIGxxx":
  *    set bit in blocked_set (even if 'cmd' is '')
  * after [v]fork, if we plan to be a shell:
@@ -1135,6 +1387,49 @@ static void restore_G_args(save_arg_t *sv, char **argv)
  * Standard says "When a subshell is entered, traps that are not being ignored
  * are set to the default actions". bash interprets it so that traps which
  * are set to '' (ignore) are NOT reset to defaults. We do the same.
+ *
+ * Problem: the above approach makes it unwieldy to catch signals while
+ * we are in read builtin, or while we read commands from stdin:
+ * masked signals are not visible!
+ *
+ * New implementation
+ * ==================
+ * We record each signal we are interested in by installing signal handler
+ * for them - a bit like emulating kernel pending signal mask in userspace.
+ * We are interested in: signals which need to have special handling
+ * as described above, and all signals which have traps set.
+ * Signals are recorded in pending_set.
+ * After each pipe execution, we extract any pending signals
+ * and act on them.
+ *
+ * unsigned special_sig_mask: a mask of shell-special signals.
+ * unsigned fatal_sig_mask: a mask of signals on which we restore tty pgrp.
+ * char *traps[sig] if trap for sig is set (even if it's '').
+ * sigset_t pending_set: set of sigs we received.
+ *
+ * "trap - SIGxxx":
+ *    if sig is in special_sig_mask, set handler back to:
+ *        record_pending_signo, or to IGN if it's a tty stop signal
+ *    if sig is in fatal_sig_mask, set handler back to sigexit.
+ *    else: set handler back to SIG_DFL
+ * "trap 'cmd' SIGxxx":
+ *    set handler to record_pending_signo.
+ * "trap '' SIGxxx":
+ *    set handler to SIG_IGN.
+ * after [v]fork, if we plan to be a shell:
+ *    set signals with special interactive handling to SIG_DFL
+ *    (because child shell is not interactive),
+ *    unset all traps except '' (note: regardless of child shell's type - {}, (), etc)
+ * after [v]fork, if we plan to exec:
+ *    POSIX says fork clears pending signal mask in child - no need to clear it.
+ *
+ * To make wait builtin interruptible, we handle SIGCHLD as special signal,
+ * otherwise (if we leave it SIG_DFL) sigsuspend in wait builtin will not wake up on it.
+ *
+ * Note (compat):
+ * Standard says "When a subshell is entered, traps that are not being ignored
+ * are set to the default actions". bash interprets it so that traps which
+ * are set to '' (ignore) are NOT reset to defaults. We do the same.
  */
 enum {
        SPECIAL_INTERACTIVE_SIGS = 0
@@ -1142,21 +1437,43 @@ enum {
                | (1 << SIGINT)
                | (1 << SIGHUP)
                ,
-       SPECIAL_JOB_SIGS = 0
+       SPECIAL_JOBSTOP_SIGS = 0
 #if ENABLE_HUSH_JOB
                | (1 << SIGTTIN)
                | (1 << SIGTTOU)
                | (1 << SIGTSTP)
 #endif
+               ,
 };
 
-#if ENABLE_HUSH_FAST
-static void SIGCHLD_handler(int sig UNUSED_PARAM)
+static void record_pending_signo(int sig)
 {
-       G.count_SIGCHLD++;
+       sigaddset(&G.pending_set, sig);
+#if ENABLE_HUSH_FAST
+       if (sig == SIGCHLD) {
+               G.count_SIGCHLD++;
 //bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
-}
+       }
 #endif
+}
+
+static sighandler_t install_sighandler(int sig, sighandler_t handler)
+{
+       struct sigaction old_sa;
+
+       /* We could use signal() to install handlers... almost:
+        * except that we need to mask ALL signals while handlers run.
+        * I saw signal nesting in strace, race window isn't small.
+        * SA_RESTART is also needed, but in Linux, signal()
+        * sets SA_RESTART too.
+        */
+       /* memset(&G.sa, 0, sizeof(G.sa)); - already done */
+       /* sigfillset(&G.sa.sa_mask);      - already done */
+       /* G.sa.sa_flags = SA_RESTART;     - already done */
+       G.sa.sa_handler = handler;
+       sigaction(sig, &G.sa, &old_sa);
+       return old_sa.sa_handler;
+}
 
 #if ENABLE_HUSH_JOB
 
@@ -1173,13 +1490,15 @@ static void SIGCHLD_handler(int sig UNUSED_PARAM)
 static void sigexit(int sig) NORETURN;
 static void sigexit(int sig)
 {
-       /* Disable all signals: job control, SIGPIPE, etc. */
-       sigprocmask_allsigs(SIG_BLOCK);
-
        /* Careful: we can end up here after [v]fork. Do not restore
         * tty pgrp then, only top-level shell process does that */
-       if (G_saved_tty_pgrp && getpid() == G.root_pid)
+       if (G_saved_tty_pgrp && getpid() == G.root_pid) {
+               /* Disable all signals: job control, SIGPIPE, etc.
+                * Mostly paranoid measure, to prevent infinite SIGTTOU.
+                */
+               sigprocmask_allsigs(SIG_BLOCK);
                tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
+       }
 
        /* Not a signal, just exit */
        if (sig <= 0)
@@ -1194,21 +1513,70 @@ static void sigexit(int sig)
 
 #endif
 
+static sighandler_t pick_sighandler(unsigned sig)
+{
+       sighandler_t handler = SIG_DFL;
+       if (sig < sizeof(unsigned)*8) {
+               unsigned sigmask = (1 << sig);
+
+#if ENABLE_HUSH_JOB
+               /* is sig fatal? */
+               if (G_fatal_sig_mask & sigmask)
+                       handler = sigexit;
+               else
+#endif
+               /* sig has special handling? */
+               if (G.special_sig_mask & sigmask) {
+                       handler = record_pending_signo;
+                       /* TTIN/TTOU/TSTP can't be set to record_pending_signo
+                        * in order to ignore them: they will be raised
+                        * in an endless loop when we try to do some
+                        * terminal ioctls! We do have to _ignore_ these.
+                        */
+                       if (SPECIAL_JOBSTOP_SIGS & sigmask)
+                               handler = SIG_IGN;
+               }
+       }
+       return handler;
+}
+
 /* Restores tty foreground process group, and exits. */
 static void hush_exit(int exitcode) NORETURN;
 static void hush_exit(int exitcode)
 {
+#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
+       save_history(G.line_input_state);
+#endif
+
+       fflush_all();
        if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) {
-               /* Prevent recursion:
-                * trap "echo Hi; exit" EXIT; exit
-                */
-               char *argv[] = { NULL, G.traps[0], NULL };
-               G.traps[0] = NULL;
-               G.exiting = 1;
+               char *argv[3];
+               /* argv[0] is unused */
+               argv[1] = G.traps[0];
+               argv[2] = NULL;
+               G.exiting = 1; /* prevent EXIT trap recursion */
+               /* Note: G.traps[0] is not cleared!
+                * "trap" will still show it, if executed
+                * in the handler */
                builtin_eval(argv);
-               free(argv[1]);
        }
 
+#if ENABLE_FEATURE_CLEAN_UP
+       {
+               struct variable *cur_var;
+               if (G.cwd != bb_msg_unknown)
+                       free((char*)G.cwd);
+               cur_var = G.top_var;
+               while (cur_var) {
+                       struct variable *tmp = cur_var;
+                       if (!cur_var->max_len)
+                               free(cur_var->varstr);
+                       cur_var = cur_var->next;
+                       free(tmp);
+               }
+       }
+#endif
+
 #if ENABLE_HUSH_JOB
        fflush_all();
        sigexit(- (exitcode & 0xff));
@@ -1217,43 +1585,49 @@ static void hush_exit(int exitcode)
 #endif
 }
 
-static int check_and_run_traps(int sig)
+
+//TODO: return a mask of ALL handled sigs?
+static int check_and_run_traps(void)
 {
-       static const struct timespec zero_timespec;
-       smalluint save_rcode;
        int last_sig = 0;
 
-       if (sig)
-               goto jump_in;
        while (1) {
-               sig = sigtimedwait(&G.blocked_set, NULL, &zero_timespec);
-               if (sig <= 0)
+               int sig;
+
+               if (sigisemptyset(&G.pending_set))
                        break;
- jump_in:
-               last_sig = sig;
+               sig = 0;
+               do {
+                       sig++;
+                       if (sigismember(&G.pending_set, sig)) {
+                               sigdelset(&G.pending_set, sig);
+                               goto got_sig;
+                       }
+               } while (sig < NSIG);
+               break;
+ got_sig:
                if (G.traps && G.traps[sig]) {
                        if (G.traps[sig][0]) {
                                /* We have user-defined handler */
-                               char *argv[] = { NULL, xstrdup(G.traps[sig]), NULL };
+                               smalluint save_rcode;
+                               char *argv[3];
+                               /* argv[0] is unused */
+                               argv[1] = G.traps[sig];
+                               argv[2] = NULL;
                                save_rcode = G.last_exitcode;
                                builtin_eval(argv);
-                               free(argv[1]);
                                G.last_exitcode = save_rcode;
+                               last_sig = sig;
                        } /* else: "" trap, ignoring signal */
                        continue;
                }
                /* not a trap: special action */
                switch (sig) {
-#if ENABLE_HUSH_FAST
-               case SIGCHLD:
-                       G.count_SIGCHLD++;
-//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
-                       break;
-#endif
                case SIGINT:
                        /* Builtin was ^C'ed, make it look prettier: */
                        bb_putchar('\n');
                        G.flag_SIGINT = 1;
+                       last_sig = sig;
                        break;
 #if ENABLE_HUSH_JOB
                case SIGHUP: {
@@ -1270,8 +1644,23 @@ static int check_and_run_traps(int sig)
                        sigexit(SIGHUP);
                }
 #endif
+#if ENABLE_HUSH_FAST
+               case SIGCHLD:
+                       G.count_SIGCHLD++;
+//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
+                       /* Note:
+                        * We dont do 'last_sig = sig' here -> NOT returning this sig.
+                        * This simplifies wait builtin a bit.
+                        */
+                       break;
+#endif
                default: /* ignored: */
                        /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */
+                       /* Note:
+                        * We dont do 'last_sig = sig' here -> NOT returning this sig.
+                        * Example: wait is not interrupted by TERM
+                        * in interactive shell, because TERM is ignored.
+                        */
                        break;
                }
        }
@@ -1297,13 +1686,11 @@ static const char *get_cwd(int force)
 /*
  * Shell and environment variable support
  */
-static struct variable **get_ptr_to_local_var(const char *name)
+static struct variable **get_ptr_to_local_var(const char *name, unsigned len)
 {
        struct variable **pp;
        struct variable *cur;
-       int len;
 
-       len = strlen(name);
        pp = &G.top_var;
        while ((cur = *pp) != NULL) {
                if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=')
@@ -1313,26 +1700,31 @@ static struct variable **get_ptr_to_local_var(const char *name)
        return NULL;
 }
 
-static struct variable *get_local_var(const char *name)
-{
-       struct variable **pp = get_ptr_to_local_var(name);
-       if (pp)
-               return *pp;
-       return NULL;
-}
-
 static const char* FAST_FUNC get_local_var_value(const char *name)
 {
-       struct variable **pp = get_ptr_to_local_var(name);
-       if (pp)
-               return strchr((*pp)->varstr, '=') + 1;
+       struct variable **vpp;
+       unsigned len = strlen(name);
+
+       if (G.expanded_assignments) {
+               char **cpp = G.expanded_assignments;
+               while (*cpp) {
+                       char *cp = *cpp;
+                       if (strncmp(cp, name, len) == 0 && cp[len] == '=')
+                               return cp + len + 1;
+                       cpp++;
+               }
+       }
+
+       vpp = get_ptr_to_local_var(name, len);
+       if (vpp)
+               return (*vpp)->varstr + len + 1;
+
        if (strcmp(name, "PPID") == 0)
                return utoa(G.root_ppid);
        // bash compat: UID? EUID?
 #if ENABLE_HUSH_RANDOM_SUPPORT
-       if (strcmp(name, "RANDOM") == 0) {
+       if (strcmp(name, "RANDOM") == 0)
                return utoa(next_random(&G.random_gen));
-       }
 #endif
        return NULL;
 }
@@ -1521,24 +1913,6 @@ static void unset_vars(char **strings)
        free(strings);
 }
 
-#if ENABLE_SH_MATH_SUPPORT
-# define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
-# define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
-static char* FAST_FUNC endofname(const char *name)
-{
-       char *p;
-
-       p = (char *) name;
-       if (!is_name(*p))
-               return p;
-       while (*++p) {
-               if (!is_in_name(*p))
-                       break;
-       }
-       return p;
-}
-#endif
-
 static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
 {
        char *var = xasprintf("%s=%s", name, val);
@@ -1582,9 +1956,7 @@ static struct variable *set_vars_and_save_old(char **strings)
 
                eq = strchr(*s, '=');
                if (eq) {
-                       *eq = '\0';
-                       var_pp = get_ptr_to_local_var(*s);
-                       *eq = '=';
+                       var_pp = get_ptr_to_local_var(*s, eq - *s);
                        if (var_pp) {
                                /* Remove variable from global linked list */
                                var_p = *var_pp;
@@ -1610,6 +1982,7 @@ static int FAST_FUNC static_get(struct in_str *i)
        int ch = *i->p;
        if (ch != '\0') {
                i->p++;
+               i->last_char = ch;
                return ch;
        }
        return EOF;
@@ -1636,7 +2009,7 @@ static void cmdedit_update_prompt(void)
                G.PS2 = "> ";
 }
 
-static const charsetup_prompt_string(int promptmode)
+static const char *setup_prompt_string(int promptmode)
 {
        const char *prompt_str;
        debug_printf("setup_prompt_string %d ", promptmode);
@@ -1667,12 +2040,21 @@ static void get_user_input(struct in_str *i)
        /* Enable command line editing only while a command line
         * is actually being read */
        do {
+               /* Unicode support should be activated even if LANG is set
+                * _during_ shell execution, not only if it was set when
+                * shell was started. Therefore, re-check LANG every time:
+                */
+               const char *s = get_local_var_value("LC_ALL");
+               if (!s) s = get_local_var_value("LC_CTYPE");
+               if (!s) s = get_local_var_value("LANG");
+               reinit_unicode(s);
+
                G.flag_SIGINT = 0;
                /* buglet: SIGINT will not make new prompt to appear _at once_,
                 * only after <Enter>. (^C will work) */
-               r = read_line_input(prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, G.line_input_state);
+               r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1);
                /* catch *SIGINT* etc (^C is handled by read_line_input) */
-               check_and_run_traps(0);
+               check_and_run_traps();
        } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */
        i->eof_flag = (r < 0);
        if (i->eof_flag) { /* EOF/error detected */
@@ -1682,11 +2064,18 @@ static void get_user_input(struct in_str *i)
 # else
        do {
                G.flag_SIGINT = 0;
-               fputs(prompt_str, stdout);
+               if (i->last_char == '\0' || i->last_char == '\n') {
+                       /* Why check_and_run_traps here? Try this interactively:
+                        * $ trap 'echo INT' INT; (sleep 2; kill -INT $$) &
+                        * $ <[enter], repeatedly...>
+                        * Without check_and_run_traps, handler never runs.
+                        */
+                       check_and_run_traps();
+                       fputs(prompt_str, stdout);
+               }
                fflush_all();
                G.user_input_buf[0] = r = fgetc(i->file);
                /*G.user_input_buf[1] = '\0'; - already is and never changed */
-//do we need check_and_run_traps(0)? (maybe only if stdin)
        } while (G.flag_SIGINT);
        i->eof_flag = (r == EOF);
 # endif
@@ -1714,22 +2103,18 @@ static int FAST_FUNC file_get(struct in_str *i)
                /* need to double check i->file because we might be doing something
                 * more complicated by now, like sourcing or substituting. */
 #if ENABLE_HUSH_INTERACTIVE
-               if (G_interactive_fd && i->promptme && i->file == stdin) {
+               if (G_interactive_fd && i->file == stdin) {
                        do {
                                get_user_input(i);
                        } while (!*i->p); /* need non-empty line */
                        i->promptmode = 1; /* PS2 */
-                       i->promptme = 0;
                        goto take_cached;
                }
 #endif
                do ch = fgetc(i->file); while (ch == '\0');
        }
        debug_printf("file_get: got '%c' %d\n", ch, ch);
-#if ENABLE_HUSH_INTERACTIVE
-       if (ch == '\n')
-               i->promptme = 1;
-#endif
+       i->last_char = ch;
        return ch;
 }
 
@@ -1756,26 +2141,22 @@ static int FAST_FUNC file_peek(struct in_str *i)
 
 static void setup_file_in_str(struct in_str *i, FILE *f)
 {
+       memset(i, 0, sizeof(*i));
        i->peek = file_peek;
        i->get = file_get;
-#if ENABLE_HUSH_INTERACTIVE
-       i->promptme = 1;
-       i->promptmode = 0; /* PS1 */
-#endif
+       /* i->promptmode = 0; - PS1 (memset did it) */
        i->file = f;
-       i->p = NULL;
+       /* i->p = NULL; */
 }
 
 static void setup_string_in_str(struct in_str *i, const char *s)
 {
+       memset(i, 0, sizeof(*i));
        i->peek = static_peek;
        i->get = static_get;
-#if ENABLE_HUSH_INTERACTIVE
-       i->promptme = 1;
-       i->promptmode = 0; /* PS1 */
-#endif
+       /* i->promptmode = 0; - PS1 (memset did it) */
        i->p = s;
-       i->eof_flag = 0;
+       /* i->eof_flag = 0; */
 }
 
 
@@ -1787,7 +2168,7 @@ static void setup_string_in_str(struct in_str *i, const char *s)
 static void o_reset_to_empty_unquoted(o_string *o)
 {
        o->length = 0;
-       o->o_quoted = 0;
+       o->has_quoted_part = 0;
        if (o->data)
                o->data[0] = '\0';
 }
@@ -1848,22 +2229,8 @@ static void o_addstr_with_NUL(o_string *o, const char *str)
        o_addblock(o, str, strlen(str) + 1);
 }
 
-static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len)
-{
-       while (len) {
-               o_addchr(o, *str);
-               if (*str++ == '\\'
-                && (*str != '*' && *str != '?' && *str != '[')
-               ) {
-                       o_addchr(o, '\\');
-               }
-               len--;
-       }
-}
-
-#undef HUSH_BRACE_EXP
 /*
- * HUSH_BRACE_EXP code needs corresponding quoting on variable expansion side.
+ * HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side.
  * Currently, "v='{q,w}'; echo $v" erroneously expands braces in $v.
  * Apparently, on unquoted $v bash still does globbing
  * ("v='*.txt'; echo $v" prints all .txt files),
@@ -1873,7 +2240,7 @@ static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len
  * We have only second one.
  */
 
-#ifdef HUSH_BRACE_EXP
+#if ENABLE_HUSH_BRACE_EXPANSION
 # define MAYBE_BRACES "{}"
 #else
 # define MAYBE_BRACES ""
@@ -1901,7 +2268,9 @@ static void o_addqchr(o_string *o, int ch)
 static void o_addQchr(o_string *o, int ch)
 {
        int sz = 1;
-       if (o->o_escape && strchr("*?[\\" MAYBE_BRACES, ch)) {
+       if ((o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)
+        && strchr("*?[\\" MAYBE_BRACES, ch)
+       ) {
                sz++;
                o->data[o->length] = '\\';
                o->length++;
@@ -1912,12 +2281,8 @@ static void o_addQchr(o_string *o, int ch)
        o->data[o->length] = '\0';
 }
 
-static void o_addQstr(o_string *o, const char *str, int len)
+static void o_addqblock(o_string *o, const char *str, int len)
 {
-       if (!o->o_escape) {
-               o_addblock(o, str, len);
-               return;
-       }
        while (len) {
                char ch;
                int sz;
@@ -1926,7 +2291,7 @@ static void o_addQstr(o_string *o, const char *str, int len)
                        ordinary_cnt = len;
                o_addblock(o, str, ordinary_cnt);
                if (ordinary_cnt == len)
-                       return;
+                       return; /* NUL is already added by o_addblock */
                str += ordinary_cnt;
                len -= ordinary_cnt + 1; /* we are processing + 1 char below */
 
@@ -1940,8 +2305,22 @@ static void o_addQstr(o_string *o, const char *str, int len)
                o_grow_by(o, sz);
                o->data[o->length] = ch;
                o->length++;
-               o->data[o->length] = '\0';
        }
+       o->data[o->length] = '\0';
+}
+
+static void o_addQblock(o_string *o, const char *str, int len)
+{
+       if (!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)) {
+               o_addblock(o, str, len);
+               return;
+       }
+       o_addqblock(o, str, len);
+}
+
+static void o_addQstr(o_string *o, const char *str)
+{
+       o_addQblock(o, str, strlen(str));
 }
 
 /* A special kind of o_string for $VAR and `cmd` expansion.
@@ -1962,19 +2341,22 @@ static void debug_print_list(const char *prefix, o_string *o, int n)
        int i = 0;
 
        indent();
-       fprintf(stderr, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d\n",
-                       prefix, list, n, string_start, o->length, o->maxlen);
+       fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n",
+                       prefix, list, n, string_start, o->length, o->maxlen,
+                       !!(o->o_expflags & EXP_FLAG_GLOB),
+                       o->has_quoted_part,
+                       !!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
        while (i < n) {
                indent();
-               fprintf(stderr, " list[%d]=%d '%s' %p\n", i, (int)list[i],
-                               o->data + (int)list[i] + string_start,
-                               o->data + (int)list[i] + string_start);
+               fdprintf(2, " list[%d]=%d '%s' %p\n", i, (int)(uintptr_t)list[i],
+                               o->data + (int)(uintptr_t)list[i] + string_start,
+                               o->data + (int)(uintptr_t)list[i] + string_start);
                i++;
        }
        if (n) {
-               const char *p = o->data + (int)list[n - 1] + string_start;
+               const char *p = o->data + (int)(uintptr_t)list[n - 1] + string_start;
                indent();
-               fprintf(stderr, " total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data));
+               fdprintf(2, " total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data));
        }
 }
 #else
@@ -2013,7 +2395,8 @@ static int o_save_ptr_helper(o_string *o, int n)
                                n, string_len, string_start);
                o->has_empty_slot = 0;
        }
-       list[n] = (char*)(ptrdiff_t)string_len;
+       o->has_quoted_part = 0;
+       list[n] = (char*)(uintptr_t)string_len;
        return n + 1;
 }
 
@@ -2023,10 +2406,10 @@ static int o_get_last_ptr(o_string *o, int n)
        char **list = (char**)o->data;
        int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
 
-       return ((int)(ptrdiff_t)list[n-1]) + string_start;
+       return ((int)(uintptr_t)list[n-1]) + string_start;
 }
 
-#ifdef HUSH_BRACE_EXP
+#if ENABLE_HUSH_BRACE_EXPANSION
 /* There in a GNU extension, GLOB_BRACE, but it is not usable:
  * first, it processes even {a} (no commas), second,
  * I didn't manage to make it return strings when they don't match
@@ -2061,9 +2444,9 @@ static const char *next_brace_sub(const char *cp)
                        cp++;
                        continue;
                }
-                /*{*/ if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0))
+               if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0))
                        break;
-               if (*cp++ == '{') /*}*/
+               if (*cp++ == '{')
                        depth++;
        }
 
@@ -2085,7 +2468,7 @@ static int glob_brace(char *pattern, o_string *o, int n)
        while (1) {
                if (*begin == '\0')
                        goto simple_glob;
-               if (*begin == '{') /*}*/ {
+               if (*begin == '{') {
                        /* Find the first sub-pattern and at the same time
                         * find the rest after the closing brace */
                        next = next_brace_sub(begin);
@@ -2093,7 +2476,7 @@ static int glob_brace(char *pattern, o_string *o, int n)
                                /* An illegal expression */
                                goto simple_glob;
                        }
-                       /*{*/ if (*next == '}') {
+                       if (*next == '}') {
                                /* "{abc}" with no commas - illegal
                                 * brace expr, disregard and skip it */
                                begin = next + 1;
@@ -2110,7 +2493,7 @@ static int glob_brace(char *pattern, o_string *o, int n)
 
        /* Now find the end of the whole brace expression */
        rest = next;
-       /*{*/ while (*rest != '}') {
+       while (*rest != '}') {
                rest = next_brace_sub(rest);
                if (rest == NULL) {
                        /* An illegal expression */
@@ -2146,7 +2529,7 @@ static int glob_brace(char *pattern, o_string *o, int n)
                 * That's why we re-copy prefix every time (1st memcpy above).
                 */
                n = glob_brace(new_pattern_buf, o, n);
-               /*{*/ if (*next == '}') {
+               if (*next == '}') {
                        /* We saw the last entry */
                        break;
                }
@@ -2196,11 +2579,11 @@ static int glob_brace(char *pattern, o_string *o, int n)
 /* Performs globbing on last list[],
  * saving each result as a new list[].
  */
-static int o_glob(o_string *o, int n)
+static int perform_glob(o_string *o, int n)
 {
        char *pattern, *copy;
 
-       debug_printf_glob("start o_glob: n:%d o->data:%p\n", n, o->data);
+       debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
        if (!o->data)
                return o_save_ptr_helper(o, n);
        pattern = o->data + o_get_last_ptr(o, n);
@@ -2218,11 +2601,11 @@ static int o_glob(o_string *o, int n)
        n = glob_brace(copy, o, n);
        free(copy);
        if (DEBUG_GLOB)
-               debug_print_list("o_glob returning", o, n);
+               debug_print_list("perform_glob returning", o, n);
        return n;
 }
 
-#else /* !HUSH_BRACE_EXP */
+#else /* !HUSH_BRACE_EXPANSION */
 
 /* Helper */
 static int glob_needed(const char *s)
@@ -2243,13 +2626,13 @@ static int glob_needed(const char *s)
 /* Performs globbing on last list[],
  * saving each result as a new list[].
  */
-static int o_glob(o_string *o, int n)
+static int perform_glob(o_string *o, int n)
 {
        glob_t globdata;
        int gr;
        char *pattern;
 
-       debug_printf_glob("start o_glob: n:%d o->data:%p\n", n, o->data);
+       debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
        if (!o->data)
                return o_save_ptr_helper(o, n);
        pattern = o->data + o_get_last_ptr(o, n);
@@ -2295,22 +2678,22 @@ static int o_glob(o_string *o, int n)
        }
        globfree(&globdata);
        if (DEBUG_GLOB)
-               debug_print_list("o_glob returning", o, n);
+               debug_print_list("perform_glob returning", o, n);
        return n;
 }
 
-#endif /* !HUSH_BRACE_EXP */
+#endif /* !HUSH_BRACE_EXPANSION */
 
-/* If o->o_glob == 1, glob the string so far remembered.
+/* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
  * Otherwise, just finish current list[] and start new */
 static int o_save_ptr(o_string *o, int n)
 {
-       if (o->o_glob) { /* if globbing is requested */
+       if (o->o_expflags & EXP_FLAG_GLOB) {
                /* If o->has_empty_slot, list[n] was already globbed
                 * (if it was requested back then when it was filled)
                 * so don't do that again! */
                if (!o->has_empty_slot)
-                       return o_glob(o, n); /* o_save_ptr_helper is inside */
+                       return perform_glob(o, n); /* o_save_ptr_helper is inside */
        }
        return o_save_ptr_helper(o, n);
 }
@@ -2330,1079 +2713,851 @@ static char **o_finalize_list(o_string *o, int n)
        list[--n] = NULL;
        while (n) {
                n--;
-               list[n] = o->data + (int)(ptrdiff_t)list[n] + string_start;
+               list[n] = o->data + (int)(uintptr_t)list[n] + string_start;
        }
        return list;
 }
 
+static void free_pipe_list(struct pipe *pi);
 
-/* Expansion can recurse */
-#if ENABLE_HUSH_TICK
-static int process_command_subs(o_string *dest, const char *s);
+/* Returns pi->next - next pipe in the list */
+static struct pipe *free_pipe(struct pipe *pi)
+{
+       struct pipe *next;
+       int i;
+
+       debug_printf_clean("free_pipe (pid %d)\n", getpid());
+       for (i = 0; i < pi->num_cmds; i++) {
+               struct command *command;
+               struct redir_struct *r, *rnext;
+
+               command = &pi->cmds[i];
+               debug_printf_clean("  command %d:\n", i);
+               if (command->argv) {
+                       if (DEBUG_CLEAN) {
+                               int a;
+                               char **p;
+                               for (a = 0, p = command->argv; *p; a++, p++) {
+                                       debug_printf_clean("   argv[%d] = %s\n", a, *p);
+                               }
+                       }
+                       free_strings(command->argv);
+                       //command->argv = NULL;
+               }
+               /* not "else if": on syntax error, we may have both! */
+               if (command->group) {
+                       debug_printf_clean("   begin group (cmd_type:%d)\n",
+                                       command->cmd_type);
+                       free_pipe_list(command->group);
+                       debug_printf_clean("   end group\n");
+                       //command->group = NULL;
+               }
+               /* else is crucial here.
+                * If group != NULL, child_func is meaningless */
+#if ENABLE_HUSH_FUNCTIONS
+               else if (command->child_func) {
+                       debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func);
+                       command->child_func->parent_cmd = NULL;
+               }
 #endif
-static char *expand_string_to_string(const char *str);
-#if BB_MMU
-#define parse_stream_dquoted(as_string, dest, input, dquote_end) \
-       parse_stream_dquoted(dest, input, dquote_end)
+#if !BB_MMU
+               free(command->group_as_string);
+               //command->group_as_string = NULL;
+#endif
+               for (r = command->redirects; r; r = rnext) {
+                       debug_printf_clean("   redirect %d%s",
+                                       r->rd_fd, redir_table[r->rd_type].descrip);
+                       /* guard against the case >$FOO, where foo is unset or blank */
+                       if (r->rd_filename) {
+                               debug_printf_clean(" fname:'%s'\n", r->rd_filename);
+                               free(r->rd_filename);
+                               //r->rd_filename = NULL;
+                       }
+                       debug_printf_clean(" rd_dup:%d\n", r->rd_dup);
+                       rnext = r->next;
+                       free(r);
+               }
+               //command->redirects = NULL;
+       }
+       free(pi->cmds);   /* children are an array, they get freed all at once */
+       //pi->cmds = NULL;
+#if ENABLE_HUSH_JOB
+       free(pi->cmdtext);
+       //pi->cmdtext = NULL;
 #endif
-static int parse_stream_dquoted(o_string *as_string,
-               o_string *dest,
-               struct in_str *input,
-               int dquote_end);
 
-/* expand_strvec_to_strvec() takes a list of strings, expands
- * all variable references within and returns a pointer to
- * a list of expanded strings, possibly with larger number
- * of strings. (Think VAR="a b"; echo $VAR).
- * This new list is allocated as a single malloc block.
- * NULL-terminated list of char* pointers is at the beginning of it,
- * followed by strings themself.
- * Caller can deallocate entire list by single free(list). */
+       next = pi->next;
+       free(pi);
+       return next;
+}
 
-/* Store given string, finalizing the word and starting new one whenever
- * we encounter IFS char(s). This is used for expanding variable values.
- * End-of-string does NOT finalize word: think about 'echo -$VAR-' */
-static int expand_on_ifs(o_string *output, int n, const char *str)
+static void free_pipe_list(struct pipe *pi)
 {
-       while (1) {
-               int word_len = strcspn(str, G.ifs);
-               if (word_len) {
-                       if (output->o_escape || !output->o_glob)
-                               o_addQstr(output, str, word_len);
-                       else /* protect backslashes against globbing up :) */
-                               o_addblock_duplicate_backslash(output, str, word_len);
-                       str += word_len;
-               }
-               if (!*str)  /* EOL - do not finalize word */
-                       break;
-               o_addchr(output, '\0');
-               debug_print_list("expand_on_ifs", output, n);
-               n = o_save_ptr(output, n);
-               str += strspn(str, G.ifs); /* skip ifs chars */
+       while (pi) {
+#if HAS_KEYWORDS
+               debug_printf_clean("pipe reserved word %d\n", pi->res_word);
+#endif
+               debug_printf_clean("pipe followup code %d\n", pi->followup);
+               pi = free_pipe(pi);
        }
-       debug_print_list("expand_on_ifs[1]", output, n);
-       return n;
 }
 
-/* Helper to expand $((...)) and heredoc body. These act as if
- * they are in double quotes, with the exception that they are not :).
- * Just the rules are similar: "expand only $var and `cmd`"
- *
- * Returns malloced string.
- * As an optimization, we return NULL if expansion is not needed.
- */
-static char *expand_pseudo_dquoted(const char *str)
+
+/*** Parsing routines ***/
+
+#ifndef debug_print_tree
+static void debug_print_tree(struct pipe *pi, int lvl)
 {
-       char *exp_str;
-       struct in_str input;
-       o_string dest = NULL_O_STRING;
+       static const char *const PIPE[] = {
+               [PIPE_SEQ] = "SEQ",
+               [PIPE_AND] = "AND",
+               [PIPE_OR ] = "OR" ,
+               [PIPE_BG ] = "BG" ,
+       };
+       static const char *RES[] = {
+               [RES_NONE ] = "NONE" ,
+# if ENABLE_HUSH_IF
+               [RES_IF   ] = "IF"   ,
+               [RES_THEN ] = "THEN" ,
+               [RES_ELIF ] = "ELIF" ,
+               [RES_ELSE ] = "ELSE" ,
+               [RES_FI   ] = "FI"   ,
+# endif
+# if ENABLE_HUSH_LOOPS
+               [RES_FOR  ] = "FOR"  ,
+               [RES_WHILE] = "WHILE",
+               [RES_UNTIL] = "UNTIL",
+               [RES_DO   ] = "DO"   ,
+               [RES_DONE ] = "DONE" ,
+# endif
+# if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
+               [RES_IN   ] = "IN"   ,
+# endif
+# if ENABLE_HUSH_CASE
+               [RES_CASE ] = "CASE" ,
+               [RES_CASE_IN ] = "CASE_IN" ,
+               [RES_MATCH] = "MATCH",
+               [RES_CASE_BODY] = "CASE_BODY",
+               [RES_ESAC ] = "ESAC" ,
+# endif
+               [RES_XXXX ] = "XXXX" ,
+               [RES_SNTX ] = "SNTX" ,
+       };
+       static const char *const CMDTYPE[] = {
+               "{}",
+               "()",
+               "[noglob]",
+# if ENABLE_HUSH_FUNCTIONS
+               "func()",
+# endif
+       };
 
-       if (strchr(str, '$') == NULL
-#if ENABLE_HUSH_TICK
-        && strchr(str, '`') == NULL
-#endif
-       ) {
-               return NULL;
+       int pin, prn;
+
+       pin = 0;
+       while (pi) {
+               fdprintf(2, "%*spipe %d res_word=%s followup=%d %s\n", lvl*2, "",
+                               pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]);
+               prn = 0;
+               while (prn < pi->num_cmds) {
+                       struct command *command = &pi->cmds[prn];
+                       char **argv = command->argv;
+
+                       fdprintf(2, "%*s cmd %d assignment_cnt:%d",
+                                       lvl*2, "", prn,
+                                       command->assignment_cnt);
+                       if (command->group) {
+                               fdprintf(2, " group %s: (argv=%p)%s%s\n",
+                                               CMDTYPE[command->cmd_type],
+                                               argv
+# if !BB_MMU
+                                               , " group_as_string:", command->group_as_string
+# else
+                                               , "", ""
+# endif
+                               );
+                               debug_print_tree(command->group, lvl+1);
+                               prn++;
+                               continue;
+                       }
+                       if (argv) while (*argv) {
+                               fdprintf(2, " '%s'", *argv);
+                               argv++;
+                       }
+                       fdprintf(2, "\n");
+                       prn++;
+               }
+               pi = pi->next;
+               pin++;
        }
+}
+#endif /* debug_print_tree */
 
-       /* We need to expand. Example:
-        * echo $(($a + `echo 1`)) $((1 + $((2)) ))
-        */
-       setup_string_in_str(&input, str);
-       parse_stream_dquoted(NULL, &dest, &input, EOF);
-       //bb_error_msg("'%s' -> '%s'", str, dest.data);
-       exp_str = expand_string_to_string(dest.data);
-       //bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
-       o_free_unsafe(&dest);
-       return exp_str;
+static struct pipe *new_pipe(void)
+{
+       struct pipe *pi;
+       pi = xzalloc(sizeof(struct pipe));
+       /*pi->followup = 0; - deliberately invalid value */
+       /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */
+       return pi;
 }
 
-#if ENABLE_SH_MATH_SUPPORT
-static arith_t expand_and_evaluate_arith(const char *arg, int *errcode_p)
+/* Command (member of a pipe) is complete, or we start a new pipe
+ * if ctx->command is NULL.
+ * No errors possible here.
+ */
+static int done_command(struct parse_context *ctx)
 {
-       arith_eval_hooks_t hooks;
-       arith_t res;
-       char *exp_str;
+       /* The command is really already in the pipe structure, so
+        * advance the pipe counter and make a new, null command. */
+       struct pipe *pi = ctx->pipe;
+       struct command *command = ctx->command;
 
-       hooks.lookupvar = get_local_var_value;
-       hooks.setvar = set_local_var_from_halves;
-       hooks.endofname = endofname;
-       exp_str = expand_pseudo_dquoted(arg);
-       res = arith(exp_str ? exp_str : arg, errcode_p, &hooks);
-       free(exp_str);
-       return res;
+       if (command) {
+               if (IS_NULL_CMD(command)) {
+                       debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
+                       goto clear_and_ret;
+               }
+               pi->num_cmds++;
+               debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
+               //debug_print_tree(ctx->list_head, 20);
+       } else {
+               debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds);
+       }
+
+       /* Only real trickiness here is that the uncommitted
+        * command structure is not counted in pi->num_cmds. */
+       pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
+       ctx->command = command = &pi->cmds[pi->num_cmds];
+ clear_and_ret:
+       memset(command, 0, sizeof(*command));
+       return pi->num_cmds; /* used only for 0/nonzero check */
 }
-#endif
 
-/* Expand all variable references in given string, adding words to list[]
- * at n, n+1,... positions. Return updated n (so that list[n] is next one
- * to be filled). This routine is extremely tricky: has to deal with
- * variables/parameters with whitespace, $* and $@, and constructs like
- * 'echo -$*-'. If you play here, you must run testsuite afterwards! */
-static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
+static void done_pipe(struct parse_context *ctx, pipe_style type)
 {
-       /* or_mask is either 0 (normal case) or 0x80 -
-        * expansion of right-hand side of assignment == 1-element expand.
-        * It will also do no globbing, and thus we must not backslash-quote!
-        */
-       char ored_ch;
-       char *p;
-
-       ored_ch = 0;
+       int not_null;
 
-       debug_printf_expand("expand_vars_to_list: arg:'%s' or_mask:%x\n", arg, or_mask);
-       debug_print_list("expand_vars_to_list", output, n);
-       n = o_save_ptr(output, n);
-       debug_print_list("expand_vars_to_list[0]", output, n);
+       debug_printf_parse("done_pipe entered, followup %d\n", type);
+       /* Close previous command */
+       not_null = done_command(ctx);
+       ctx->pipe->followup = type;
+#if HAS_KEYWORDS
+       ctx->pipe->pi_inverted = ctx->ctx_inverted;
+       ctx->ctx_inverted = 0;
+       ctx->pipe->res_word = ctx->ctx_res_w;
+#endif
 
-       while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) {
-               char first_ch;
-               int i;
-               char *to_be_freed = NULL;
-               const char *val = NULL;
-#if ENABLE_HUSH_TICK
-               o_string subst_result = NULL_O_STRING;
+       /* Without this check, even just <enter> on command line generates
+        * tree of three NOPs (!). Which is harmless but annoying.
+        * IOW: it is safe to do it unconditionally. */
+       if (not_null
+#if ENABLE_HUSH_IF
+        || ctx->ctx_res_w == RES_FI
 #endif
-#if ENABLE_SH_MATH_SUPPORT
-               char arith_buf[sizeof(arith_t)*3 + 2];
+#if ENABLE_HUSH_LOOPS
+        || ctx->ctx_res_w == RES_DONE
+        || ctx->ctx_res_w == RES_FOR
+        || ctx->ctx_res_w == RES_IN
 #endif
-               o_addblock(output, arg, p - arg);
-               debug_print_list("expand_vars_to_list[1]", output, n);
-               arg = ++p;
-               p = strchr(p, SPECIAL_VAR_SYMBOL);
+#if ENABLE_HUSH_CASE
+        || ctx->ctx_res_w == RES_ESAC
+#endif
+       ) {
+               struct pipe *new_p;
+               debug_printf_parse("done_pipe: adding new pipe: "
+                               "not_null:%d ctx->ctx_res_w:%d\n",
+                               not_null, ctx->ctx_res_w);
+               new_p = new_pipe();
+               ctx->pipe->next = new_p;
+               ctx->pipe = new_p;
+               /* RES_THEN, RES_DO etc are "sticky" -
+                * they remain set for pipes inside if/while.
+                * This is used to control execution.
+                * RES_FOR and RES_IN are NOT sticky (needed to support
+                * cases where variable or value happens to match a keyword):
+                */
+#if ENABLE_HUSH_LOOPS
+               if (ctx->ctx_res_w == RES_FOR
+                || ctx->ctx_res_w == RES_IN)
+                       ctx->ctx_res_w = RES_NONE;
+#endif
+#if ENABLE_HUSH_CASE
+               if (ctx->ctx_res_w == RES_MATCH)
+                       ctx->ctx_res_w = RES_CASE_BODY;
+               if (ctx->ctx_res_w == RES_CASE)
+                       ctx->ctx_res_w = RES_CASE_IN;
+#endif
+               ctx->command = NULL; /* trick done_command below */
+               /* Create the memory for command, roughly:
+                * ctx->pipe->cmds = new struct command;
+                * ctx->command = &ctx->pipe->cmds[0];
+                */
+               done_command(ctx);
+               //debug_print_tree(ctx->list_head, 10);
+       }
+       debug_printf_parse("done_pipe return\n");
+}
 
-               first_ch = arg[0] | or_mask; /* forced to "quoted" if or_mask = 0x80 */
-               /* "$@" is special. Even if quoted, it can still
-                * expand to nothing (not even an empty string) */
-               if ((first_ch & 0x7f) != '@')
-                       ored_ch |= first_ch;
+static void initialize_context(struct parse_context *ctx)
+{
+       memset(ctx, 0, sizeof(*ctx));
+       ctx->pipe = ctx->list_head = new_pipe();
+       /* Create the memory for command, roughly:
+        * ctx->pipe->cmds = new struct command;
+        * ctx->command = &ctx->pipe->cmds[0];
+        */
+       done_command(ctx);
+}
 
-               switch (first_ch & 0x7f) {
-               /* Highest bit in first_ch indicates that var is double-quoted */
-               case '*':
-               case '@':
-                       i = 1;
-                       if (!G.global_argv[i])
-                               break;
-                       ored_ch |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
-                       if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
-                               smallint sv = output->o_escape;
-                               /* unquoted var's contents should be globbed, so don't escape */
-                               output->o_escape = 0;
-                               while (G.global_argv[i]) {
-                                       n = expand_on_ifs(output, n, G.global_argv[i]);
-                                       debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1);
-                                       if (G.global_argv[i++][0] && G.global_argv[i]) {
-                                               /* this argv[] is not empty and not last:
-                                                * put terminating NUL, start new word */
-                                               o_addchr(output, '\0');
-                                               debug_print_list("expand_vars_to_list[2]", output, n);
-                                               n = o_save_ptr(output, n);
-                                               debug_print_list("expand_vars_to_list[3]", output, n);
-                                       }
-                               }
-                               output->o_escape = sv;
-                       } else
-                       /* If or_mask is nonzero, we handle assignment 'a=....$@.....'
-                        * and in this case should treat it like '$*' - see 'else...' below */
-                       if (first_ch == ('@'|0x80) && !or_mask) { /* quoted $@ */
-                               while (1) {
-                                       o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i]));
-                                       if (++i >= G.global_argc)
-                                               break;
-                                       o_addchr(output, '\0');
-                                       debug_print_list("expand_vars_to_list[4]", output, n);
-                                       n = o_save_ptr(output, n);
-                               }
-                       } else { /* quoted $*: add as one word */
-                               while (1) {
-                                       o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i]));
-                                       if (!G.global_argv[++i])
-                                               break;
-                                       if (G.ifs[0])
-                                               o_addchr(output, G.ifs[0]);
-                               }
-                       }
-                       break;
-               case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */
-                       /* "Empty variable", used to make "" etc to not disappear */
-                       arg++;
-                       ored_ch = 0x80;
-                       break;
-#if ENABLE_HUSH_TICK
-               case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */
-                       *p = '\0';
-                       arg++;
-                       /* Can't just stuff it into output o_string,
-                        * expanded result may need to be globbed
-                        * and $IFS-splitted */
-                       debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch);
-                       G.last_exitcode = process_command_subs(&subst_result, arg);
-                       debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data);
-                       val = subst_result.data;
-                       goto store_val;
-#endif
-#if ENABLE_SH_MATH_SUPPORT
-               case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */
-                       arith_t res;
-                       int errcode;
-
-                       arg++; /* skip '+' */
-                       *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
-                       debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch);
-                       res = expand_and_evaluate_arith(arg, &errcode);
-
-                       if (errcode < 0) {
-                               const char *msg = "error in arithmetic";
-                               switch (errcode) {
-                               case -3:
-                                       msg = "exponent less than 0";
-                                       break;
-                               case -2:
-                                       msg = "divide by 0";
-                                       break;
-                               case -5:
-                                       msg = "expression recursion loop detected";
-                                       break;
-                               }
-                               die_if_script(msg);
-                       }
-                       debug_printf_subst("ARITH RES '"arith_t_fmt"'\n", res);
-                       sprintf(arith_buf, arith_t_fmt, res);
-                       val = arith_buf;
-                       break;
-               }
-#endif
-               default: { /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */
-                       char *var;
-                       char first_char;
-                       char exp_op;
-                       char exp_save = exp_save; /* for compiler */
-                       char *exp_saveptr; /* points to expansion operator */
-                       char *exp_word = exp_word; /* for compiler */
-
-                       var = arg;
-                       *p = '\0';
-                       exp_saveptr = arg[1] ? strchr("%#:-=+?", arg[1]) : NULL;
-                       first_char = arg[0] = first_ch & 0x7f;
-                       exp_op = 0;
-
-                       if (first_char == '#' && arg[1] && !exp_saveptr) {
-                               /* handle length expansion ${#var} */
-                               var++;
-                               exp_op = 'L';
-                       } else {
-                               /* maybe handle parameter expansion */
-                               if (exp_saveptr /* if 2nd char is one of expansion operators */
-                                && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */
-                               ) {
-                                       /* ${?:0}, ${#[:]%0} etc */
-                                       exp_saveptr = var + 1;
-                               } else {
-                                       /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */
-                                       exp_saveptr = var+1 + strcspn(var+1, "%#:-=+?");
-                               }
-                               exp_op = exp_save = *exp_saveptr;
-                               if (exp_op) {
-                                       exp_word = exp_saveptr + 1;
-                                       if (exp_op == ':') {
-                                               exp_op = *exp_word++;
-                                               if (ENABLE_HUSH_BASH_COMPAT
-                                                && (exp_op == '\0' || !strchr("%#:-=+?"+3, exp_op))
-                                               ) {
-                                                       /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
-                                                       exp_op = ':';
-                                                       exp_word--;
-                                               }
-                                       }
-                                       *exp_saveptr = '\0';
-                               } /* else: it's not an expansion op, but bare ${var} */
-                       }
-
-                       /* lookup the variable in question */
-                       if (isdigit(var[0])) {
-                               /* parse_dollar() should have vetted var for us */
-                               i = xatoi_u(var);
-                               if (i < G.global_argc)
-                                       val = G.global_argv[i];
-                               /* else val remains NULL: $N with too big N */
-                       } else {
-                               switch (var[0]) {
-                               case '$': /* pid */
-                                       val = utoa(G.root_pid);
-                                       break;
-                               case '!': /* bg pid */
-                                       val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)"";
-                                       break;
-                               case '?': /* exitcode */
-                                       val = utoa(G.last_exitcode);
-                                       break;
-                               case '#': /* argc */
-                                       val = utoa(G.global_argc ? G.global_argc-1 : 0);
-                                       break;
-                               default:
-                                       val = get_local_var_value(var);
-                               }
-                       }
+/* If a reserved word is found and processed, parse context is modified
+ * and 1 is returned.
+ */
+#if HAS_KEYWORDS
+struct reserved_combo {
+       char literal[6];
+       unsigned char res;
+       unsigned char assignment_flag;
+       int flag;
+};
+enum {
+       FLAG_END   = (1 << RES_NONE ),
+# if ENABLE_HUSH_IF
+       FLAG_IF    = (1 << RES_IF   ),
+       FLAG_THEN  = (1 << RES_THEN ),
+       FLAG_ELIF  = (1 << RES_ELIF ),
+       FLAG_ELSE  = (1 << RES_ELSE ),
+       FLAG_FI    = (1 << RES_FI   ),
+# endif
+# if ENABLE_HUSH_LOOPS
+       FLAG_FOR   = (1 << RES_FOR  ),
+       FLAG_WHILE = (1 << RES_WHILE),
+       FLAG_UNTIL = (1 << RES_UNTIL),
+       FLAG_DO    = (1 << RES_DO   ),
+       FLAG_DONE  = (1 << RES_DONE ),
+       FLAG_IN    = (1 << RES_IN   ),
+# endif
+# if ENABLE_HUSH_CASE
+       FLAG_MATCH = (1 << RES_MATCH),
+       FLAG_ESAC  = (1 << RES_ESAC ),
+# endif
+       FLAG_START = (1 << RES_XXXX ),
+};
 
-                       /* handle any expansions */
-                       if (exp_op == 'L') {
-                               debug_printf_expand("expand: length(%s)=", val);
-                               val = utoa(val ? strlen(val) : 0);
-                               debug_printf_expand("%s\n", val);
-                       } else if (exp_op) {
-                               if (exp_op == '%' || exp_op == '#') {
-       /* Standard-mandated substring removal ops:
-        * ${parameter%word} - remove smallest suffix pattern
-        * ${parameter%%word} - remove largest suffix pattern
-        * ${parameter#word} - remove smallest prefix pattern
-        * ${parameter##word} - remove largest prefix pattern
-        *
-        * Word is expanded to produce a glob pattern.
-        * Then var's value is matched to it and matching part removed.
-        */
-                                       if (val) {
-                                               bool match_at_left;
-                                               char *loc;
-                                               scan_t scan = pick_scan(exp_op, *exp_word, &match_at_left);
-                                               if (exp_op == *exp_word)        /* ## or %% */
-                                                       exp_word++;
-                                               val = to_be_freed = xstrdup(val);
-                                               {
-                                                       char *exp_exp_word = expand_pseudo_dquoted(exp_word);
-                                                       if (exp_exp_word)
-                                                               exp_word = exp_exp_word;
-                                                       loc = scan(to_be_freed, exp_word, match_at_left);
-                                                       //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'",
-                                                       //              exp_op, to_be_freed, exp_word, loc);
-                                                       free(exp_exp_word);
-                                               }
-                                               if (loc) { /* match was found */
-                                                       if (match_at_left) /* # or ## */
-                                                               val = loc;
-                                                       else /* % or %% */
-                                                               *loc = '\0';
-                                               }
-                                       }
-                               } else if (exp_op == ':') {
-#if ENABLE_HUSH_BASH_COMPAT && ENABLE_SH_MATH_SUPPORT
-       /* It's ${var:N[:M]} bashism.
-        * Note that in encoded form it has TWO parts:
-        * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
-        */
-                                       arith_t beg, len;
-                                       int errcode = 0;
-
-                                       beg = expand_and_evaluate_arith(exp_word, &errcode);
-                                       debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
-                                       *p++ = SPECIAL_VAR_SYMBOL;
-                                       exp_word = p;
-                                       p = strchr(p, SPECIAL_VAR_SYMBOL);
-                                       *p = '\0';
-                                       len = expand_and_evaluate_arith(exp_word, &errcode);
-                                       debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
-
-                                       if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */
-                                               if (beg < 0) /* bash compat */
-                                                       beg = 0;
-                                               debug_printf_varexp("from val:'%s'\n", val);
-                                               if (len == 0 || !val || beg >= strlen(val))
-                                                       val = "";
-                                               else {
-                                                       /* Paranoia. What if user entered 9999999999999
-                                                        * which fits in arith_t but not int? */
-                                                       if (len >= INT_MAX)
-                                                               len = INT_MAX;
-                                                       val = to_be_freed = xstrndup(val + beg, len);
-                                               }
-                                               debug_printf_varexp("val:'%s'\n", val);
-                                       } else
-#endif
-                                       {
-                                               die_if_script("malformed ${%s:...}", var);
-                                               val = "";
-                                       }
-                               } else { /* one of "-=+?" */
-       /* Standard-mandated substitution ops:
-        * ${var?word} - indicate error if unset
-        *      If var is unset, word (or a message indicating it is unset
-        *      if word is null) is written to standard error
-        *      and the shell exits with a non-zero exit status.
-        *      Otherwise, the value of var is substituted.
-        * ${var-word} - use default value
-        *      If var is unset, word is substituted.
-        * ${var=word} - assign and use default value
-        *      If var is unset, word is assigned to var.
-        *      In all cases, final value of var is substituted.
-        * ${var+word} - use alternative value
-        *      If var is unset, null is substituted.
-        *      Otherwise, word is substituted.
-        *
-        * Word is subjected to tilde expansion, parameter expansion,
-        * command substitution, and arithmetic expansion.
-        * If word is not needed, it is not expanded.
-        *
-        * Colon forms (${var:-word}, ${var:=word} etc) do the same,
-        * but also treat null var as if it is unset.
+static const struct reserved_combo* match_reserved_word(o_string *word)
+{
+       /* Mostly a list of accepted follow-up reserved words.
+        * FLAG_END means we are done with the sequence, and are ready
+        * to turn the compound list into a command.
+        * FLAG_START means the word must start a new compound list.
         */
-                                       int use_word = (!val || ((exp_save == ':') && !val[0]));
-                                       if (exp_op == '+')
-                                               use_word = !use_word;
-                                       debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
-                                               (exp_save == ':') ? "true" : "false", use_word);
-                                       if (use_word) {
-                                               to_be_freed = expand_pseudo_dquoted(exp_word);
-                                               if (to_be_freed)
-                                                       exp_word = to_be_freed;
-                                               if (exp_op == '?') {
-                                                       /* mimic bash message */
-                                                       die_if_script("%s: %s",
-                                                               var,
-                                                               exp_word[0] ? exp_word : "parameter null or not set"
-                                                       );
-//TODO: how interactive bash aborts expansion mid-command?
-                                               } else {
-                                                       val = exp_word;
-                                               }
-
-                                               if (exp_op == '=') {
-                                                       /* ${var=[word]} or ${var:=[word]} */
-                                                       if (isdigit(var[0]) || var[0] == '#') {
-                                                               /* mimic bash message */
-                                                               die_if_script("$%s: cannot assign in this way", var);
-                                                               val = NULL;
-                                                       } else {
-                                                               char *new_var = xasprintf("%s=%s", var, val);
-                                                               set_local_var(new_var, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
-                                                       }
-                                               }
-                                       }
-                               } /* one of "-=+?" */
+       static const struct reserved_combo reserved_list[] = {
+# if ENABLE_HUSH_IF
+               { "!",     RES_NONE,  NOT_ASSIGNMENT  , 0 },
+               { "if",    RES_IF,    MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START },
+               { "then",  RES_THEN,  MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
+               { "elif",  RES_ELIF,  MAYBE_ASSIGNMENT, FLAG_THEN },
+               { "else",  RES_ELSE,  MAYBE_ASSIGNMENT, FLAG_FI   },
+               { "fi",    RES_FI,    NOT_ASSIGNMENT  , FLAG_END  },
+# endif
+# if ENABLE_HUSH_LOOPS
+               { "for",   RES_FOR,   NOT_ASSIGNMENT  , FLAG_IN | FLAG_DO | FLAG_START },
+               { "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
+               { "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
+               { "in",    RES_IN,    NOT_ASSIGNMENT  , FLAG_DO   },
+               { "do",    RES_DO,    MAYBE_ASSIGNMENT, FLAG_DONE },
+               { "done",  RES_DONE,  NOT_ASSIGNMENT  , FLAG_END  },
+# endif
+# if ENABLE_HUSH_CASE
+               { "case",  RES_CASE,  NOT_ASSIGNMENT  , FLAG_MATCH | FLAG_START },
+               { "esac",  RES_ESAC,  NOT_ASSIGNMENT  , FLAG_END  },
+# endif
+       };
+       const struct reserved_combo *r;
 
-                               *exp_saveptr = exp_save;
-                       } /* if (exp_op) */
+       for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) {
+               if (strcmp(word->data, r->literal) == 0)
+                       return r;
+       }
+       return NULL;
+}
+/* Return 0: not a keyword, 1: keyword
+ */
+static int reserved_word(o_string *word, struct parse_context *ctx)
+{
+# if ENABLE_HUSH_CASE
+       static const struct reserved_combo reserved_match = {
+               "",        RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC
+       };
+# endif
+       const struct reserved_combo *r;
 
-                       arg[0] = first_ch;
-#if ENABLE_HUSH_TICK
- store_val:
-#endif
-                       if (!(first_ch & 0x80)) { /* unquoted $VAR */
-                               debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, output->o_escape);
-                               if (val) {
-                                       /* unquoted var's contents should be globbed, so don't escape */
-                                       smallint sv = output->o_escape;
-                                       output->o_escape = 0;
-                                       n = expand_on_ifs(output, n, val);
-                                       val = NULL;
-                                       output->o_escape = sv;
-                               }
-                       } else { /* quoted $VAR, val will be appended below */
-                               debug_printf_expand("quoted '%s', output->o_escape:%d\n", val, output->o_escape);
-                       }
-               } /* default: */
-               } /* switch (char after <SPECIAL_VAR_SYMBOL>) */
+       if (word->has_quoted_part)
+               return 0;
+       r = match_reserved_word(word);
+       if (!r)
+               return 0;
 
-               if (val) {
-                       o_addQstr(output, val, strlen(val));
+       debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
+# if ENABLE_HUSH_CASE
+       if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) {
+               /* "case word IN ..." - IN part starts first MATCH part */
+               r = &reserved_match;
+       } else
+# endif
+       if (r->flag == 0) { /* '!' */
+               if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
+                       syntax_error("! ! command");
+                       ctx->ctx_res_w = RES_SNTX;
                }
-               free(to_be_freed);
-               /* Do the check to avoid writing to a const string */
-               if (*p != SPECIAL_VAR_SYMBOL)
-                       *p = SPECIAL_VAR_SYMBOL;
-
-#if ENABLE_HUSH_TICK
-               o_free(&subst_result);
-#endif
-               arg = ++p;
-       } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */
+               ctx->ctx_inverted = 1;
+               return 1;
+       }
+       if (r->flag & FLAG_START) {
+               struct parse_context *old;
 
-       if (arg[0]) {
-               debug_print_list("expand_vars_to_list[a]", output, n);
-               /* this part is literal, and it was already pre-quoted
-                * if needed (much earlier), do not use o_addQstr here! */
-               o_addstr_with_NUL(output, arg);
-               debug_print_list("expand_vars_to_list[b]", output, n);
-       } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */
-        && !(ored_ch & 0x80) /* and all vars were not quoted. */
-       ) {
-               n--;
-               /* allow to reuse list[n] later without re-growth */
-               output->has_empty_slot = 1;
+               old = xmalloc(sizeof(*old));
+               debug_printf_parse("push stack %p\n", old);
+               *old = *ctx;   /* physical copy */
+               initialize_context(ctx);
+               ctx->stack = old;
+       } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
+               syntax_error_at(word->data);
+               ctx->ctx_res_w = RES_SNTX;
+               return 1;
        } else {
-               o_addchr(output, '\0');
+               /* "{...} fi" is ok. "{...} if" is not
+                * Example:
+                * if { echo foo; } then { echo bar; } fi */
+               if (ctx->command->group)
+                       done_pipe(ctx, PIPE_SEQ);
        }
-       return n;
-}
 
-static char **expand_variables(char **argv, int or_mask)
-{
-       int n;
-       char **list;
-       char **v;
-       o_string output = NULL_O_STRING;
+       ctx->ctx_res_w = r->res;
+       ctx->old_flag = r->flag;
+       word->o_assignment = r->assignment_flag;
+       debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]);
 
-       if (or_mask & 0x100) {
-               output.o_escape = 1; /* protect against globbing for "$var" */
-               /* (unquoted $var will temporarily switch it off) */
-               output.o_glob = 1;
-       }
+       if (ctx->old_flag & FLAG_END) {
+               struct parse_context *old;
 
-       n = 0;
-       v = argv;
-       while (*v) {
-               n = expand_vars_to_list(&output, n, *v, (unsigned char)or_mask);
-               v++;
+               done_pipe(ctx, PIPE_SEQ);
+               debug_printf_parse("pop stack %p\n", ctx->stack);
+               old = ctx->stack;
+               old->command->group = ctx->list_head;
+               old->command->cmd_type = CMD_NORMAL;
+# if !BB_MMU
+               o_addstr(&old->as_string, ctx->as_string.data);
+               o_free_unsafe(&ctx->as_string);
+               old->command->group_as_string = xstrdup(old->as_string.data);
+               debug_printf_parse("pop, remembering as:'%s'\n",
+                               old->command->group_as_string);
+# endif
+               *ctx = *old;   /* physical copy */
+               free(old);
        }
-       debug_print_list("expand_variables", &output, n);
-
-       /* output.data (malloced in one block) gets returned in "list" */
-       list = o_finalize_list(&output, n);
-       debug_print_strings("expand_variables[1]", list);
-       return list;
+       return 1;
 }
+#endif /* HAS_KEYWORDS */
 
-static char **expand_strvec_to_strvec(char **argv)
+/* Word is complete, look at it and update parsing context.
+ * Normal return is 0. Syntax errors return 1.
+ * Note: on return, word is reset, but not o_free'd!
+ */
+static int done_word(o_string *word, struct parse_context *ctx)
 {
-       return expand_variables(argv, 0x100);
-}
+       struct command *command = ctx->command;
 
-#if ENABLE_HUSH_BASH_COMPAT
-static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
-{
-       return expand_variables(argv, 0x80);
-}
-#endif
+       debug_printf_parse("done_word entered: '%s' %p\n", word->data, command);
+       if (word->length == 0 && !word->has_quoted_part) {
+               debug_printf_parse("done_word return 0: true null, ignored\n");
+               return 0;
+       }
 
-#ifdef CMD_SINGLEWORD_NOGLOB_COND
-static char **expand_strvec_to_strvec_singleword_noglob_cond(char **argv)
-{
-       int n;
-       char **list;
-       char **v;
-       o_string output = NULL_O_STRING;
-
-       n = 0;
-       v = argv;
-       while (*v) {
-               int is_var = is_well_formed_var_name(*v, '=');
-               /* is_var * 0x80: singleword expansion for vars */
-               n = expand_vars_to_list(&output, n, *v, is_var * 0x80);
-
-               /* Subtle! expand_vars_to_list did not glob last word yet.
-                * It does this only when fed with further data.
-                * Therefore we set globbing flags AFTER it, not before:
+       if (ctx->pending_redirect) {
+               /* We do not glob in e.g. >*.tmp case. bash seems to glob here
+                * only if run as "bash", not "sh" */
+               /* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
+                * "2.7 Redirection
+                * ...the word that follows the redirection operator
+                * shall be subjected to tilde expansion, parameter expansion,
+                * command substitution, arithmetic expansion, and quote
+                * removal. Pathname expansion shall not be performed
+                * on the word by a non-interactive shell; an interactive
+                * shell may perform it, but shall do so only when
+                * the expansion would result in one word."
+                */
+               ctx->pending_redirect->rd_filename = xstrdup(word->data);
+               /* Cater for >\file case:
+                * >\a creates file a; >\\a, >"\a", >"\\a" create file \a
+                * Same with heredocs:
+                * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H
                 */
+               if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) {
+                       unbackslash(ctx->pending_redirect->rd_filename);
+                       /* Is it <<"HEREDOC"? */
+                       if (word->has_quoted_part) {
+                               ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED;
+                       }
+               }
+               debug_printf_parse("word stored in rd_filename: '%s'\n", word->data);
+               ctx->pending_redirect = NULL;
+       } else {
+#if HAS_KEYWORDS
+# if ENABLE_HUSH_CASE
+               if (ctx->ctx_dsemicolon
+                && strcmp(word->data, "esac") != 0 /* not "... pattern) cmd;; esac" */
+               ) {
+                       /* already done when ctx_dsemicolon was set to 1: */
+                       /* ctx->ctx_res_w = RES_MATCH; */
+                       ctx->ctx_dsemicolon = 0;
+               } else
+# endif
+               if (!command->argv /* if it's the first word... */
+# if ENABLE_HUSH_LOOPS
+                && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
+                && ctx->ctx_res_w != RES_IN
+# endif
+# if ENABLE_HUSH_CASE
+                && ctx->ctx_res_w != RES_CASE
+# endif
+               ) {
+                       int reserved = reserved_word(word, ctx);
+                       debug_printf_parse("checking for reserved-ness: %d\n", reserved);
+                       if (reserved) {
+                               o_reset_to_empty_unquoted(word);
+                               debug_printf_parse("done_word return %d\n",
+                                               (ctx->ctx_res_w == RES_SNTX));
+                               return (ctx->ctx_res_w == RES_SNTX);
+                       }
+# if ENABLE_HUSH_BASH_COMPAT
+                       if (strcmp(word->data, "[[") == 0) {
+                               command->cmd_type = CMD_SINGLEWORD_NOGLOB;
+                       }
+                       /* fall through */
+# endif
+               }
+#endif
+               if (command->group) {
+                       /* "{ echo foo; } echo bar" - bad */
+                       syntax_error_at(word->data);
+                       debug_printf_parse("done_word return 1: syntax error, "
+                                       "groups and arglists don't mix\n");
+                       return 1;
+               }
 
-               /* if it is not recognizably abc=...; then: */
-               output.o_escape = !is_var; /* protect against globbing for "$var" */
-               /* (unquoted $var will temporarily switch it off) */
-               output.o_glob = !is_var; /* and indeed do globbing */
-               v++;
+               /* If this word wasn't an assignment, next ones definitely
+                * can't be assignments. Even if they look like ones. */
+               if (word->o_assignment != DEFINITELY_ASSIGNMENT
+                && word->o_assignment != WORD_IS_KEYWORD
+               ) {
+                       word->o_assignment = NOT_ASSIGNMENT;
+               } else {
+                       if (word->o_assignment == DEFINITELY_ASSIGNMENT) {
+                               command->assignment_cnt++;
+                               debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt);
+                       }
+                       debug_printf_parse("word->o_assignment was:'%s'\n", assignment_flag[word->o_assignment]);
+                       word->o_assignment = MAYBE_ASSIGNMENT;
+               }
+               debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]);
+
+               if (word->has_quoted_part
+                /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */
+                && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL)
+                /* (otherwise it's known to be not empty and is already safe) */
+               ) {
+                       /* exclude "$@" - it can expand to no word despite "" */
+                       char *p = word->data;
+                       while (p[0] == SPECIAL_VAR_SYMBOL
+                           && (p[1] & 0x7f) == '@'
+                           && p[2] == SPECIAL_VAR_SYMBOL
+                       ) {
+                               p += 3;
+                       }
+               }
+               command->argv = add_string_to_strings(command->argv, xstrdup(word->data));
+               debug_print_strings("word appended to argv", command->argv);
        }
-       debug_print_list("expand_cond", &output, n);
 
-       /* output.data (malloced in one block) gets returned in "list" */
-       list = o_finalize_list(&output, n);
-       debug_print_strings("expand_cond[1]", list);
-       return list;
-}
+#if ENABLE_HUSH_LOOPS
+       if (ctx->ctx_res_w == RES_FOR) {
+               if (word->has_quoted_part
+                || !is_well_formed_var_name(command->argv[0], '\0')
+               ) {
+                       /* bash says just "not a valid identifier" */
+                       syntax_error("not a valid identifier in for");
+                       return 1;
+               }
+               /* Force FOR to have just one word (variable name) */
+               /* NB: basically, this makes hush see "for v in ..."
+                * syntax as if it is "for v; in ...". FOR and IN become
+                * two pipe structs in parse tree. */
+               done_pipe(ctx, PIPE_SEQ);
+       }
+#endif
+#if ENABLE_HUSH_CASE
+       /* Force CASE to have just one word */
+       if (ctx->ctx_res_w == RES_CASE) {
+               done_pipe(ctx, PIPE_SEQ);
+       }
 #endif
 
-/* Used for expansion of right hand of assignments */
-/* NB: should NOT do globbing! "export v=/bin/c*; env | grep ^v=" outputs
- * "v=/bin/c*" */
-static char *expand_string_to_string(const char *str)
-{
-       char *argv[2], **list;
+       o_reset_to_empty_unquoted(word);
 
-       argv[0] = (char*)str;
-       argv[1] = NULL;
-       list = expand_variables(argv, 0x80); /* 0x80: singleword expansion */
-       if (HUSH_DEBUG)
-               if (!list[0] || list[1])
-                       bb_error_msg_and_die("BUG in varexp2");
-       /* actually, just move string 2*sizeof(char*) bytes back */
-       overlapping_strcpy((char*)list, list[0]);
-       unbackslash((char*)list);
-       debug_printf_expand("string_to_string='%s'\n", (char*)list);
-       return (char*)list;
+       debug_printf_parse("done_word return 0\n");
+       return 0;
 }
 
-/* Used for "eval" builtin */
-static char* expand_strvec_to_string(char **argv)
+
+/* Peek ahead in the input to find out if we have a "&n" construct,
+ * as in "2>&1", that represents duplicating a file descriptor.
+ * Return:
+ * REDIRFD_CLOSE if >&- "close fd" construct is seen,
+ * REDIRFD_SYNTAX_ERR if syntax error,
+ * REDIRFD_TO_FILE if no & was seen,
+ * or the number found.
+ */
+#if BB_MMU
+#define parse_redir_right_fd(as_string, input) \
+       parse_redir_right_fd(input)
+#endif
+static int parse_redir_right_fd(o_string *as_string, struct in_str *input)
 {
-       char **list;
+       int ch, d, ok;
 
-       list = expand_variables(argv, 0x80);
-       /* Convert all NULs to spaces */
-       if (list[0]) {
-               int n = 1;
-               while (list[n]) {
-                       if (HUSH_DEBUG)
-                               if (list[n-1] + strlen(list[n-1]) + 1 != list[n])
-                                       bb_error_msg_and_die("BUG in varexp3");
-                       /* bash uses ' ' regardless of $IFS contents */
-                       list[n][-1] = ' ';
-                       n++;
-               }
-       }
-       overlapping_strcpy((char*)list, list[0]);
-       debug_printf_expand("strvec_to_string='%s'\n", (char*)list);
-       return (char*)list;
-}
+       ch = i_peek(input);
+       if (ch != '&')
+               return REDIRFD_TO_FILE;
 
-static char **expand_assignments(char **argv, int count)
-{
-       int i;
-       char **p = NULL;
-       /* Expand assignments into one string each */
-       for (i = 0; i < count; i++) {
-               p = add_string_to_strings(p, expand_string_to_string(argv[i]));
+       ch = i_getch(input);  /* get the & */
+       nommu_addchr(as_string, ch);
+       ch = i_peek(input);
+       if (ch == '-') {
+               ch = i_getch(input);
+               nommu_addchr(as_string, ch);
+               return REDIRFD_CLOSE;
        }
-       return p;
-}
+       d = 0;
+       ok = 0;
+       while (ch != EOF && isdigit(ch)) {
+               d = d*10 + (ch-'0');
+               ok = 1;
+               ch = i_getch(input);
+               nommu_addchr(as_string, ch);
+               ch = i_peek(input);
+       }
+       if (ok) return d;
 
+//TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2)
 
-#if BB_MMU
-/* never called */
-void re_execute_shell(char ***to_free, const char *s,
-               char *g_argv0, char **g_argv,
-               char **builtin_argv) NORETURN;
+       bb_error_msg("ambiguous redirect");
+       return REDIRFD_SYNTAX_ERR;
+}
 
-static void reset_traps_to_defaults(void)
+/* Return code is 0 normal, 1 if a syntax error is detected
+ */
+static int parse_redirect(struct parse_context *ctx,
+               int fd,
+               redir_type style,
+               struct in_str *input)
 {
-       /* This function is always called in a child shell
-        * after fork (not vfork, NOMMU doesn't use this function).
-        */
-       unsigned sig;
-       unsigned mask;
+       struct command *command = ctx->command;
+       struct redir_struct *redir;
+       struct redir_struct **redirp;
+       int dup_num;
 
-       /* Child shells are not interactive.
-        * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling.
-        * Testcase: (while :; do :; done) + ^Z should background.
-        * Same goes for SIGTERM, SIGHUP, SIGINT.
-        */
-       if (!G.traps && !(G.non_DFL_mask & SPECIAL_INTERACTIVE_SIGS))
-               return; /* already no traps and no SPECIAL_INTERACTIVE_SIGS */
-
-       /* Switching off SPECIAL_INTERACTIVE_SIGS.
-        * Stupid. It can be done with *single* &= op, but we can't use
-        * the fact that G.blocked_set is implemented as a bitmask
-        * in libc... */
-       mask = (SPECIAL_INTERACTIVE_SIGS >> 1);
-       sig = 1;
-       while (1) {
-               if (mask & 1) {
-                       /* Careful. Only if no trap or trap is not "" */
-                       if (!G.traps || !G.traps[sig] || G.traps[sig][0])
-                               sigdelset(&G.blocked_set, sig);
+       dup_num = REDIRFD_TO_FILE;
+       if (style != REDIRECT_HEREDOC) {
+               /* Check for a '>&1' type redirect */
+               dup_num = parse_redir_right_fd(&ctx->as_string, input);
+               if (dup_num == REDIRFD_SYNTAX_ERR)
+                       return 1;
+       } else {
+               int ch = i_peek(input);
+               dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */
+               if (dup_num) { /* <<-... */
+                       ch = i_getch(input);
+                       nommu_addchr(&ctx->as_string, ch);
+                       ch = i_peek(input);
                }
-               mask >>= 1;
-               if (!mask)
-                       break;
-               sig++;
-       }
-       /* Our homegrown sig mask is saner to work with :) */
-       G.non_DFL_mask &= ~SPECIAL_INTERACTIVE_SIGS;
-
-       /* Resetting all traps to default except empty ones */
-       mask = G.non_DFL_mask;
-       if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) {
-               if (!G.traps[sig] || !G.traps[sig][0])
-                       continue;
-               free(G.traps[sig]);
-               G.traps[sig] = NULL;
-               /* There is no signal for 0 (EXIT) */
-               if (sig == 0)
-                       continue;
-               /* There was a trap handler, we just removed it.
-                * But if sig still has non-DFL handling,
-                * we should not unblock the sig. */
-               if (mask & 1)
-                       continue;
-               sigdelset(&G.blocked_set, sig);
        }
-       sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
-}
-
-#else /* !BB_MMU */
 
-static void re_execute_shell(char ***to_free, const char *s,
-               char *g_argv0, char **g_argv,
-               char **builtin_argv) NORETURN;
-static void re_execute_shell(char ***to_free, const char *s,
-               char *g_argv0, char **g_argv,
-               char **builtin_argv)
-{
-# define NOMMU_HACK_FMT ("-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x"))
-       /* delims + 2 * (number of bytes in printed hex numbers) */
-       char param_buf[sizeof(NOMMU_HACK_FMT) + 2 * (sizeof(int)*6 + sizeof(long long)*1)];
-       char *heredoc_argv[4];
-       struct variable *cur;
-# if ENABLE_HUSH_FUNCTIONS
-       struct function *funcp;
-# endif
-       char **argv, **pp;
-       unsigned cnt;
-       unsigned long long empty_trap_mask;
+       if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
+               int ch = i_peek(input);
+               if (ch == '|') {
+                       /* >|FILE redirect ("clobbering" >).
+                        * Since we do not support "set -o noclobber" yet,
+                        * >| and > are the same for now. Just eat |.
+                        */
+                       ch = i_getch(input);
+                       nommu_addchr(&ctx->as_string, ch);
+               }
+       }
 
-       if (!g_argv0) { /* heredoc */
-               argv = heredoc_argv;
-               argv[0] = (char *) G.argv0_for_re_execing;
-               argv[1] = (char *) "-<";
-               argv[2] = (char *) s;
-               argv[3] = NULL;
-               pp = &argv[3]; /* used as pointer to empty environment */
-               goto do_exec;
+       /* Create a new redir_struct and append it to the linked list */
+       redirp = &command->redirects;
+       while ((redir = *redirp) != NULL) {
+               redirp = &(redir->next);
        }
+       *redirp = redir = xzalloc(sizeof(*redir));
+       /* redir->next = NULL; */
+       /* redir->rd_filename = NULL; */
+       redir->rd_type = style;
+       redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd;
 
-       cnt = 0;
-       pp = builtin_argv;
-       if (pp) while (*pp++)
-               cnt++;
+       debug_printf_parse("redirect type %d %s\n", redir->rd_fd,
+                               redir_table[style].descrip);
 
-       empty_trap_mask = 0;
-       if (G.traps) {
-               int sig;
-               for (sig = 1; sig < NSIG; sig++) {
-                       if (G.traps[sig] && !G.traps[sig][0])
-                               empty_trap_mask |= 1LL << sig;
-               }
+       redir->rd_dup = dup_num;
+       if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) {
+               /* Erik had a check here that the file descriptor in question
+                * is legit; I postpone that to "run time"
+                * A "-" representation of "close me" shows up as a -3 here */
+               debug_printf_parse("duplicating redirect '%d>&%d'\n",
+                               redir->rd_fd, redir->rd_dup);
+       } else {
+               /* Set ctx->pending_redirect, so we know what to do at the
+                * end of the next parsed word. */
+               ctx->pending_redirect = redir;
        }
+       return 0;
+}
 
-       sprintf(param_buf, NOMMU_HACK_FMT
-                       , (unsigned) G.root_pid
-                       , (unsigned) G.root_ppid
-                       , (unsigned) G.last_bg_pid
-                       , (unsigned) G.last_exitcode
-                       , cnt
-                       , empty_trap_mask
-                       IF_HUSH_LOOPS(, G.depth_of_loop)
-                       );
-# undef NOMMU_HACK_FMT
-       /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<etc...> <vars...> <funcs...>
-        * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL
-        */
-       cnt += 6;
-       for (cur = G.top_var; cur; cur = cur->next) {
-               if (!cur->flg_export || cur->flg_read_only)
-                       cnt += 2;
-       }
-# if ENABLE_HUSH_FUNCTIONS
-       for (funcp = G.top_func; funcp; funcp = funcp->next)
-               cnt += 3;
-# endif
-       pp = g_argv;
-       while (*pp++)
-               cnt++;
-       *to_free = argv = pp = xzalloc(sizeof(argv[0]) * cnt);
-       *pp++ = (char *) G.argv0_for_re_execing;
-       *pp++ = param_buf;
-       for (cur = G.top_var; cur; cur = cur->next) {
-               if (cur->varstr == hush_version_str)
-                       continue;
-               if (cur->flg_read_only) {
-                       *pp++ = (char *) "-R";
-                       *pp++ = cur->varstr;
-               } else if (!cur->flg_export) {
-                       *pp++ = (char *) "-V";
-                       *pp++ = cur->varstr;
-               }
-       }
-# if ENABLE_HUSH_FUNCTIONS
-       for (funcp = G.top_func; funcp; funcp = funcp->next) {
-               *pp++ = (char *) "-F";
-               *pp++ = funcp->name;
-               *pp++ = funcp->body_as_string;
-       }
-# endif
-       /* We can pass activated traps here. Say, -Tnn:trap_string
-        *
-        * However, POSIX says that subshells reset signals with traps
-        * to SIG_DFL.
-        * I tested bash-3.2 and it not only does that with true subshells
-        * of the form ( list ), but with any forked children shells.
-        * I set trap "echo W" WINCH; and then tried:
-        *
-        * { echo 1; sleep 20; echo 2; } &
-        * while true; do echo 1; sleep 20; echo 2; break; done &
-        * true | { echo 1; sleep 20; echo 2; } | cat
-        *
-        * In all these cases sending SIGWINCH to the child shell
-        * did not run the trap. If I add trap "echo V" WINCH;
-        * _inside_ group (just before echo 1), it works.
-        *
-        * I conclude it means we don't need to pass active traps here.
-        * Even if we would use signal handlers instead of signal masking
-        * in order to implement trap handling,
-        * exec syscall below resets signals to SIG_DFL for us.
-        */
-       *pp++ = (char *) "-c";
-       *pp++ = (char *) s;
-       if (builtin_argv) {
-               while (*++builtin_argv)
-                       *pp++ = *builtin_argv;
-               *pp++ = (char *) "";
-       }
-       *pp++ = g_argv0;
-       while (*g_argv)
-               *pp++ = *g_argv++;
-       /* *pp = NULL; - is already there */
-       pp = environ;
+/* If a redirect is immediately preceded by a number, that number is
+ * supposed to tell which file descriptor to redirect.  This routine
+ * looks for such preceding numbers.  In an ideal world this routine
+ * needs to handle all the following classes of redirects...
+ *     echo 2>foo     # redirects fd  2 to file "foo", nothing passed to echo
+ *     echo 49>foo    # redirects fd 49 to file "foo", nothing passed to echo
+ *     echo -2>foo    # redirects fd  1 to file "foo",    "-2" passed to echo
+ *     echo 49x>foo   # redirects fd  1 to file "foo",   "49x" passed to echo
+ *
+ * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
+ * "2.7 Redirection
+ * ... If n is quoted, the number shall not be recognized as part of
+ * the redirection expression. For example:
+ * echo \2>a
+ * writes the character 2 into file a"
+ * We are getting it right by setting ->has_quoted_part on any \<char>
+ *
+ * A -1 return means no valid number was found,
+ * the caller should use the appropriate default for this redirection.
+ */
+static int redirect_opt_num(o_string *o)
+{
+       int num;
 
- do_exec:
-       debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s);
-       sigprocmask(SIG_SETMASK, &G.inherited_set, NULL);
-       execve(bb_busybox_exec_path, argv, pp);
-       /* Fallback. Useful for init=/bin/hush usage etc */
-       if (argv[0][0] == '/')
-               execve(argv[0], argv, pp);
-       xfunc_error_retval = 127;
-       bb_error_msg_and_die("can't re-execute the shell");
+       if (o->data == NULL)
+               return -1;
+       num = bb_strtou(o->data, NULL, 10);
+       if (errno || num < 0)
+               return -1;
+       o_reset_to_empty_unquoted(o);
+       return num;
 }
-#endif  /* !BB_MMU */
-
 
-static void setup_heredoc(struct redir_struct *redir)
-{
-       struct fd_pair pair;
-       pid_t pid;
-       int len, written;
-       /* the _body_ of heredoc (misleading field name) */
-       const char *heredoc = redir->rd_filename;
-       char *expanded;
-#if !BB_MMU
-       char **to_free;
+#if BB_MMU
+#define fetch_till_str(as_string, input, word, skip_tabs) \
+       fetch_till_str(input, word, skip_tabs)
 #endif
+static char *fetch_till_str(o_string *as_string,
+               struct in_str *input,
+               const char *word,
+               int heredoc_flags)
+{
+       o_string heredoc = NULL_O_STRING;
+       unsigned past_EOL;
+       int prev = 0; /* not \ */
+       int ch;
 
-       expanded = NULL;
-       if (!(redir->rd_dup & HEREDOC_QUOTED)) {
-               expanded = expand_pseudo_dquoted(heredoc);
-               if (expanded)
-                       heredoc = expanded;
-       }
-       len = strlen(heredoc);
-
-       close(redir->rd_fd); /* often saves dup2+close in xmove_fd */
-       xpiped_pair(pair);
-       xmove_fd(pair.rd, redir->rd_fd);
+       goto jump_in;
 
-       /* Try writing without forking. Newer kernels have
-        * dynamically growing pipes. Must use non-blocking write! */
-       ndelay_on(pair.wr);
        while (1) {
-               written = write(pair.wr, heredoc, len);
-               if (written <= 0)
-                       break;
-               len -= written;
-               if (len == 0) {
-                       close(pair.wr);
-                       free(expanded);
-                       return;
+               ch = i_getch(input);
+               if (ch != EOF)
+                       nommu_addchr(as_string, ch);
+               if ((ch == '\n' || ch == EOF)
+                && ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\')
+               ) {
+                       if (strcmp(heredoc.data + past_EOL, word) == 0) {
+                               heredoc.data[past_EOL] = '\0';
+                               debug_printf_parse("parsed heredoc '%s'\n", heredoc.data);
+                               return heredoc.data;
+                       }
+                       while (ch == '\n') {
+                               o_addchr(&heredoc, ch);
+                               prev = ch;
+ jump_in:
+                               past_EOL = heredoc.length;
+                               do {
+                                       ch = i_getch(input);
+                                       if (ch != EOF)
+                                               nommu_addchr(as_string, ch);
+                               } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t');
+                       }
                }
-               heredoc += written;
+               if (ch == EOF) {
+                       o_free_unsafe(&heredoc);
+                       return NULL;
+               }
+               o_addchr(&heredoc, ch);
+               nommu_addchr(as_string, ch);
+               if (prev == '\\' && ch == '\\')
+                       /* Correctly handle foo\\<eol> (not a line cont.) */
+                       prev = 0; /* not \ */
+               else
+                       prev = ch;
        }
-       ndelay_off(pair.wr);
+}
 
-       /* Okay, pipe buffer was not big enough */
-       /* Note: we must not create a stray child (bastard? :)
-        * for the unsuspecting parent process. Child creates a grandchild
-        * and exits before parent execs the process which consumes heredoc
-        * (that exec happens after we return from this function) */
-#if !BB_MMU
-       to_free = NULL;
-#endif
-       pid = xvfork();
-       if (pid == 0) {
-               /* child */
-               disable_restore_tty_pgrp_on_exit();
-               pid = BB_MMU ? xfork() : xvfork();
-               if (pid != 0)
-                       _exit(0);
-               /* grandchild */
-               close(redir->rd_fd); /* read side of the pipe */
-#if BB_MMU
-               full_write(pair.wr, heredoc, len); /* may loop or block */
-               _exit(0);
-#else
-               /* Delegate blocking writes to another process */
-               xmove_fd(pair.wr, STDOUT_FILENO);
-               re_execute_shell(&to_free, heredoc, NULL, NULL, NULL);
-#endif
-       }
-       /* parent */
-#if ENABLE_HUSH_FAST
-       G.count_SIGCHLD++;
-//bb_error_msg("[%d] fork in setup_heredoc: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
-#endif
-       enable_restore_tty_pgrp_on_exit();
-#if !BB_MMU
-       free(to_free);
-#endif
-       close(pair.wr);
-       free(expanded);
-       wait(NULL); /* wait till child has died */
-}
-
-/* squirrel != NULL means we squirrel away copies of stdin, stdout,
- * and stderr if they are redirected. */
-static int setup_redirects(struct command *prog, int squirrel[])
-{
-       int openfd, mode;
-       struct redir_struct *redir;
-
-       for (redir = prog->redirects; redir; redir = redir->next) {
-               if (redir->rd_type == REDIRECT_HEREDOC2) {
-                       /* rd_fd<<HERE case */
-                       if (squirrel && redir->rd_fd < 3
-                        && squirrel[redir->rd_fd] < 0
-                       ) {
-                               squirrel[redir->rd_fd] = dup(redir->rd_fd);
-                       }
-                       /* for REDIRECT_HEREDOC2, rd_filename holds _contents_
-                        * of the heredoc */
-                       debug_printf_parse("set heredoc '%s'\n",
-                                       redir->rd_filename);
-                       setup_heredoc(redir);
-                       continue;
-               }
-
-               if (redir->rd_dup == REDIRFD_TO_FILE) {
-                       /* rd_fd<*>file case (<*> is <,>,>>,<>) */
-                       char *p;
-                       if (redir->rd_filename == NULL) {
-                               /* Something went wrong in the parse.
-                                * Pretend it didn't happen */
-                               bb_error_msg("bug in redirect parse");
-                               continue;
-                       }
-                       mode = redir_table[redir->rd_type].mode;
-                       p = expand_string_to_string(redir->rd_filename);
-                       openfd = open_or_warn(p, mode);
-                       free(p);
-                       if (openfd < 0) {
-                       /* this could get lost if stderr has been redirected, but
-                        * bash and ash both lose it as well (though zsh doesn't!) */
-//what the above comment tries to say?
-                               return 1;
-                       }
-               } else {
-                       /* rd_fd<*>rd_dup or rd_fd<*>- cases */
-                       openfd = redir->rd_dup;
-               }
-
-               if (openfd != redir->rd_fd) {
-                       if (squirrel && redir->rd_fd < 3
-                        && squirrel[redir->rd_fd] < 0
-                       ) {
-                               squirrel[redir->rd_fd] = dup(redir->rd_fd);
-                       }
-                       if (openfd == REDIRFD_CLOSE) {
-                               /* "n>-" means "close me" */
-                               close(redir->rd_fd);
-                       } else {
-                               xdup2(openfd, redir->rd_fd);
-                               if (redir->rd_dup == REDIRFD_TO_FILE)
-                                       close(openfd);
-                       }
-               }
-       }
-       return 0;
-}
-
-static void restore_redirects(int squirrel[])
+/* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs
+ * and load them all. There should be exactly heredoc_cnt of them.
+ */
+static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_str *input)
 {
-       int i, fd;
-       for (i = 0; i < 3; i++) {
-               fd = squirrel[i];
-               if (fd != -1) {
-                       /* We simply die on error */
-                       xmove_fd(fd, i);
-               }
-       }
-}
+       struct pipe *pi = ctx->list_head;
 
+       while (pi && heredoc_cnt) {
+               int i;
+               struct command *cmd = pi->cmds;
 
-static void free_pipe_list(struct pipe *head);
+               debug_printf_parse("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n",
+                               pi->num_cmds,
+                               cmd->argv ? cmd->argv[0] : "NONE");
+               for (i = 0; i < pi->num_cmds; i++) {
+                       struct redir_struct *redir = cmd->redirects;
 
-/* Return code is the exit status of the pipe */
-static void free_pipe(struct pipe *pi)
-{
-       char **p;
-       struct command *command;
-       struct redir_struct *r, *rnext;
-       int a, i;
+                       debug_printf_parse("fetch_heredocs: %d cmd argv0:'%s'\n",
+                                       i, cmd->argv ? cmd->argv[0] : "NONE");
+                       while (redir) {
+                               if (redir->rd_type == REDIRECT_HEREDOC) {
+                                       char *p;
 
-       if (pi->stopped_cmds > 0) /* why? */
-               return;
-       debug_printf_clean("run pipe: (pid %d)\n", getpid());
-       for (i = 0; i < pi->num_cmds; i++) {
-               command = &pi->cmds[i];
-               debug_printf_clean("  command %d:\n", i);
-               if (command->argv) {
-                       for (a = 0, p = command->argv; *p; a++, p++) {
-                               debug_printf_clean("   argv[%d] = %s\n", a, *p);
-                       }
-                       free_strings(command->argv);
-                       command->argv = NULL;
-               }
-               /* not "else if": on syntax error, we may have both! */
-               if (command->group) {
-                       debug_printf_clean("   begin group (cmd_type:%d)\n",
-                                       command->cmd_type);
-                       free_pipe_list(command->group);
-                       debug_printf_clean("   end group\n");
-                       command->group = NULL;
-               }
-               /* else is crucial here.
-                * If group != NULL, child_func is meaningless */
-#if ENABLE_HUSH_FUNCTIONS
-               else if (command->child_func) {
-                       debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func);
-                       command->child_func->parent_cmd = NULL;
-               }
-#endif
-#if !BB_MMU
-               free(command->group_as_string);
-               command->group_as_string = NULL;
-#endif
-               for (r = command->redirects; r; r = rnext) {
-                       debug_printf_clean("   redirect %d%s",
-                                       r->rd_fd, redir_table[r->rd_type].descrip);
-                       /* guard against the case >$FOO, where foo is unset or blank */
-                       if (r->rd_filename) {
-                               debug_printf_clean(" fname:'%s'\n", r->rd_filename);
-                               free(r->rd_filename);
-                               r->rd_filename = NULL;
+                                       redir->rd_type = REDIRECT_HEREDOC2;
+                                       /* redir->rd_dup is (ab)used to indicate <<- */
+                                       p = fetch_till_str(&ctx->as_string, input,
+                                                       redir->rd_filename, redir->rd_dup);
+                                       if (!p) {
+                                               syntax_error("unexpected EOF in here document");
+                                               return 1;
+                                       }
+                                       free(redir->rd_filename);
+                                       redir->rd_filename = p;
+                                       heredoc_cnt--;
+                               }
+                               redir = redir->next;
                        }
-                       debug_printf_clean(" rd_dup:%d\n", r->rd_dup);
-                       rnext = r->next;
-                       free(r);
+                       cmd++;
                }
-               command->redirects = NULL;
+               pi = pi->next;
        }
-       free(pi->cmds);   /* children are an array, they get freed all at once */
-       pi->cmds = NULL;
-#if ENABLE_HUSH_JOB
-       free(pi->cmdtext);
-       pi->cmdtext = NULL;
-#endif
-}
-
-static void free_pipe_list(struct pipe *head)
-{
-       struct pipe *pi, *next;
-
-       for (pi = head; pi; pi = next) {
-#if HAS_KEYWORDS
-               debug_printf_clean(" pipe reserved word %d\n", pi->res_word);
+#if 0
+       /* Should be 0. If it isn't, it's a parse error */
+       if (heredoc_cnt)
+               bb_error_msg_and_die("heredoc BUG 2");
 #endif
-               free_pipe(pi);
-               debug_printf_clean("pipe followup code %d\n", pi->followup);
-               next = pi->next;
-               /*pi->next = NULL;*/
-               free(pi);
-       }
+       return 0;
 }
 
 
@@ -3414,3518 +3569,4165 @@ static int run_list(struct pipe *pi);
 static struct pipe *parse_stream(char **pstring,
                struct in_str *input,
                int end_trigger);
-static void parse_and_run_string(const char *s);
 
 
-static char *find_in_path(const char *arg)
+#if !ENABLE_HUSH_FUNCTIONS
+#define parse_group(dest, ctx, input, ch) \
+       parse_group(ctx, input, ch)
+#endif
+static int parse_group(o_string *dest, struct parse_context *ctx,
+       struct in_str *input, int ch)
 {
-       char *ret = NULL;
-       const char *PATH = get_local_var_value("PATH");
-
-       if (!PATH)
-               return NULL;
-
-       while (1) {
-               const char *end = strchrnul(PATH, ':');
-               int sz = end - PATH; /* must be int! */
+       /* dest contains characters seen prior to ( or {.
+        * Typically it's empty, but for function defs,
+        * it contains function name (without '()'). */
+       struct pipe *pipe_list;
+       int endch;
+       struct command *command = ctx->command;
 
-               free(ret);
-               if (sz != 0) {
-                       ret = xasprintf("%.*s/%s", sz, PATH, arg);
-               } else {
-                       /* We have xxx::yyyy in $PATH,
-                        * it means "use current dir" */
-                       ret = xstrdup(arg);
+       debug_printf_parse("parse_group entered\n");
+#if ENABLE_HUSH_FUNCTIONS
+       if (ch == '(' && !dest->has_quoted_part) {
+               if (dest->length)
+                       if (done_word(dest, ctx))
+                               return 1;
+               if (!command->argv)
+                       goto skip; /* (... */
+               if (command->argv[1]) { /* word word ... (... */
+                       syntax_error_unexpected_ch('(');
+                       return 1;
                }
-               if (access(ret, F_OK) == 0)
-                       break;
-
-               if (*end == '\0') {
-                       free(ret);
-                       return NULL;
+               /* it is "word(..." or "word (..." */
+               do
+                       ch = i_getch(input);
+               while (ch == ' ' || ch == '\t');
+               if (ch != ')') {
+                       syntax_error_unexpected_ch(ch);
+                       return 1;
                }
-               PATH = end + 1;
-       }
-
-       return ret;
-}
-
-static const struct built_in_command* find_builtin_helper(const char *name,
-               const struct built_in_command *x,
-               const struct built_in_command *end)
-{
-       while (x != end) {
-               if (strcmp(name, x->b_cmd) != 0) {
-                       x++;
-                       continue;
+               nommu_addchr(&ctx->as_string, ch);
+               do
+                       ch = i_getch(input);
+               while (ch == ' ' || ch == '\t' || ch == '\n');
+               if (ch != '{') {
+                       syntax_error_unexpected_ch(ch);
+                       return 1;
                }
-               debug_printf_exec("found builtin '%s'\n", name);
-               return x;
+               nommu_addchr(&ctx->as_string, ch);
+               command->cmd_type = CMD_FUNCDEF;
+               goto skip;
        }
-       return NULL;
-}
-static const struct built_in_command* find_builtin1(const char *name)
-{
-       return find_builtin_helper(name, bltins1, &bltins1[ARRAY_SIZE(bltins1)]);
-}
-static const struct built_in_command* find_builtin(const char *name)
-{
-       const struct built_in_command *x = find_builtin1(name);
-       if (x)
-               return x;
-       return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
-}
+#endif
+
+#if 0 /* Prevented by caller */
+       if (command->argv /* word [word]{... */
+        || dest->length /* word{... */
+        || dest->has_quoted_part /* ""{... */
+       ) {
+               syntax_error(NULL);
+               debug_printf_parse("parse_group return 1: "
+                       "syntax error, groups and arglists don't mix\n");
+               return 1;
+       }
+#endif
 
 #if ENABLE_HUSH_FUNCTIONS
-static struct function **find_function_slot(const char *name)
-{
-       struct function **funcpp = &G.top_func;
-       while (*funcpp) {
-               if (strcmp(name, (*funcpp)->name) == 0) {
-                       break;
+ skip:
+#endif
+       endch = '}';
+       if (ch == '(') {
+               endch = ')';
+               command->cmd_type = CMD_SUBSHELL;
+       } else {
+               /* bash does not allow "{echo...", requires whitespace */
+               ch = i_getch(input);
+               if (ch != ' ' && ch != '\t' && ch != '\n') {
+                       syntax_error_unexpected_ch(ch);
+                       return 1;
                }
-               funcpp = &(*funcpp)->next;
+               nommu_addchr(&ctx->as_string, ch);
        }
-       return funcpp;
+
+       {
+#if BB_MMU
+# define as_string NULL
+#else
+               char *as_string = NULL;
+#endif
+               pipe_list = parse_stream(&as_string, input, endch);
+#if !BB_MMU
+               if (as_string)
+                       o_addstr(&ctx->as_string, as_string);
+#endif
+               /* empty ()/{} or parse error? */
+               if (!pipe_list || pipe_list == ERR_PTR) {
+                       /* parse_stream already emitted error msg */
+                       if (!BB_MMU)
+                               free(as_string);
+                       debug_printf_parse("parse_group return 1: "
+                               "parse_stream returned %p\n", pipe_list);
+                       return 1;
+               }
+               command->group = pipe_list;
+#if !BB_MMU
+               as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */
+               command->group_as_string = as_string;
+               debug_printf_parse("end of group, remembering as:'%s'\n",
+                               command->group_as_string);
+#endif
+#undef as_string
+       }
+       debug_printf_parse("parse_group return 0\n");
+       return 0;
+       /* command remains "open", available for possible redirects */
 }
 
-static const struct function *find_function(const char *name)
+#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS
+/* Subroutines for copying $(...) and `...` things */
+static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
+/* '...' */
+static int add_till_single_quote(o_string *dest, struct in_str *input)
 {
-       const struct function *funcp = *find_function_slot(name);
-       if (funcp)
-               debug_printf_exec("found function '%s'\n", name);
-       return funcp;
+       while (1) {
+               int ch = i_getch(input);
+               if (ch == EOF) {
+                       syntax_error_unterm_ch('\'');
+                       return 0;
+               }
+               if (ch == '\'')
+                       return 1;
+               o_addchr(dest, ch);
+       }
 }
-
-/* Note: takes ownership on name ptr */
-static struct function *new_function(char *name)
+/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
+static int add_till_double_quote(o_string *dest, struct in_str *input)
 {
-       struct function **funcpp = find_function_slot(name);
-       struct function *funcp = *funcpp;
-
-       if (funcp != NULL) {
-               struct command *cmd = funcp->parent_cmd;
-               debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd);
-               if (!cmd) {
-                       debug_printf_exec("freeing & replacing function '%s'\n", funcp->name);
-                       free(funcp->name);
-                       /* Note: if !funcp->body, do not free body_as_string!
-                        * This is a special case of "-F name body" function:
-                        * body_as_string was not malloced! */
-                       if (funcp->body) {
-                               free_pipe_list(funcp->body);
-# if !BB_MMU
-                               free(funcp->body_as_string);
-# endif
-                       }
-               } else {
-                       debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name);
-                       cmd->argv[0] = funcp->name;
-                       cmd->group = funcp->body;
-# if !BB_MMU
-                       cmd->group_as_string = funcp->body_as_string;
-# endif
+       while (1) {
+               int ch = i_getch(input);
+               if (ch == EOF) {
+                       syntax_error_unterm_ch('"');
+                       return 0;
                }
-       } else {
-               debug_printf_exec("remembering new function '%s'\n", name);
-               funcp = *funcpp = xzalloc(sizeof(*funcp));
-               /*funcp->next = NULL;*/
+               if (ch == '"')
+                       return 1;
+               if (ch == '\\') {  /* \x. Copy both chars. */
+                       o_addchr(dest, ch);
+                       ch = i_getch(input);
+               }
+               o_addchr(dest, ch);
+               if (ch == '`') {
+                       if (!add_till_backquote(dest, input, /*in_dquote:*/ 1))
+                               return 0;
+                       o_addchr(dest, ch);
+                       continue;
+               }
+               //if (ch == '$') ...
        }
-
-       funcp->name = name;
-       return funcp;
 }
-
-static void unset_func(const char *name)
+/* Process `cmd` - copy contents until "`" is seen. Complicated by
+ * \` quoting.
+ * "Within the backquoted style of command substitution, backslash
+ * shall retain its literal meaning, except when followed by: '$', '`', or '\'.
+ * The search for the matching backquote shall be satisfied by the first
+ * backquote found without a preceding backslash; during this search,
+ * if a non-escaped backquote is encountered within a shell comment,
+ * a here-document, an embedded command substitution of the $(command)
+ * form, or a quoted string, undefined results occur. A single-quoted
+ * or double-quoted string that begins, but does not end, within the
+ * "`...`" sequence produces undefined results."
+ * Example                               Output
+ * echo `echo '\'TEST\`echo ZZ\`BEST`    \TESTZZBEST
+ */
+static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote)
 {
-       struct function **funcpp = find_function_slot(name);
-       struct function *funcp = *funcpp;
-
-       if (funcp != NULL) {
-               debug_printf_exec("freeing function '%s'\n", funcp->name);
-               *funcpp = funcp->next;
-               /* funcp is unlinked now, deleting it.
-                * Note: if !funcp->body, the function was created by
-                * "-F name body", do not free ->body_as_string
-                * and ->name as they were not malloced. */
-               if (funcp->body) {
-                       free_pipe_list(funcp->body);
-                       free(funcp->name);
-# if !BB_MMU
-                       free(funcp->body_as_string);
-# endif
+       while (1) {
+               int ch = i_getch(input);
+               if (ch == '`')
+                       return 1;
+               if (ch == '\\') {
+                       /* \x. Copy both unless it is \`, \$, \\ and maybe \" */
+                       ch = i_getch(input);
+                       if (ch != '`'
+                        && ch != '$'
+                        && ch != '\\'
+                        && (!in_dquote || ch != '"')
+                       ) {
+                               o_addchr(dest, '\\');
+                       }
                }
-               free(funcp);
+               if (ch == EOF) {
+                       syntax_error_unterm_ch('`');
+                       return 0;
+               }
+               o_addchr(dest, ch);
        }
 }
-
-# if BB_MMU
-#define exec_function(to_free, funcp, argv) \
-       exec_function(funcp, argv)
-# endif
-static void exec_function(char ***to_free,
-               const struct function *funcp,
-               char **argv) NORETURN;
-static void exec_function(char ***to_free,
-               const struct function *funcp,
-               char **argv)
+/* Process $(cmd) - copy contents until ")" is seen. Complicated by
+ * quoting and nested ()s.
+ * "With the $(command) style of command substitution, all characters
+ * following the open parenthesis to the matching closing parenthesis
+ * constitute the command. Any valid shell script can be used for command,
+ * except a script consisting solely of redirections which produces
+ * unspecified results."
+ * Example                              Output
+ * echo $(echo '(TEST)' BEST)           (TEST) BEST
+ * echo $(echo 'TEST)' BEST)            TEST) BEST
+ * echo $(echo \(\(TEST\) BEST)         ((TEST) BEST
+ *
+ * Also adapted to eat ${var%...} and $((...)) constructs, since ... part
+ * can contain arbitrary constructs, just like $(cmd).
+ * In bash compat mode, it needs to also be able to stop on ':' or '/'
+ * for ${var:N[:M]} and ${var/P[/R]} parsing.
+ */
+#define DOUBLE_CLOSE_CHAR_FLAG 0x80
+static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch)
 {
-# if BB_MMU
-       int n = 1;
+       int ch;
+       char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
+# if ENABLE_HUSH_BASH_COMPAT
+       char end_char2 = end_ch >> 8;
+# endif
+       end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
 
-       argv[0] = G.global_argv[0];
-       G.global_argv = argv;
-       while (*++argv)
-               n++;
-       G.global_argc = n;
-       /* On MMU, funcp->body is always non-NULL */
-       n = run_list(funcp->body);
-       fflush_all();
-       _exit(n);
-# else
-       re_execute_shell(to_free,
-                       funcp->body_as_string,
-                       G.global_argv[0],
-                       argv + 1,
-                       NULL);
-# endif
-}
-
-static int run_function(const struct function *funcp, char **argv)
-{
-       int rc;
-       save_arg_t sv;
-       smallint sv_flg;
-
-       save_and_replace_G_args(&sv, argv);
-
-       /* "we are in function, ok to use return" */
-       sv_flg = G.flag_return_in_progress;
-       G.flag_return_in_progress = -1;
-# if ENABLE_HUSH_LOCAL
-       G.func_nest_level++;
-# endif
-
-       /* On MMU, funcp->body is always non-NULL */
-# if !BB_MMU
-       if (!funcp->body) {
-               /* Function defined by -F */
-               parse_and_run_string(funcp->body_as_string);
-               rc = G.last_exitcode;
-       } else
-# endif
-       {
-               rc = run_list(funcp->body);
-       }
-
-# if ENABLE_HUSH_LOCAL
-       {
-               struct variable *var;
-               struct variable **var_pp;
-
-               var_pp = &G.top_var;
-               while ((var = *var_pp) != NULL) {
-                       if (var->func_nest_level < G.func_nest_level) {
-                               var_pp = &var->next;
-                               continue;
+       while (1) {
+               ch = i_getch(input);
+               if (ch == EOF) {
+                       syntax_error_unterm_ch(end_ch);
+                       return 0;
+               }
+               if (ch == end_ch  IF_HUSH_BASH_COMPAT( || ch == end_char2)) {
+                       if (!dbl)
+                               break;
+                       /* we look for closing )) of $((EXPR)) */
+                       if (i_peek(input) == end_ch) {
+                               i_getch(input); /* eat second ')' */
+                               break;
                        }
-                       /* Unexport */
-                       if (var->flg_export)
-                               bb_unsetenv(var->varstr);
-                       /* Remove from global list */
-                       *var_pp = var->next;
-                       /* Free */
-                       if (!var->max_len)
-                               free(var->varstr);
-                       free(var);
                }
-               G.func_nest_level--;
+               o_addchr(dest, ch);
+               if (ch == '(' || ch == '{') {
+                       ch = (ch == '(' ? ')' : '}');
+                       if (!add_till_closing_bracket(dest, input, ch))
+                               return 0;
+                       o_addchr(dest, ch);
+                       continue;
+               }
+               if (ch == '\'') {
+                       if (!add_till_single_quote(dest, input))
+                               return 0;
+                       o_addchr(dest, ch);
+                       continue;
+               }
+               if (ch == '"') {
+                       if (!add_till_double_quote(dest, input))
+                               return 0;
+                       o_addchr(dest, ch);
+                       continue;
+               }
+               if (ch == '`') {
+                       if (!add_till_backquote(dest, input, /*in_dquote:*/ 0))
+                               return 0;
+                       o_addchr(dest, ch);
+                       continue;
+               }
+               if (ch == '\\') {
+                       /* \x. Copy verbatim. Important for  \(, \) */
+                       ch = i_getch(input);
+                       if (ch == EOF) {
+                               syntax_error_unterm_ch(')');
+                               return 0;
+                       }
+                       o_addchr(dest, ch);
+                       continue;
+               }
        }
-# endif
-       G.flag_return_in_progress = sv_flg;
-
-       restore_G_args(&sv, argv);
-
-       return rc;
+       return ch;
 }
-#endif /* ENABLE_HUSH_FUNCTIONS */
-
+#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS */
 
+/* Return code: 0 for OK, 1 for syntax error */
 #if BB_MMU
-#define exec_builtin(to_free, x, argv) \
-       exec_builtin(x, argv)
-#else
-#define exec_builtin(to_free, x, argv) \
-       exec_builtin(to_free, argv)
+#define parse_dollar(as_string, dest, input, quote_mask) \
+       parse_dollar(dest, input, quote_mask)
+#define as_string NULL
 #endif
-static void exec_builtin(char ***to_free,
-               const struct built_in_command *x,
-               char **argv) NORETURN;
-static void exec_builtin(char ***to_free,
-               const struct built_in_command *x,
-               char **argv)
+static int parse_dollar(o_string *as_string,
+               o_string *dest,
+               struct in_str *input, unsigned char quote_mask)
 {
-#if BB_MMU
-       int rcode = x->b_function(argv);
-       fflush_all();
-       _exit(rcode);
-#else
-       /* On NOMMU, we must never block!
-        * Example: { sleep 99 | read line; } & echo Ok
-        */
-       re_execute_shell(to_free,
-                       argv[0],
-                       G.global_argv[0],
-                       G.global_argv + 1,
-                       argv);
-#endif
-}
+       int ch = i_peek(input);  /* first character after the $ */
 
+       debug_printf_parse("parse_dollar entered: ch='%c'\n", ch);
+       if (isalpha(ch)) {
+               ch = i_getch(input);
+               nommu_addchr(as_string, ch);
+ make_var:
+               o_addchr(dest, SPECIAL_VAR_SYMBOL);
+               while (1) {
+                       debug_printf_parse(": '%c'\n", ch);
+                       o_addchr(dest, ch | quote_mask);
+                       quote_mask = 0;
+                       ch = i_peek(input);
+                       if (!isalnum(ch) && ch != '_')
+                               break;
+                       ch = i_getch(input);
+                       nommu_addchr(as_string, ch);
+               }
+               o_addchr(dest, SPECIAL_VAR_SYMBOL);
+       } else if (isdigit(ch)) {
+ make_one_char_var:
+               ch = i_getch(input);
+               nommu_addchr(as_string, ch);
+               o_addchr(dest, SPECIAL_VAR_SYMBOL);
+               debug_printf_parse(": '%c'\n", ch);
+               o_addchr(dest, ch | quote_mask);
+               o_addchr(dest, SPECIAL_VAR_SYMBOL);
+       } else switch (ch) {
+       case '$': /* pid */
+       case '!': /* last bg pid */
+       case '?': /* last exit code */
+       case '#': /* number of args */
+       case '*': /* args */
+       case '@': /* args */
+               goto make_one_char_var;
+       case '{': {
+               o_addchr(dest, SPECIAL_VAR_SYMBOL);
 
-static void execvp_or_die(char **argv) NORETURN;
-static void execvp_or_die(char **argv)
-{
-       debug_printf_exec("execing '%s'\n", argv[0]);
-       sigprocmask(SIG_SETMASK, &G.inherited_set, NULL);
-       execvp(argv[0], argv);
-       bb_perror_msg("can't execute '%s'", argv[0]);
-       _exit(127); /* bash compat */
-}
+               ch = i_getch(input); /* eat '{' */
+               nommu_addchr(as_string, ch);
 
-#if BB_MMU
-#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \
-       pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
-#define pseudo_exec(nommu_save, command, argv_expanded) \
-       pseudo_exec(command, argv_expanded)
-#endif
+               ch = i_getch(input); /* first char after '{' */
+               /* It should be ${?}, or ${#var},
+                * or even ${?+subst} - operator acting on a special variable,
+                * or the beginning of variable name.
+                */
+               if (ch == EOF
+                || (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) /* not one of those */
+               ) {
+ bad_dollar_syntax:
+                       syntax_error_unterm_str("${name}");
+                       debug_printf_parse("parse_dollar return 0: unterminated ${name}\n");
+                       return 0;
+               }
+               nommu_addchr(as_string, ch);
+               ch |= quote_mask;
 
-/* Called after [v]fork() in run_pipe, or from builtin_exec.
- * Never returns.
- * Don't exit() here.  If you don't exec, use _exit instead.
- * The at_exit handlers apparently confuse the calling process,
- * in particular stdin handling.  Not sure why? -- because of vfork! (vda) */
-static void pseudo_exec_argv(nommu_save_t *nommu_save,
-               char **argv, int assignment_cnt,
-               char **argv_expanded) NORETURN;
-static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
-               char **argv, int assignment_cnt,
-               char **argv_expanded)
-{
-       char **new_env;
+               /* It's possible to just call add_till_closing_bracket() at this point.
+                * However, this regresses some of our testsuite cases
+                * which check invalid constructs like ${%}.
+                * Oh well... let's check that the var name part is fine... */
 
-       /* Case when we are here: ... | var=val | ... */
-       if (!argv[assignment_cnt])
-               _exit(EXIT_SUCCESS);
+               while (1) {
+                       unsigned pos;
 
-       new_env = expand_assignments(argv, assignment_cnt);
-#if BB_MMU
-       set_vars_and_save_old(new_env);
-       free(new_env); /* optional */
-       /* we can also destroy set_vars_and_save_old's return value,
-        * to save memory */
-#else
-       nommu_save->new_env = new_env;
-       nommu_save->old_vars = set_vars_and_save_old(new_env);
-#endif
-       if (argv_expanded) {
-               argv = argv_expanded;
-       } else {
-               argv = expand_strvec_to_strvec(argv + assignment_cnt);
-#if !BB_MMU
-               nommu_save->argv = argv;
-#endif
-       }
+                       o_addchr(dest, ch);
+                       debug_printf_parse(": '%c'\n", ch);
 
-#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
-       if (strchr(argv[0], '/') != NULL)
-               goto skip;
-#endif
+                       ch = i_getch(input);
+                       nommu_addchr(as_string, ch);
+                       if (ch == '}')
+                               break;
 
-       /* Check if the command matches any of the builtins.
-        * Depending on context, this might be redundant.  But it's
-        * easier to waste a few CPU cycles than it is to figure out
-        * if this is one of those cases.
-        */
-       {
-               /* On NOMMU, it is more expensive to re-execute shell
-                * just in order to run echo or test builtin.
-                * It's better to skip it here and run corresponding
-                * non-builtin later. */
-               const struct built_in_command *x;
-               x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]);
-               if (x) {
-                       exec_builtin(&nommu_save->argv_from_re_execing, x, argv);
-               }
-       }
-#if ENABLE_HUSH_FUNCTIONS
-       /* Check if the command matches any functions */
-       {
-               const struct function *funcp = find_function(argv[0]);
-               if (funcp) {
-                       exec_function(&nommu_save->argv_from_re_execing, funcp, argv);
+                       if (!isalnum(ch) && ch != '_') {
+                               unsigned end_ch;
+                               unsigned char last_ch;
+                               /* handle parameter expansions
+                                * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
+                                */
+                               if (!strchr(VAR_SUBST_OPS, ch)) /* ${var<bad_char>... */
+                                       goto bad_dollar_syntax;
+
+                               /* Eat everything until closing '}' (or ':') */
+                               end_ch = '}';
+                               if (ENABLE_HUSH_BASH_COMPAT
+                                && ch == ':'
+                                && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input))
+                               ) {
+                                       /* It's ${var:N[:M]} thing */
+                                       end_ch = '}' * 0x100 + ':';
+                               }
+                               if (ENABLE_HUSH_BASH_COMPAT
+                                && ch == '/'
+                               ) {
+                                       /* It's ${var/[/]pattern[/repl]} thing */
+                                       if (i_peek(input) == '/') { /* ${var//pattern[/repl]}? */
+                                               i_getch(input);
+                                               nommu_addchr(as_string, '/');
+                                               ch = '\\';
+                                       }
+                                       end_ch = '}' * 0x100 + '/';
+                               }
+                               o_addchr(dest, ch);
+ again:
+                               if (!BB_MMU)
+                                       pos = dest->length;
+#if ENABLE_HUSH_DOLLAR_OPS
+                               last_ch = add_till_closing_bracket(dest, input, end_ch);
+                               if (last_ch == 0) /* error? */
+                                       return 0;
+#else
+#error Simple code to only allow ${var} is not implemented
+#endif
+                               if (as_string) {
+                                       o_addstr(as_string, dest->data + pos);
+                                       o_addchr(as_string, last_ch);
+                               }
+
+                               if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) {
+                                       /* close the first block: */
+                                       o_addchr(dest, SPECIAL_VAR_SYMBOL);
+                                       /* while parsing N from ${var:N[:M]}
+                                        * or pattern from ${var/[/]pattern[/repl]} */
+                                       if ((end_ch & 0xff) == last_ch) {
+                                               /* got ':' or '/'- parse the rest */
+                                               end_ch = '}';
+                                               goto again;
+                                       }
+                                       /* got '}' */
+                                       if (end_ch == '}' * 0x100 + ':') {
+                                               /* it's ${var:N} - emulate :999999999 */
+                                               o_addstr(dest, "999999999");
+                                       } /* else: it's ${var/[/]pattern} */
+                               }
+                               break;
+                       }
                }
+               o_addchr(dest, SPECIAL_VAR_SYMBOL);
+               break;
        }
-#endif
+#if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK
+       case '(': {
+               unsigned pos;
 
-#if ENABLE_FEATURE_SH_STANDALONE
-       /* Check if the command matches any busybox applets */
-       {
-               int a = find_applet_by_name(argv[0]);
-               if (a >= 0) {
-# if BB_MMU /* see above why on NOMMU it is not allowed */
-                       if (APPLET_IS_NOEXEC(a)) {
-                               debug_printf_exec("running applet '%s'\n", argv[0]);
-                               run_applet_no_and_exit(a, argv);
+               ch = i_getch(input);
+               nommu_addchr(as_string, ch);
+# if ENABLE_SH_MATH_SUPPORT
+               if (i_peek(input) == '(') {
+                       ch = i_getch(input);
+                       nommu_addchr(as_string, ch);
+                       o_addchr(dest, SPECIAL_VAR_SYMBOL);
+                       o_addchr(dest, /*quote_mask |*/ '+');
+                       if (!BB_MMU)
+                               pos = dest->length;
+                       if (!add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG))
+                               return 0; /* error */
+                       if (as_string) {
+                               o_addstr(as_string, dest->data + pos);
+                               o_addchr(as_string, ')');
+                               o_addchr(as_string, ')');
                        }
+                       o_addchr(dest, SPECIAL_VAR_SYMBOL);
+                       break;
+               }
 # endif
-                       /* Re-exec ourselves */
-                       debug_printf_exec("re-execing applet '%s'\n", argv[0]);
-                       sigprocmask(SIG_SETMASK, &G.inherited_set, NULL);
-                       execv(bb_busybox_exec_path, argv);
-                       /* If they called chroot or otherwise made the binary no longer
-                        * executable, fall through */
+# if ENABLE_HUSH_TICK
+               o_addchr(dest, SPECIAL_VAR_SYMBOL);
+               o_addchr(dest, quote_mask | '`');
+               if (!BB_MMU)
+                       pos = dest->length;
+               if (!add_till_closing_bracket(dest, input, ')'))
+                       return 0; /* error */
+               if (as_string) {
+                       o_addstr(as_string, dest->data + pos);
+                       o_addchr(as_string, ')');
                }
+               o_addchr(dest, SPECIAL_VAR_SYMBOL);
+# endif
+               break;
        }
 #endif
-
-#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
- skip:
-#endif
-       execvp_or_die(argv);
+       case '_':
+               ch = i_getch(input);
+               nommu_addchr(as_string, ch);
+               ch = i_peek(input);
+               if (isalnum(ch)) { /* it's $_name or $_123 */
+                       ch = '_';
+                       goto make_var;
+               }
+               /* else: it's $_ */
+       /* TODO: $_ and $-: */
+       /* $_ Shell or shell script name; or last argument of last command
+        * (if last command wasn't a pipe; if it was, bash sets $_ to "");
+        * but in command's env, set to full pathname used to invoke it */
+       /* $- Option flags set by set builtin or shell options (-i etc) */
+       default:
+               o_addQchr(dest, '$');
+       }
+       debug_printf_parse("parse_dollar return 1 (ok)\n");
+       return 1;
+#undef as_string
 }
 
-/* Called after [v]fork() in run_pipe
- */
-static void pseudo_exec(nommu_save_t *nommu_save,
-               struct command *command,
-               char **argv_expanded) NORETURN;
-static void pseudo_exec(nommu_save_t *nommu_save,
-               struct command *command,
-               char **argv_expanded)
+#if BB_MMU
+# if ENABLE_HUSH_BASH_COMPAT
+#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
+       encode_string(dest, input, dquote_end, process_bkslash)
+# else
+/* only ${var/pattern/repl} (its pattern part) needs additional mode */
+#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
+       encode_string(dest, input, dquote_end)
+# endif
+#define as_string NULL
+
+#else /* !MMU */
+
+# if ENABLE_HUSH_BASH_COMPAT
+/* all parameters are needed, no macro tricks */
+# else
+#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
+       encode_string(as_string, dest, input, dquote_end)
+# endif
+#endif
+static int encode_string(o_string *as_string,
+               o_string *dest,
+               struct in_str *input,
+               int dquote_end,
+               int process_bkslash)
 {
-       if (command->argv) {
-               pseudo_exec_argv(nommu_save, command->argv,
-                               command->assignment_cnt, argv_expanded);
-       }
+#if !ENABLE_HUSH_BASH_COMPAT
+       const int process_bkslash = 1;
+#endif
+       int ch;
+       int next;
 
-       if (command->group) {
-               /* Cases when we are here:
-                * ( list )
-                * { list } &
-                * ... | ( list ) | ...
-                * ... | { list } | ...
+ again:
+       ch = i_getch(input);
+       if (ch != EOF)
+               nommu_addchr(as_string, ch);
+       if (ch == dquote_end) { /* may be only '"' or EOF */
+               debug_printf_parse("encode_string return 1 (ok)\n");
+               return 1;
+       }
+       /* note: can't move it above ch == dquote_end check! */
+       if (ch == EOF) {
+               syntax_error_unterm_ch('"');
+               return 0; /* error */
+       }
+       next = '\0';
+       if (ch != '\n') {
+               next = i_peek(input);
+       }
+       debug_printf_parse("\" ch=%c (%d) escape=%d\n",
+                       ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
+       if (process_bkslash && ch == '\\') {
+               if (next == EOF) {
+                       syntax_error("\\<eof>");
+                       xfunc_die();
+               }
+               /* bash:
+                * "The backslash retains its special meaning [in "..."]
+                * only when followed by one of the following characters:
+                * $, `, ", \, or <newline>.  A double quote may be quoted
+                * within double quotes by preceding it with a backslash."
+                * NB: in (unquoted) heredoc, above does not apply to ",
+                * therefore we check for it by "next == dquote_end" cond.
                 */
-#if BB_MMU
-               int rcode;
-               debug_printf_exec("pseudo_exec: run_list\n");
-               reset_traps_to_defaults();
-               rcode = run_list(command->group);
-               /* OK to leak memory by not calling free_pipe_list,
-                * since this process is about to exit */
-               _exit(rcode);
-#else
-               re_execute_shell(&nommu_save->argv_from_re_execing,
-                               command->group_as_string,
-                               G.global_argv[0],
-                               G.global_argv + 1,
-                               NULL);
-#endif
+               if (next == dquote_end || strchr("$`\\\n", next)) {
+                       ch = i_getch(input); /* eat next */
+                       if (ch == '\n')
+                               goto again; /* skip \<newline> */
+               } /* else: ch remains == '\\', and we double it below: */
+               o_addqchr(dest, ch); /* \c if c is a glob char, else just c */
+               nommu_addchr(as_string, ch);
+               goto again;
        }
-
-       /* Case when we are here: ... | >file */
-       debug_printf_exec("pseudo_exec'ed null command\n");
-       _exit(EXIT_SUCCESS);
+       if (ch == '$') {
+               if (!parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80)) {
+                       debug_printf_parse("encode_string return 0: "
+                                       "parse_dollar returned 0 (error)\n");
+                       return 0;
+               }
+               goto again;
+       }
+#if ENABLE_HUSH_TICK
+       if (ch == '`') {
+               //unsigned pos = dest->length;
+               o_addchr(dest, SPECIAL_VAR_SYMBOL);
+               o_addchr(dest, 0x80 | '`');
+               if (!add_till_backquote(dest, input, /*in_dquote:*/ dquote_end == '"'))
+                       return 0; /* error */
+               o_addchr(dest, SPECIAL_VAR_SYMBOL);
+               //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
+               goto again;
+       }
+#endif
+       o_addQchr(dest, ch);
+       goto again;
+#undef as_string
 }
 
-#if ENABLE_HUSH_JOB
-static const char *get_cmdtext(struct pipe *pi)
+/*
+ * Scan input until EOF or end_trigger char.
+ * Return a list of pipes to execute, or NULL on EOF
+ * or if end_trigger character is met.
+ * On syntax error, exit if shell is not interactive,
+ * reset parsing machinery and start parsing anew,
+ * or return ERR_PTR.
+ */
+static struct pipe *parse_stream(char **pstring,
+               struct in_str *input,
+               int end_trigger)
 {
-       char **argv;
-       char *p;
-       int len;
+       struct parse_context ctx;
+       o_string dest = NULL_O_STRING;
+       int heredoc_cnt;
 
-       /* This is subtle. ->cmdtext is created only on first backgrounding.
-        * (Think "cat, <ctrl-z>, fg, <ctrl-z>, fg, <ctrl-z>...." here...)
-        * On subsequent bg argv is trashed, but we won't use it */
-       if (pi->cmdtext)
-               return pi->cmdtext;
-       argv = pi->cmds[0].argv;
-       if (!argv || !argv[0]) {
-               pi->cmdtext = xzalloc(1);
-               return pi->cmdtext;
-       }
-
-       len = 0;
-       do {
-               len += strlen(*argv) + 1;
-       } while (*++argv);
-       p = xmalloc(len);
-       pi->cmdtext = p;
-       argv = pi->cmds[0].argv;
-       do {
-               len = strlen(*argv);
-               memcpy(p, *argv, len);
-               p += len;
-               *p++ = ' ';
-       } while (*++argv);
-       p[-1] = '\0';
-       return pi->cmdtext;
-}
-
-static void insert_bg_job(struct pipe *pi)
-{
-       struct pipe *job, **jobp;
-       int i;
-
-       /* Linear search for the ID of the job to use */
-       pi->jobid = 1;
-       for (job = G.job_list; job; job = job->next)
-               if (job->jobid >= pi->jobid)
-                       pi->jobid = job->jobid + 1;
-
-       /* Add job to the list of running jobs */
-       jobp = &G.job_list;
-       while ((job = *jobp) != NULL)
-               jobp = &job->next;
-       job = *jobp = xmalloc(sizeof(*job));
+       /* Single-quote triggers a bypass of the main loop until its mate is
+        * found.  When recursing, quote state is passed in via dest->o_expflags.
+        */
+       debug_printf_parse("parse_stream entered, end_trigger='%c'\n",
+                       end_trigger ? end_trigger : 'X');
+       debug_enter();
 
-       *job = *pi; /* physical copy */
-       job->next = NULL;
-       job->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds);
-       /* Cannot copy entire pi->cmds[] vector! This causes double frees */
-       for (i = 0; i < pi->num_cmds; i++) {
-               job->cmds[i].pid = pi->cmds[i].pid;
-               /* all other fields are not used and stay zero */
-       }
-       job->cmdtext = xstrdup(get_cmdtext(pi));
+       /* If very first arg is "" or '', dest.data may end up NULL.
+        * Preventing this: */
+       o_addchr(&dest, '\0');
+       dest.length = 0;
 
-       if (G_interactive_fd)
-               printf("[%d] %d %s\n", job->jobid, job->cmds[0].pid, job->cmdtext);
-       /* Last command's pid goes to $! */
-       G.last_bg_pid = job->cmds[job->num_cmds - 1].pid;
-       G.last_jobid = job->jobid;
-}
+       /* We used to separate words on $IFS here. This was wrong.
+        * $IFS is used only for word splitting when $var is expanded,
+        * here we should use blank chars as separators, not $IFS
+        */
 
-static void remove_bg_job(struct pipe *pi)
-{
-       struct pipe *prev_pipe;
+       if (MAYBE_ASSIGNMENT != 0)
+               dest.o_assignment = MAYBE_ASSIGNMENT;
+       initialize_context(&ctx);
+       heredoc_cnt = 0;
+       while (1) {
+               const char *is_blank;
+               const char *is_special;
+               int ch;
+               int next;
+               int redir_fd;
+               redir_type redir_style;
 
-       if (pi == G.job_list) {
-               G.job_list = pi->next;
-       } else {
-               prev_pipe = G.job_list;
-               while (prev_pipe->next != pi)
-                       prev_pipe = prev_pipe->next;
-               prev_pipe->next = pi->next;
-       }
-       if (G.job_list)
-               G.last_jobid = G.job_list->jobid;
-       else
-               G.last_jobid = 0;
-}
+               ch = i_getch(input);
+               debug_printf_parse(": ch=%c (%d) escape=%d\n",
+                               ch, ch, !!(dest.o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
+               if (ch == EOF) {
+                       struct pipe *pi;
 
-/* Remove a backgrounded job */
-static void delete_finished_bg_job(struct pipe *pi)
-{
-       remove_bg_job(pi);
-       pi->stopped_cmds = 0;
-       free_pipe(pi);
-       free(pi);
-}
-#endif /* JOB */
+                       if (heredoc_cnt) {
+                               syntax_error_unterm_str("here document");
+                               goto parse_error;
+                       }
+                       /* end_trigger == '}' case errors out earlier,
+                        * checking only ')' */
+                       if (end_trigger == ')') {
+                               syntax_error_unterm_ch('(');
+                               goto parse_error;
+                       }
 
-/* Check to see if any processes have exited -- if they
- * have, figure out why and see if a job has completed */
-static int checkjobs(struct pipe* fg_pipe)
-{
-       int attributes;
-       int status;
-#if ENABLE_HUSH_JOB
-       struct pipe *pi;
+                       if (done_word(&dest, &ctx)) {
+                               goto parse_error;
+                       }
+                       o_free(&dest);
+                       done_pipe(&ctx, PIPE_SEQ);
+                       pi = ctx.list_head;
+                       /* If we got nothing... */
+                       /* (this makes bare "&" cmd a no-op.
+                        * bash says: "syntax error near unexpected token '&'") */
+                       if (pi->num_cmds == 0
+                       IF_HAS_KEYWORDS(&& pi->res_word == RES_NONE)
+                       ) {
+                               free_pipe_list(pi);
+                               pi = NULL;
+                       }
+#if !BB_MMU
+                       debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
+                       if (pstring)
+                               *pstring = ctx.as_string.data;
+                       else
+                               o_free_unsafe(&ctx.as_string);
 #endif
-       pid_t childpid;
-       int rcode = 0;
-
-       debug_printf_jobs("checkjobs %p\n", fg_pipe);
+                       debug_leave();
+                       debug_printf_parse("parse_stream return %p\n", pi);
+                       return pi;
+               }
+               nommu_addchr(&ctx.as_string, ch);
 
-       attributes = WUNTRACED;
-       if (fg_pipe == NULL)
-               attributes |= WNOHANG;
+               next = '\0';
+               if (ch != '\n')
+                       next = i_peek(input);
 
-       errno = 0;
-#if ENABLE_HUSH_FAST
-       if (G.handled_SIGCHLD == G.count_SIGCHLD) {
-//bb_error_msg("[%d] checkjobs: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d children?:%d fg_pipe:%p",
-//getpid(), G.count_SIGCHLD, G.handled_SIGCHLD, G.we_have_children, fg_pipe);
-               /* There was neither fork nor SIGCHLD since last waitpid */
-               /* Avoid doing waitpid syscall if possible */
-               if (!G.we_have_children) {
-                       errno = ECHILD;
-                       return -1;
-               }
-               if (fg_pipe == NULL) { /* is WNOHANG set? */
-                       /* We have children, but they did not exit
-                        * or stop yet (we saw no SIGCHLD) */
-                       return 0;
+               is_special = "{}<>;&|()#'" /* special outside of "str" */
+                               "\\$\"" IF_HUSH_TICK("`"); /* always special */
+               /* Are { and } special here? */
+               if (ctx.command->argv /* word [word]{... - non-special */
+                || dest.length       /* word{... - non-special */
+                || dest.has_quoted_part     /* ""{... - non-special */
+                || (next != ';'             /* }; - special */
+                   && next != ')'           /* }) - special */
+                   && next != '&'           /* }& and }&& ... - special */
+                   && next != '|'           /* }|| ... - special */
+                   && !strchr(defifs, next) /* {word - non-special */
+                   )
+               ) {
+                       /* They are not special, skip "{}" */
+                       is_special += 2;
                }
-               /* else: !WNOHANG, waitpid will block, can't short-circuit */
-       }
-#endif
-
-/* Do we do this right?
- * bash-3.00# sleep 20 | false
- * <ctrl-Z pressed>
- * [3]+  Stopped          sleep 20 | false
- * bash-3.00# echo $?
- * 1   <========== bg pipe is not fully done, but exitcode is already known!
- * [hush 1.14.0: yes we do it right]
- */
- wait_more:
-       while (1) {
-               int i;
-               int dead;
+               is_special = strchr(is_special, ch);
+               is_blank = strchr(defifs, ch);
 
-#if ENABLE_HUSH_FAST
-               i = G.count_SIGCHLD;
-#endif
-               childpid = waitpid(-1, &status, attributes);
-               if (childpid <= 0) {
-                       if (childpid && errno != ECHILD)
-                               bb_perror_msg("waitpid");
-#if ENABLE_HUSH_FAST
-                       else { /* Until next SIGCHLD, waitpid's are useless */
-                               G.we_have_children = (childpid == 0);
-                               G.handled_SIGCHLD = i;
-//bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
+               if (!is_special && !is_blank) { /* ordinary char */
+ ordinary_char:
+                       o_addQchr(&dest, ch);
+                       if ((dest.o_assignment == MAYBE_ASSIGNMENT
+                           || dest.o_assignment == WORD_IS_KEYWORD)
+                        && ch == '='
+                        && is_well_formed_var_name(dest.data, '=')
+                       ) {
+                               dest.o_assignment = DEFINITELY_ASSIGNMENT;
+                               debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
                        }
-#endif
-                       break;
+                       continue;
                }
-               dead = WIFEXITED(status) || WIFSIGNALED(status);
 
-#if DEBUG_JOBS
-               if (WIFSTOPPED(status))
-                       debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n",
-                                       childpid, WSTOPSIG(status), WEXITSTATUS(status));
-               if (WIFSIGNALED(status))
-                       debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n",
-                                       childpid, WTERMSIG(status), WEXITSTATUS(status));
-               if (WIFEXITED(status))
-                       debug_printf_jobs("pid %d exited, exitcode %d\n",
-                                       childpid, WEXITSTATUS(status));
-#endif
-               /* Were we asked to wait for fg pipe? */
-               if (fg_pipe) {
-                       for (i = 0; i < fg_pipe->num_cmds; i++) {
-                               debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
-                               if (fg_pipe->cmds[i].pid != childpid)
-                                       continue;
-                               if (dead) {
-                                       fg_pipe->cmds[i].pid = 0;
-                                       fg_pipe->alive_cmds--;
-                                       if (i == fg_pipe->num_cmds - 1) {
-                                               /* last process gives overall exitstatus */
-                                               rcode = WEXITSTATUS(status);
-                                               /* bash prints killer signal's name for *last*
-                                                * process in pipe (prints just newline for SIGINT).
-                                                * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
-                                                */
-                                               if (WIFSIGNALED(status)) {
-                                                       int sig = WTERMSIG(status);
-                                                       printf("%s\n", sig == SIGINT ? "" : get_signame(sig));
-                                                       /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
-                                                        * Maybe we need to use sig | 128? */
-                                                       rcode = sig + 128;
-                                               }
-                                               IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
+               if (is_blank) {
+                       if (done_word(&dest, &ctx)) {
+                               goto parse_error;
+                       }
+                       if (ch == '\n') {
+                               /* Is this a case when newline is simply ignored?
+                                * Some examples:
+                                * "cmd | <newline> cmd ..."
+                                * "case ... in <newline> word) ..."
+                                */
+                               if (IS_NULL_CMD(ctx.command)
+                                && dest.length == 0 && !dest.has_quoted_part
+                               ) {
+                                       /* This newline can be ignored. But...
+                                        * Without check #1, interactive shell
+                                        * ignores even bare <newline>,
+                                        * and shows the continuation prompt:
+                                        * ps1_prompt$ <enter>
+                                        * ps2> _   <=== wrong, should be ps1
+                                        * Without check #2, "cmd & <newline>"
+                                        * is similarly mistreated.
+                                        * (BTW, this makes "cmd & cmd"
+                                        * and "cmd && cmd" non-orthogonal.
+                                        * Really, ask yourself, why
+                                        * "cmd && <newline>" doesn't start
+                                        * cmd but waits for more input?
+                                        * No reason...)
+                                        */
+                                       struct pipe *pi = ctx.list_head;
+                                       if (pi->num_cmds != 0       /* check #1 */
+                                        && pi->followup != PIPE_BG /* check #2 */
+                                       ) {
+                                               continue;
                                        }
-                               } else {
-                                       fg_pipe->cmds[i].is_stopped = 1;
-                                       fg_pipe->stopped_cmds++;
                                }
-                               debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
-                                               fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
-                               if (fg_pipe->alive_cmds - fg_pipe->stopped_cmds <= 0) {
-                                       /* All processes in fg pipe have exited or stopped */
-/* Note: *non-interactive* bash does not continue if all processes in fg pipe
- * are stopped. Testcase: "cat | cat" in a script (not on command line!)
- * and "killall -STOP cat" */
-                                       if (G_interactive_fd) {
-#if ENABLE_HUSH_JOB
-                                               if (fg_pipe->alive_cmds)
-                                                       insert_bg_job(fg_pipe);
-#endif
-                                               return rcode;
-                                       }
-                                       if (!fg_pipe->alive_cmds)
-                                               return rcode;
+                               /* Treat newline as a command separator. */
+                               done_pipe(&ctx, PIPE_SEQ);
+                               debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt);
+                               if (heredoc_cnt) {
+                                       if (fetch_heredocs(heredoc_cnt, &ctx, input)) {
+                                               goto parse_error;
+                                       }
+                                       heredoc_cnt = 0;
                                }
-                               /* There are still running processes in the fg pipe */
-                               goto wait_more; /* do waitpid again */
-                       }
-                       /* it wasnt fg_pipe, look for process in bg pipes */
-               }
-
-#if ENABLE_HUSH_JOB
-               /* We asked to wait for bg or orphaned children */
-               /* No need to remember exitcode in this case */
-               for (pi = G.job_list; pi; pi = pi->next) {
-                       for (i = 0; i < pi->num_cmds; i++) {
-                               if (pi->cmds[i].pid == childpid)
-                                       goto found_pi_and_prognum;
+                               dest.o_assignment = MAYBE_ASSIGNMENT;
+                               debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
+                               ch = ';';
+                               /* note: if (is_blank) continue;
+                                * will still trigger for us */
                        }
                }
-               /* Happens when shell is used as init process (init=/bin/sh) */
-               debug_printf("checkjobs: pid %d was not in our list!\n", childpid);
-               continue; /* do waitpid again */
 
found_pi_and_prognum:
-               if (dead) {
-                       /* child exited */
-                       pi->cmds[i].pid = 0;
-                       pi->alive_cmds--;
-                       if (!pi->alive_cmds) {
-                               if (G_interactive_fd)
-                                       printf(JOB_STATUS_FORMAT, pi->jobid,
-                                                       "Done", pi->cmdtext);
-                               delete_finished_bg_job(pi);
              /* "cmd}" or "cmd }..." without semicolon or &:
+                * } is an ordinary char in this case, even inside { cmd; }
+                * Pathological example: { ""}; } should exec "}" cmd
+                */
+               if (ch == '}') {
+                       if (!IS_NULL_CMD(ctx.command) /* cmd } */
+                        || dest.length != 0 /* word} */
+                        || dest.has_quoted_part    /* ""} */
+                       ) {
+                               goto ordinary_char;
                        }
-               } else {
-                       /* child stopped */
-                       pi->cmds[i].is_stopped = 1;
-                       pi->stopped_cmds++;
-               }
-#endif
-       } /* while (waitpid succeeds)... */
-
-       return rcode;
-}
-
-#if ENABLE_HUSH_JOB
-static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
-{
-       pid_t p;
-       int rcode = checkjobs(fg_pipe);
-       if (G_saved_tty_pgrp) {
-               /* Job finished, move the shell to the foreground */
-               p = getpgrp(); /* our process group id */
-               debug_printf_jobs("fg'ing ourself: getpgrp()=%d\n", (int)p);
-               tcsetpgrp(G_interactive_fd, p);
-       }
-       return rcode;
-}
-#endif
-
-/* Start all the jobs, but don't wait for anything to finish.
- * See checkjobs().
- *
- * Return code is normally -1, when the caller has to wait for children
- * to finish to determine the exit status of the pipe.  If the pipe
- * is a simple builtin command, however, the action is done by the
- * time run_pipe returns, and the exit code is provided as the
- * return value.
- *
- * Returns -1 only if started some children. IOW: we have to
- * mask out retvals of builtins etc with 0xff!
- *
- * The only case when we do not need to [v]fork is when the pipe
- * is single, non-backgrounded, non-subshell command. Examples:
- * cmd ; ...   { list } ; ...
- * cmd && ...  { list } && ...
- * cmd || ...  { list } || ...
- * If it is, then we can run cmd as a builtin, NOFORK [do we do this?],
- * or (if SH_STANDALONE) an applet, and we can run the { list }
- * with run_list. If it isn't one of these, we fork and exec cmd.
- *
- * Cases when we must fork:
- * non-single:   cmd | cmd
- * backgrounded: cmd &     { list } &
- * subshell:     ( list ) [&]
- */
-static NOINLINE int run_pipe(struct pipe *pi)
-{
-       static const char *const null_ptr = NULL;
-       int i;
-       int nextin;
-       struct command *command;
-       char **argv_expanded;
-       char **argv;
-       char *p;
-       /* it is not always needed, but we aim to smaller code */
-       int squirrel[] = { -1, -1, -1 };
-       int rcode;
-
-       debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds);
-       debug_enter();
-
-       IF_HUSH_JOB(pi->pgrp = -1;)
-       pi->stopped_cmds = 0;
-       command = &(pi->cmds[0]);
-       argv_expanded = NULL;
-
-       if (pi->num_cmds != 1
-        || pi->followup == PIPE_BG
-        || command->cmd_type == CMD_SUBSHELL
-       ) {
-               goto must_fork;
-       }
-
-       pi->alive_cmds = 1;
-
-       debug_printf_exec(": group:%p argv:'%s'\n",
-               command->group, command->argv ? command->argv[0] : "NONE");
-
-       if (command->group) {
-#if ENABLE_HUSH_FUNCTIONS
-               if (command->cmd_type == CMD_FUNCDEF) {
-                       /* "executing" func () { list } */
-                       struct function *funcp;
-
-                       funcp = new_function(command->argv[0]);
-                       /* funcp->name is already set to argv[0] */
-                       funcp->body = command->group;
-# if !BB_MMU
-                       funcp->body_as_string = command->group_as_string;
-                       command->group_as_string = NULL;
-# endif
-                       command->group = NULL;
-                       command->argv[0] = NULL;
-                       debug_printf_exec("cmd %p has child func at %p\n", command, funcp);
-                       funcp->parent_cmd = command;
-                       command->child_func = funcp;
-
-                       debug_printf_exec("run_pipe: return EXIT_SUCCESS\n");
-                       debug_leave();
-                       return EXIT_SUCCESS;
-               }
-#endif
-               /* { list } */
-               debug_printf("non-subshell group\n");
-               rcode = 1; /* exitcode if redir failed */
-               if (setup_redirects(command, squirrel) == 0) {
-                       debug_printf_exec(": run_list\n");
-                       rcode = run_list(command->group) & 0xff;
+                       if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
+                               goto skip_end_trigger;
+                       /* else: } does terminate a group */
                }
-               restore_redirects(squirrel);
-               IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
-               debug_leave();
-               debug_printf_exec("run_pipe: return %d\n", rcode);
-               return rcode;
-       }
 
-       argv = command->argv ? command->argv : (char **) &null_ptr;
-       {
-               const struct built_in_command *x;
-#if ENABLE_HUSH_FUNCTIONS
-               const struct function *funcp;
-#else
-               enum { funcp = 0 };
+               if (end_trigger && end_trigger == ch
+                && (ch != ';' || heredoc_cnt == 0)
+#if ENABLE_HUSH_CASE
+                && (ch != ')'
+                   || ctx.ctx_res_w != RES_MATCH
+                   || (!dest.has_quoted_part && strcmp(dest.data, "esac") == 0)
+                   )
 #endif
-               char **new_env = NULL;
-               struct variable *old_vars = NULL;
-
-               if (argv[command->assignment_cnt] == NULL) {
-                       /* Assignments, but no command */
-                       /* Ensure redirects take effect (that is, create files).
-                        * Try "a=t >file": */
-                       rcode = setup_redirects(command, squirrel);
-                       restore_redirects(squirrel);
-                       /* Set shell variables */
-                       while (*argv) {
-                               p = expand_string_to_string(*argv);
-                               debug_printf_exec("set shell var:'%s'->'%s'\n",
-                                               *argv, p);
-                               set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
-                               argv++;
+               ) {
+                       if (heredoc_cnt) {
+                               /* This is technically valid:
+                                * { cat <<HERE; }; echo Ok
+                                * heredoc
+                                * heredoc
+                                * HERE
+                                * but we don't support this.
+                                * We require heredoc to be in enclosing {}/(),
+                                * if any.
+                                */
+                               syntax_error_unterm_str("here document");
+                               goto parse_error;
                        }
-                       /* Redirect error sets $? to 1. Othervise,
-                        * if evaluating assignment value set $?, retain it.
-                        * Try "false; q=`exit 2`; echo $?" - should print 2: */
-                       if (rcode == 0)
-                               rcode = G.last_exitcode;
-                       /* Do we need to flag set_local_var() errors?
-                        * "assignment to readonly var" and "putenv error"
-                        */
-                       IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
-                       debug_leave();
-                       debug_printf_exec("run_pipe: return %d\n", rcode);
-                       return rcode;
-               }
-
-               /* Expand the rest into (possibly) many strings each */
-               if (0) {}
-#if ENABLE_HUSH_BASH_COMPAT
-               else if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) {
-                       argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
-               }
-#endif
-#ifdef CMD_SINGLEWORD_NOGLOB_COND
-               else if (command->cmd_type == CMD_SINGLEWORD_NOGLOB_COND) {
-                       argv_expanded = expand_strvec_to_strvec_singleword_noglob_cond(argv + command->assignment_cnt);
-
-               }
+                       if (done_word(&dest, &ctx)) {
+                               goto parse_error;
+                       }
+                       done_pipe(&ctx, PIPE_SEQ);
+                       dest.o_assignment = MAYBE_ASSIGNMENT;
+                       debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
+                       /* Do we sit outside of any if's, loops or case's? */
+                       if (!HAS_KEYWORDS
+                       IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
+                       ) {
+                               o_free(&dest);
+#if !BB_MMU
+                               debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
+                               if (pstring)
+                                       *pstring = ctx.as_string.data;
+                               else
+                                       o_free_unsafe(&ctx.as_string);
 #endif
-               else {
-                       argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
-               }
-
-               /* if someone gives us an empty string: `cmd with empty output` */
-               if (!argv_expanded[0]) {
-                       free(argv_expanded);
-                       debug_leave();
-                       return G.last_exitcode;
+                               debug_leave();
+                               debug_printf_parse("parse_stream return %p: "
+                                               "end_trigger char found\n",
+                                               ctx.list_head);
+                               return ctx.list_head;
+                       }
                }
+ skip_end_trigger:
+               if (is_blank)
+                       continue;
 
-               x = find_builtin(argv_expanded[0]);
-#if ENABLE_HUSH_FUNCTIONS
-               funcp = NULL;
-               if (!x)
-                       funcp = find_function(argv_expanded[0]);
+               /* Catch <, > before deciding whether this word is
+                * an assignment. a=1 2>z b=2: b=2 is still assignment */
+               switch (ch) {
+               case '>':
+                       redir_fd = redirect_opt_num(&dest);
+                       if (done_word(&dest, &ctx)) {
+                               goto parse_error;
+                       }
+                       redir_style = REDIRECT_OVERWRITE;
+                       if (next == '>') {
+                               redir_style = REDIRECT_APPEND;
+                               ch = i_getch(input);
+                               nommu_addchr(&ctx.as_string, ch);
+                       }
+#if 0
+                       else if (next == '(') {
+                               syntax_error(">(process) not supported");
+                               goto parse_error;
+                       }
 #endif
-               if (x || funcp) {
-                       if (!funcp) {
-                               if (x->b_function == builtin_exec && argv_expanded[1] == NULL) {
-                                       debug_printf("exec with redirects only\n");
-                                       rcode = setup_redirects(command, NULL);
-                                       goto clean_up_and_ret1;
-                               }
+                       if (parse_redirect(&ctx, redir_fd, redir_style, input))
+                               goto parse_error;
+                       continue; /* back to top of while (1) */
+               case '<':
+                       redir_fd = redirect_opt_num(&dest);
+                       if (done_word(&dest, &ctx)) {
+                               goto parse_error;
+                       }
+                       redir_style = REDIRECT_INPUT;
+                       if (next == '<') {
+                               redir_style = REDIRECT_HEREDOC;
+                               heredoc_cnt++;
+                               debug_printf_parse("++heredoc_cnt=%d\n", heredoc_cnt);
+                               ch = i_getch(input);
+                               nommu_addchr(&ctx.as_string, ch);
+                       } else if (next == '>') {
+                               redir_style = REDIRECT_IO;
+                               ch = i_getch(input);
+                               nommu_addchr(&ctx.as_string, ch);
+                       }
+#if 0
+                       else if (next == '(') {
+                               syntax_error("<(process) not supported");
+                               goto parse_error;
                        }
-                       /* setup_redirects acts on file descriptors, not FILEs.
-                        * This is perfect for work that comes after exec().
-                        * Is it really safe for inline use?  Experimentally,
-                        * things seem to work. */
-                       rcode = setup_redirects(command, squirrel);
-                       if (rcode == 0) {
-                               new_env = expand_assignments(argv, command->assignment_cnt);
-                               old_vars = set_vars_and_save_old(new_env);
-                               if (!funcp) {
-                                       debug_printf_exec(": builtin '%s' '%s'...\n",
-                                               x->b_cmd, argv_expanded[1]);
-                                       rcode = x->b_function(argv_expanded) & 0xff;
-                                       fflush_all();
-                               }
-#if ENABLE_HUSH_FUNCTIONS
-                               else {
-# if ENABLE_HUSH_LOCAL
-                                       struct variable **sv;
-                                       sv = G.shadowed_vars_pp;
-                                       G.shadowed_vars_pp = &old_vars;
-# endif
-                                       debug_printf_exec(": function '%s' '%s'...\n",
-                                               funcp->name, argv_expanded[1]);
-                                       rcode = run_function(funcp, argv_expanded) & 0xff;
-# if ENABLE_HUSH_LOCAL
-                                       G.shadowed_vars_pp = sv;
-# endif
-                               }
 #endif
+                       if (parse_redirect(&ctx, redir_fd, redir_style, input))
+                               goto parse_error;
+                       continue; /* back to top of while (1) */
+               case '#':
+                       if (dest.length == 0 && !dest.has_quoted_part) {
+                               /* skip "#comment" */
+                               while (1) {
+                                       ch = i_peek(input);
+                                       if (ch == EOF || ch == '\n')
+                                               break;
+                                       i_getch(input);
+                                       /* note: we do not add it to &ctx.as_string */
+                               }
+                               nommu_addchr(&ctx.as_string, '\n');
+                               continue; /* back to top of while (1) */
                        }
-#if ENABLE_FEATURE_SH_STANDALONE
- clean_up_and_ret:
+                       break;
+               case '\\':
+                       if (next == '\n') {
+                               /* It's "\<newline>" */
+#if !BB_MMU
+                               /* Remove trailing '\' from ctx.as_string */
+                               ctx.as_string.data[--ctx.as_string.length] = '\0';
 #endif
-                       restore_redirects(squirrel);
-                       unset_vars(new_env);
-                       add_vars(old_vars);
- clean_up_and_ret1:
-                       free(argv_expanded);
-                       IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
-                       debug_leave();
-                       debug_printf_exec("run_pipe return %d\n", rcode);
-                       return rcode;
-               }
-
-#if ENABLE_FEATURE_SH_STANDALONE
-               i = find_applet_by_name(argv_expanded[0]);
-               if (i >= 0 && APPLET_IS_NOFORK(i)) {
-                       rcode = setup_redirects(command, squirrel);
-                       if (rcode == 0) {
-                               new_env = expand_assignments(argv, command->assignment_cnt);
-                               old_vars = set_vars_and_save_old(new_env);
-                               debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
-                                       argv_expanded[0], argv_expanded[1]);
-                               rcode = run_nofork_applet(i, argv_expanded);
+                               ch = i_getch(input); /* eat it */
+                               continue; /* back to top of while (1) */
                        }
-                       goto clean_up_and_ret;
+                       break;
                }
-#endif
-               /* It is neither builtin nor applet. We must fork. */
-       }
 
- must_fork:
-       /* NB: argv_expanded may already be created, and that
-        * might include `cmd` runs! Do not rerun it! We *must*
-        * use argv_expanded if it's non-NULL */
-
-       /* Going to fork a child per each pipe member */
-       pi->alive_cmds = 0;
-       nextin = 0;
-
-       for (i = 0; i < pi->num_cmds; i++) {
-               struct fd_pair pipefds;
-#if !BB_MMU
-               volatile nommu_save_t nommu_save;
-               nommu_save.new_env = NULL;
-               nommu_save.old_vars = NULL;
-               nommu_save.argv = NULL;
-               nommu_save.argv_from_re_execing = NULL;
-#endif
-               command = &(pi->cmds[i]);
-               if (command->argv) {
-                       debug_printf_exec(": pipe member '%s' '%s'...\n",
-                                       command->argv[0], command->argv[1]);
-               } else {
-                       debug_printf_exec(": pipe member with no argv\n");
+               if (dest.o_assignment == MAYBE_ASSIGNMENT
+                /* check that we are not in word in "a=1 2>word b=1": */
+                && !ctx.pending_redirect
+               ) {
+                       /* ch is a special char and thus this word
+                        * cannot be an assignment */
+                       dest.o_assignment = NOT_ASSIGNMENT;
+                       debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
                }
 
-               /* pipes are inserted between pairs of commands */
-               pipefds.rd = 0;
-               pipefds.wr = 1;
-               if ((i + 1) < pi->num_cmds)
-                       xpiped_pair(pipefds);
+               /* Note: nommu_addchr(&ctx.as_string, ch) is already done */
 
-               command->pid = BB_MMU ? fork() : vfork();
-               if (!command->pid) { /* child */
-#if ENABLE_HUSH_JOB
-                       disable_restore_tty_pgrp_on_exit();
-                       CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
+               switch (ch) {
+               case '#': /* non-comment #: "echo a#b" etc */
+                       o_addQchr(&dest, ch);
+                       break;
+               case '\\':
+                       if (next == EOF) {
+                               syntax_error("\\<eof>");
+                               xfunc_die();
+                       }
+                       ch = i_getch(input);
+                       /* note: ch != '\n' (that case does not reach this place) */
+                       o_addchr(&dest, '\\');
+                       /*nommu_addchr(&ctx.as_string, '\\'); - already done */
+                       o_addchr(&dest, ch);
+                       nommu_addchr(&ctx.as_string, ch);
+                       /* Example: echo Hello \2>file
+                        * we need to know that word 2 is quoted */
+                       dest.has_quoted_part = 1;
+                       break;
+               case '$':
+                       if (!parse_dollar(&ctx.as_string, &dest, input, /*quote_mask:*/ 0)) {
+                               debug_printf_parse("parse_stream parse error: "
+                                       "parse_dollar returned 0 (error)\n");
+                               goto parse_error;
+                       }
+                       break;
+               case '\'':
+                       dest.has_quoted_part = 1;
+                       if (next == '\'' && !ctx.pending_redirect) {
+ insert_empty_quoted_str_marker:
+                               nommu_addchr(&ctx.as_string, next);
+                               i_getch(input); /* eat second ' */
+                               o_addchr(&dest, SPECIAL_VAR_SYMBOL);
+                               o_addchr(&dest, SPECIAL_VAR_SYMBOL);
+                       } else {
+                               while (1) {
+                                       ch = i_getch(input);
+                                       if (ch == EOF) {
+                                               syntax_error_unterm_ch('\'');
+                                               goto parse_error;
+                                       }
+                                       nommu_addchr(&ctx.as_string, ch);
+                                       if (ch == '\'')
+                                               break;
+                                       o_addqchr(&dest, ch);
+                               }
+                       }
+                       break;
+               case '"':
+                       dest.has_quoted_part = 1;
+                       if (next == '"' && !ctx.pending_redirect)
+                               goto insert_empty_quoted_str_marker;
+                       if (dest.o_assignment == NOT_ASSIGNMENT)
+                               dest.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
+                       if (!encode_string(&ctx.as_string, &dest, input, '"', /*process_bkslash:*/ 1))
+                               goto parse_error;
+                       dest.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
+                       break;
+#if ENABLE_HUSH_TICK
+               case '`': {
+                       USE_FOR_NOMMU(unsigned pos;)
 
-                       /* Every child adds itself to new process group
-                        * with pgid == pid_of_first_child_in_pipe */
-                       if (G.run_list_level == 1 && G_interactive_fd) {
-                               pid_t pgrp;
-                               pgrp = pi->pgrp;
-                               if (pgrp < 0) /* true for 1st process only */
-                                       pgrp = getpid();
-                               if (setpgid(0, pgrp) == 0
-                                && pi->followup != PIPE_BG
-                                && G_saved_tty_pgrp /* we have ctty */
-                               ) {
-                                       /* We do it in *every* child, not just first,
-                                        * to avoid races */
-                                       tcsetpgrp(G_interactive_fd, pgrp);
+                       o_addchr(&dest, SPECIAL_VAR_SYMBOL);
+                       o_addchr(&dest, '`');
+                       USE_FOR_NOMMU(pos = dest.length;)
+                       if (!add_till_backquote(&dest, input, /*in_dquote:*/ 0))
+                               goto parse_error;
+# if !BB_MMU
+                       o_addstr(&ctx.as_string, dest.data + pos);
+                       o_addchr(&ctx.as_string, '`');
+# endif
+                       o_addchr(&dest, SPECIAL_VAR_SYMBOL);
+                       //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos);
+                       break;
+               }
+#endif
+               case ';':
+#if ENABLE_HUSH_CASE
+ case_semi:
+#endif
+                       if (done_word(&dest, &ctx)) {
+                               goto parse_error;
+                       }
+                       done_pipe(&ctx, PIPE_SEQ);
+#if ENABLE_HUSH_CASE
+                       /* Eat multiple semicolons, detect
+                        * whether it means something special */
+                       while (1) {
+                               ch = i_peek(input);
+                               if (ch != ';')
+                                       break;
+                               ch = i_getch(input);
+                               nommu_addchr(&ctx.as_string, ch);
+                               if (ctx.ctx_res_w == RES_CASE_BODY) {
+                                       ctx.ctx_dsemicolon = 1;
+                                       ctx.ctx_res_w = RES_MATCH;
+                                       break;
                                }
                        }
 #endif
-                       if (pi->alive_cmds == 0 && pi->followup == PIPE_BG) {
-                               /* 1st cmd in backgrounded pipe
-                                * should have its stdin /dev/null'ed */
-                               close(0);
-                               if (open(bb_dev_null, O_RDONLY))
-                                       xopen("/", O_RDONLY);
+ new_cmd:
+                       /* We just finished a cmd. New one may start
+                        * with an assignment */
+                       dest.o_assignment = MAYBE_ASSIGNMENT;
+                       debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
+                       break;
+               case '&':
+                       if (done_word(&dest, &ctx)) {
+                               goto parse_error;
+                       }
+                       if (next == '&') {
+                               ch = i_getch(input);
+                               nommu_addchr(&ctx.as_string, ch);
+                               done_pipe(&ctx, PIPE_AND);
                        } else {
-                               xmove_fd(nextin, 0);
+                               done_pipe(&ctx, PIPE_BG);
                        }
-                       xmove_fd(pipefds.wr, 1);
-                       if (pipefds.rd > 1)
-                               close(pipefds.rd);
-                       /* Like bash, explicit redirects override pipes,
-                        * and the pipe fd is available for dup'ing. */
-                       if (setup_redirects(command, NULL))
-                               _exit(1);
-
-                       /* Restore default handlers just prior to exec */
-                       /*signal(SIGCHLD, SIG_DFL); - so far we don't have any handlers */
-
-                       /* Stores to nommu_save list of env vars putenv'ed
-                        * (NOMMU, on MMU we don't need that) */
-                       /* cast away volatility... */
-                       pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded);
-                       /* pseudo_exec() does not return */
-               }
-
-               /* parent or error */
-#if ENABLE_HUSH_FAST
-               G.count_SIGCHLD++;
-//bb_error_msg("[%d] fork in run_pipe: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
+                       goto new_cmd;
+               case '|':
+                       if (done_word(&dest, &ctx)) {
+                               goto parse_error;
+                       }
+#if ENABLE_HUSH_CASE
+                       if (ctx.ctx_res_w == RES_MATCH)
+                               break; /* we are in case's "word | word)" */
 #endif
-               enable_restore_tty_pgrp_on_exit();
+                       if (next == '|') { /* || */
+                               ch = i_getch(input);
+                               nommu_addchr(&ctx.as_string, ch);
+                               done_pipe(&ctx, PIPE_OR);
+                       } else {
+                               /* we could pick up a file descriptor choice here
+                                * with redirect_opt_num(), but bash doesn't do it.
+                                * "echo foo 2| cat" yields "foo 2". */
+                               done_command(&ctx);
 #if !BB_MMU
-               /* Clean up after vforked child */
-               free(nommu_save.argv);
-               free(nommu_save.argv_from_re_execing);
-               unset_vars(nommu_save.new_env);
-               add_vars(nommu_save.old_vars);
+                               o_reset_to_empty_unquoted(&ctx.as_string);
 #endif
-               free(argv_expanded);
-               argv_expanded = NULL;
-               if (command->pid < 0) { /* [v]fork failed */
-                       /* Clearly indicate, was it fork or vfork */
-                       bb_perror_msg(BB_MMU ? "vfork"+1 : "vfork");
-               } else {
-                       pi->alive_cmds++;
-#if ENABLE_HUSH_JOB
-                       /* Second and next children need to know pid of first one */
-                       if (pi->pgrp < 0)
-                               pi->pgrp = command->pid;
+                       }
+                       goto new_cmd;
+               case '(':
+#if ENABLE_HUSH_CASE
+                       /* "case... in [(]word)..." - skip '(' */
+                       if (ctx.ctx_res_w == RES_MATCH
+                        && ctx.command->argv == NULL /* not (word|(... */
+                        && dest.length == 0 /* not word(... */
+                        && dest.has_quoted_part == 0 /* not ""(... */
+                       ) {
+                               continue;
+                       }
+#endif
+               case '{':
+                       if (parse_group(&dest, &ctx, input, ch) != 0) {
+                               goto parse_error;
+                       }
+                       goto new_cmd;
+               case ')':
+#if ENABLE_HUSH_CASE
+                       if (ctx.ctx_res_w == RES_MATCH)
+                               goto case_semi;
 #endif
+               case '}':
+                       /* proper use of this character is caught by end_trigger:
+                        * if we see {, we call parse_group(..., end_trigger='}')
+                        * and it will match } earlier (not here). */
+                       syntax_error_unexpected_ch(ch);
+                       goto parse_error;
+               default:
+                       if (HUSH_DEBUG)
+                               bb_error_msg_and_die("BUG: unexpected %c\n", ch);
                }
+       } /* while (1) */
 
-               if (i)
-                       close(nextin);
-               if ((i + 1) < pi->num_cmds)
-                       close(pipefds.wr);
-               /* Pass read (output) pipe end to next iteration */
-               nextin = pipefds.rd;
-       }
+ parse_error:
+       {
+               struct parse_context *pctx;
+               IF_HAS_KEYWORDS(struct parse_context *p2;)
 
-       if (!pi->alive_cmds) {
+               /* Clean up allocated tree.
+                * Sample for finding leaks on syntax error recovery path.
+                * Run it from interactive shell, watch pmap `pidof hush`.
+                * while if false; then false; fi; do break; fi
+                * Samples to catch leaks at execution:
+                * while if (true | {true;}); then echo ok; fi; do break; done
+                * while if (true | {true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done
+                */
+               pctx = &ctx;
+               do {
+                       /* Update pipe/command counts,
+                        * otherwise freeing may miss some */
+                       done_pipe(pctx, PIPE_SEQ);
+                       debug_printf_clean("freeing list %p from ctx %p\n",
+                                       pctx->list_head, pctx);
+                       debug_print_tree(pctx->list_head, 0);
+                       free_pipe_list(pctx->list_head);
+                       debug_printf_clean("freed list %p\n", pctx->list_head);
+#if !BB_MMU
+                       o_free_unsafe(&pctx->as_string);
+#endif
+                       IF_HAS_KEYWORDS(p2 = pctx->stack;)
+                       if (pctx != &ctx) {
+                               free(pctx);
+                       }
+                       IF_HAS_KEYWORDS(pctx = p2;)
+               } while (HAS_KEYWORDS && pctx);
+
+               o_free(&dest);
+               G.last_exitcode = 1;
+#if !BB_MMU
+               if (pstring)
+                       *pstring = NULL;
+#endif
                debug_leave();
-               debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n");
-               return 1;
+               return ERR_PTR;
        }
-
-       debug_leave();
-       debug_printf_exec("run_pipe return -1 (%u children started)\n", pi->alive_cmds);
-       return -1;
 }
 
-#ifndef debug_print_tree
-static void debug_print_tree(struct pipe *pi, int lvl)
-{
-       static const char *const PIPE[] = {
-               [PIPE_SEQ] = "SEQ",
-               [PIPE_AND] = "AND",
-               [PIPE_OR ] = "OR" ,
-               [PIPE_BG ] = "BG" ,
-       };
-       static const char *RES[] = {
-               [RES_NONE ] = "NONE" ,
-# if ENABLE_HUSH_IF
-               [RES_IF   ] = "IF"   ,
-               [RES_THEN ] = "THEN" ,
-               [RES_ELIF ] = "ELIF" ,
-               [RES_ELSE ] = "ELSE" ,
-               [RES_FI   ] = "FI"   ,
-# endif
-# if ENABLE_HUSH_LOOPS
-               [RES_FOR  ] = "FOR"  ,
-               [RES_WHILE] = "WHILE",
-               [RES_UNTIL] = "UNTIL",
-               [RES_DO   ] = "DO"   ,
-               [RES_DONE ] = "DONE" ,
-# endif
-# if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
-               [RES_IN   ] = "IN"   ,
-# endif
-# if ENABLE_HUSH_CASE
-               [RES_CASE ] = "CASE" ,
-               [RES_CASE_IN ] = "CASE_IN" ,
-               [RES_MATCH] = "MATCH",
-               [RES_CASE_BODY] = "CASE_BODY",
-               [RES_ESAC ] = "ESAC" ,
-# endif
-               [RES_XXXX ] = "XXXX" ,
-               [RES_SNTX ] = "SNTX" ,
-       };
-       static const char *const CMDTYPE[] = {
-               "{}",
-               "()",
-               "[noglob]",
-# if ENABLE_HUSH_FUNCTIONS
-               "func()",
-# endif
-       };
 
-       int pin, prn;
+/*** Execution routines ***/
+
+/* Expansion can recurse, need forward decls: */
+#if !ENABLE_HUSH_BASH_COMPAT
+/* only ${var/pattern/repl} (its pattern part) needs additional mode */
+#define expand_string_to_string(str, do_unbackslash) \
+       expand_string_to_string(str)
+#endif
+static char *expand_string_to_string(const char *str, int do_unbackslash);
+#if ENABLE_HUSH_TICK
+static int process_command_subs(o_string *dest, const char *s);
+#endif
+
+/* expand_strvec_to_strvec() takes a list of strings, expands
+ * all variable references within and returns a pointer to
+ * a list of expanded strings, possibly with larger number
+ * of strings. (Think VAR="a b"; echo $VAR).
+ * This new list is allocated as a single malloc block.
+ * NULL-terminated list of char* pointers is at the beginning of it,
+ * followed by strings themselves.
+ * Caller can deallocate entire list by single free(list). */
 
-       pin = 0;
-       while (pi) {
-               fprintf(stderr, "%*spipe %d res_word=%s followup=%d %s\n", lvl*2, "",
-                               pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]);
-               prn = 0;
-               while (prn < pi->num_cmds) {
-                       struct command *command = &pi->cmds[prn];
-                       char **argv = command->argv;
+/* A horde of its helpers come first: */
 
-                       fprintf(stderr, "%*s cmd %d assignment_cnt:%d",
-                                       lvl*2, "", prn,
-                                       command->assignment_cnt);
-                       if (command->group) {
-                               fprintf(stderr, " group %s: (argv=%p)%s%s\n",
-                                               CMDTYPE[command->cmd_type],
-                                               argv
-# if !BB_MMU
-                                               , " group_as_string:", command->group_as_string
-# else
-                                               , "", ""
-# endif
-                               );
-                               debug_print_tree(command->group, lvl+1);
-                               prn++;
-                               continue;
-                       }
-                       if (argv) while (*argv) {
-                               fprintf(stderr, " '%s'", *argv);
-                               argv++;
+static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len)
+{
+       while (--len >= 0) {
+               char c = *str++;
+
+#if ENABLE_HUSH_BRACE_EXPANSION
+               if (c == '{' || c == '}') {
+                       /* { -> \{, } -> \} */
+                       o_addchr(o, '\\');
+                       /* And now we want to add { or } and continue:
+                        *  o_addchr(o, c);
+                        *  continue;
+                        * luckily, just falling throught achieves this.
+                        */
+               }
+#endif
+               o_addchr(o, c);
+               if (c == '\\') {
+                       /* \z -> \\\z; \<eol> -> \\<eol> */
+                       o_addchr(o, '\\');
+                       if (len) {
+                               len--;
+                               o_addchr(o, '\\');
+                               o_addchr(o, *str++);
                        }
-                       fprintf(stderr, "\n");
-                       prn++;
                }
-               pi = pi->next;
-               pin++;
        }
 }
-#endif /* debug_print_tree */
 
-/* NB: called by pseudo_exec, and therefore must not modify any
- * global data until exec/_exit (we can be a child after vfork!) */
-static int run_list(struct pipe *pi)
+/* Store given string, finalizing the word and starting new one whenever
+ * we encounter IFS char(s). This is used for expanding variable values.
+ * End-of-string does NOT finalize word: think about 'echo -$VAR-'.
+ * Return in *ended_with_ifs:
+ * 1 - ended with IFS char, else 0 (this includes case of empty str).
+ */
+static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const char *str)
 {
-#if ENABLE_HUSH_CASE
-       char *case_word = NULL;
-#endif
-#if ENABLE_HUSH_LOOPS
-       struct pipe *loop_top = NULL;
-       char **for_lcur = NULL;
-       char **for_list = NULL;
-#endif
-       smallint last_followup;
-       smalluint rcode;
-#if ENABLE_HUSH_IF || ENABLE_HUSH_CASE
-       smalluint cond_code = 0;
-#else
-       enum { cond_code = 0 };
-#endif
-#if HAS_KEYWORDS
-       smallint rword; /* enum reserved_style */
-       smallint last_rword; /* ditto */
-#endif
+       int last_is_ifs = 0;
 
-       debug_printf_exec("run_list start lvl %d\n", G.run_list_level);
-       debug_enter();
+       while (1) {
+               int word_len;
 
-#if ENABLE_HUSH_LOOPS
-       /* Check syntax for "for" */
-       for (struct pipe *cpipe = pi; cpipe; cpipe = cpipe->next) {
-               if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN)
-                       continue;
-               /* current word is FOR or IN (BOLD in comments below) */
-               if (cpipe->next == NULL) {
-                       syntax_error("malformed for");
-                       debug_leave();
-                       debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
-                       return 1;
+               if (!*str)  /* EOL - do not finalize word */
+                       break;
+               word_len = strcspn(str, G.ifs);
+               if (word_len) {
+                       /* We have WORD_LEN leading non-IFS chars */
+                       if (!(output->o_expflags & EXP_FLAG_GLOB)) {
+                               o_addblock(output, str, word_len);
+                       } else {
+                               /* Protect backslashes against globbing up :)
+                                * Example: "v='\*'; echo b$v" prints "b\*"
+                                * (and does not try to glob on "*")
+                                */
+                               o_addblock_duplicate_backslash(output, str, word_len);
+                               /*/ Why can't we do it easier? */
+                               /*o_addblock(output, str, word_len); - WRONG: "v='\*'; echo Z$v" prints "Z*" instead of "Z\*" */
+                               /*o_addqblock(output, str, word_len); - WRONG: "v='*'; echo Z$v" prints "Z*" instead of Z* files */
+                       }
+                       last_is_ifs = 0;
+                       str += word_len;
+                       if (!*str)  /* EOL - do not finalize word */
+                               break;
                }
-               /* "FOR v; do ..." and "for v IN a b; do..." are ok */
-               if (cpipe->next->res_word == RES_DO)
-                       continue;
-               /* next word is not "do". It must be "in" then ("FOR v in ...") */
-               if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */
-                || cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */
+
+               /* We know str here points to at least one IFS char */
+               last_is_ifs = 1;
+               str += strspn(str, G.ifs); /* skip IFS chars */
+               if (!*str)  /* EOL - do not finalize word */
+                       break;
+
+               /* Start new word... but not always! */
+               /* Case "v=' a'; echo ''$v": we do need to finalize empty word: */
+               if (output->has_quoted_part
+               /* Case "v=' a'; echo $v":
+                * here nothing precedes the space in $v expansion,
+                * therefore we should not finish the word
+                * (IOW: if there *is* word to finalize, only then do it):
+                */
+                || (n > 0 && output->data[output->length - 1])
                ) {
-                       syntax_error("malformed for");
-                       debug_leave();
-                       debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
-                       return 1;
+                       o_addchr(output, '\0');
+                       debug_print_list("expand_on_ifs", output, n);
+                       n = o_save_ptr(output, n);
                }
        }
-#endif
 
-       /* Past this point, all code paths should jump to ret: label
-        * in order to return, no direct "return" statements please.
-        * This helps to ensure that no memory is leaked. */
+       if (ended_with_ifs)
+               *ended_with_ifs = last_is_ifs;
+       debug_print_list("expand_on_ifs[1]", output, n);
+       return n;
+}
 
-#if ENABLE_HUSH_JOB
-       G.run_list_level++;
+/* Helper to expand $((...)) and heredoc body. These act as if
+ * they are in double quotes, with the exception that they are not :).
+ * Just the rules are similar: "expand only $var and `cmd`"
+ *
+ * Returns malloced string.
+ * As an optimization, we return NULL if expansion is not needed.
+ */
+#if !ENABLE_HUSH_BASH_COMPAT
+/* only ${var/pattern/repl} (its pattern part) needs additional mode */
+#define encode_then_expand_string(str, process_bkslash, do_unbackslash) \
+       encode_then_expand_string(str)
 #endif
+static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash)
+{
+       char *exp_str;
+       struct in_str input;
+       o_string dest = NULL_O_STRING;
 
-#if HAS_KEYWORDS
-       rword = RES_NONE;
-       last_rword = RES_XXXX;
+       if (!strchr(str, '$')
+        && !strchr(str, '\\')
+#if ENABLE_HUSH_TICK
+        && !strchr(str, '`')
 #endif
-       last_followup = PIPE_SEQ;
-       rcode = G.last_exitcode;
+       ) {
+               return NULL;
+       }
 
-       /* Go through list of pipes, (maybe) executing them. */
-       for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) {
-               if (G.flag_SIGINT)
-                       break;
+       /* We need to expand. Example:
+        * echo $(($a + `echo 1`)) $((1 + $((2)) ))
+        */
+       setup_string_in_str(&input, str);
+       encode_string(NULL, &dest, &input, EOF, process_bkslash);
+//TODO: error check (encode_string returns 0 on error)?
+       //bb_error_msg("'%s' -> '%s'", str, dest.data);
+       exp_str = expand_string_to_string(dest.data, /*unbackslash:*/ do_unbackslash);
+       //bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
+       o_free_unsafe(&dest);
+       return exp_str;
+}
 
-               IF_HAS_KEYWORDS(rword = pi->res_word;)
-               debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n",
-                               rword, cond_code, last_rword);
-#if ENABLE_HUSH_LOOPS
-               if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR)
-                && loop_top == NULL /* avoid bumping G.depth_of_loop twice */
-               ) {
-                       /* start of a loop: remember where loop starts */
-                       loop_top = pi;
-                       G.depth_of_loop++;
-               }
+#if ENABLE_SH_MATH_SUPPORT
+static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
+{
+       arith_state_t math_state;
+       arith_t res;
+       char *exp_str;
+
+       math_state.lookupvar = get_local_var_value;
+       math_state.setvar = set_local_var_from_halves;
+       //math_state.endofname = endofname;
+       exp_str = encode_then_expand_string(arg, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
+       res = arith(&math_state, exp_str ? exp_str : arg);
+       free(exp_str);
+       if (errmsg_p)
+               *errmsg_p = math_state.errmsg;
+       if (math_state.errmsg)
+               die_if_script(math_state.errmsg);
+       return res;
+}
 #endif
-               /* Still in the same "if...", "then..." or "do..." branch? */
-               if (IF_HAS_KEYWORDS(rword == last_rword &&) 1) {
-                       if ((rcode == 0 && last_followup == PIPE_OR)
-                        || (rcode != 0 && last_followup == PIPE_AND)
-                       ) {
-                               /* It is "<true> || CMD" or "<false> && CMD"
-                                * and we should not execute CMD */
-                               debug_printf_exec("skipped cmd because of || or &&\n");
-                               last_followup = pi->followup;
-                               continue;
-                       }
-               }
-               last_followup = pi->followup;
-               IF_HAS_KEYWORDS(last_rword = rword;)
-#if ENABLE_HUSH_IF
-               if (cond_code) {
-                       if (rword == RES_THEN) {
-                               /* if false; then ... fi has exitcode 0! */
-                               G.last_exitcode = rcode = EXIT_SUCCESS;
-                               /* "if <false> THEN cmd": skip cmd */
-                               continue;
-                       }
-               } else {
-                       if (rword == RES_ELSE || rword == RES_ELIF) {
-                               /* "if <true> then ... ELSE/ELIF cmd":
-                                * skip cmd and all following ones */
-                               break;
-                       }
+
+#if ENABLE_HUSH_BASH_COMPAT
+/* ${var/[/]pattern[/repl]} helpers */
+static char *strstr_pattern(char *val, const char *pattern, int *size)
+{
+       while (1) {
+               char *end = scan_and_match(val, pattern, SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF);
+               debug_printf_varexp("val:'%s' pattern:'%s' end:'%s'\n", val, pattern, end);
+               if (end) {
+                       *size = end - val;
+                       return val;
                }
-#endif
-#if ENABLE_HUSH_LOOPS
-               if (rword == RES_FOR) { /* && pi->num_cmds - always == 1 */
-                       if (!for_lcur) {
-                               /* first loop through for */
+               if (*val == '\0')
+                       return NULL;
+               /* Optimization: if "*pat" did not match the start of "string",
+                * we know that "tring", "ring" etc will not match too:
+                */
+               if (pattern[0] == '*')
+                       return NULL;
+               val++;
+       }
+}
+static char *replace_pattern(char *val, const char *pattern, const char *repl, char exp_op)
+{
+       char *result = NULL;
+       unsigned res_len = 0;
+       unsigned repl_len = strlen(repl);
 
-                               static const char encoded_dollar_at[] ALIGN1 = {
-                                       SPECIAL_VAR_SYMBOL, '@' | 0x80, SPECIAL_VAR_SYMBOL, '\0'
-                               }; /* encoded representation of "$@" */
-                               static const char *const encoded_dollar_at_argv[] = {
-                                       encoded_dollar_at, NULL
-                               }; /* argv list with one element: "$@" */
-                               char **vals;
+       while (1) {
+               int size;
+               char *s = strstr_pattern(val, pattern, &size);
+               if (!s)
+                       break;
 
-                               vals = (char**)encoded_dollar_at_argv;
-                               if (pi->next->res_word == RES_IN) {
-                                       /* if no variable values after "in" we skip "for" */
-                                       if (!pi->next->cmds[0].argv) {
-                                               G.last_exitcode = rcode = EXIT_SUCCESS;
-                                               debug_printf_exec(": null FOR: exitcode EXIT_SUCCESS\n");
-                                               break;
-                                       }
-                                       vals = pi->next->cmds[0].argv;
-                               } /* else: "for var; do..." -> assume "$@" list */
-                               /* create list of variable values */
-                               debug_print_strings("for_list made from", vals);
-                               for_list = expand_strvec_to_strvec(vals);
-                               for_lcur = for_list;
-                               debug_print_strings("for_list", for_list);
-                       }
-                       if (!*for_lcur) {
-                               /* "for" loop is over, clean up */
-                               free(for_list);
-                               for_list = NULL;
-                               for_lcur = NULL;
-                               break;
-                       }
-                       /* Insert next value from for_lcur */
-                       /* note: *for_lcur already has quotes removed, $var expanded, etc */
-                       set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], *for_lcur++), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
-                       continue;
-               }
-               if (rword == RES_IN) {
-                       continue; /* "for v IN list;..." - "in" has no cmds anyway */
-               }
-               if (rword == RES_DONE) {
-                       continue; /* "done" has no cmds too */
-               }
+               result = xrealloc(result, res_len + (s - val) + repl_len + 1);
+               memcpy(result + res_len, val, s - val);
+               res_len += s - val;
+               strcpy(result + res_len, repl);
+               res_len += repl_len;
+               debug_printf_varexp("val:'%s' s:'%s' result:'%s'\n", val, s, result);
+
+               val = s + size;
+               if (exp_op == '/')
+                       break;
+       }
+       if (val[0] && result) {
+               result = xrealloc(result, res_len + strlen(val) + 1);
+               strcpy(result + res_len, val);
+               debug_printf_varexp("val:'%s' result:'%s'\n", val, result);
+       }
+       debug_printf_varexp("result:'%s'\n", result);
+       return result;
+}
 #endif
-#if ENABLE_HUSH_CASE
-               if (rword == RES_CASE) {
-                       case_word = expand_strvec_to_string(pi->cmds->argv);
-                       continue;
-               }
-               if (rword == RES_MATCH) {
-                       char **argv;
 
-                       if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */
-                               break;
-                       /* all prev words didn't match, does this one match? */
-                       argv = pi->cmds->argv;
-                       while (*argv) {
-                               char *pattern = expand_string_to_string(*argv);
-                               /* TODO: which FNM_xxx flags to use? */
-                               cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0);
-                               free(pattern);
-                               if (cond_code == 0) { /* match! we will execute this branch */
-                                       free(case_word); /* make future "word)" stop */
-                                       case_word = NULL;
-                                       break;
+/* Helper:
+ * Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
+ */
+static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp)
+{
+       const char *val = NULL;
+       char *to_be_freed = NULL;
+       char *p = *pp;
+       char *var;
+       char first_char;
+       char exp_op;
+       char exp_save = exp_save; /* for compiler */
+       char *exp_saveptr; /* points to expansion operator */
+       char *exp_word = exp_word; /* for compiler */
+       char arg0;
+
+       *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */
+       var = arg;
+       exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL;
+       arg0 = arg[0];
+       first_char = arg[0] = arg0 & 0x7f;
+       exp_op = 0;
+
+       if (first_char == '#'      /* ${#... */
+        && arg[1] && !exp_saveptr /* not ${#} and not ${#<op_char>...} */
+       ) {
+               /* It must be length operator: ${#var} */
+               var++;
+               exp_op = 'L';
+       } else {
+               /* Maybe handle parameter expansion */
+               if (exp_saveptr /* if 2nd char is one of expansion operators */
+                && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */
+               ) {
+                       /* ${?:0}, ${#[:]%0} etc */
+                       exp_saveptr = var + 1;
+               } else {
+                       /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */
+                       exp_saveptr = var+1 + strcspn(var+1, VAR_ENCODED_SUBST_OPS);
+               }
+               exp_op = exp_save = *exp_saveptr;
+               if (exp_op) {
+                       exp_word = exp_saveptr + 1;
+                       if (exp_op == ':') {
+                               exp_op = *exp_word++;
+//TODO: try ${var:} and ${var:bogus} in non-bash config
+                               if (ENABLE_HUSH_BASH_COMPAT
+                                && (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op))
+                               ) {
+                                       /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
+                                       exp_op = ':';
+                                       exp_word--;
+                               }
+                       }
+                       *exp_saveptr = '\0';
+               } /* else: it's not an expansion op, but bare ${var} */
+       }
+
+       /* Look up the variable in question */
+       if (isdigit(var[0])) {
+               /* parse_dollar should have vetted var for us */
+               int n = xatoi_positive(var);
+               if (n < G.global_argc)
+                       val = G.global_argv[n];
+               /* else val remains NULL: $N with too big N */
+       } else {
+               switch (var[0]) {
+               case '$': /* pid */
+                       val = utoa(G.root_pid);
+                       break;
+               case '!': /* bg pid */
+                       val = G.last_bg_pid ? utoa(G.last_bg_pid) : "";
+                       break;
+               case '?': /* exitcode */
+                       val = utoa(G.last_exitcode);
+                       break;
+               case '#': /* argc */
+                       val = utoa(G.global_argc ? G.global_argc-1 : 0);
+                       break;
+               default:
+                       val = get_local_var_value(var);
+               }
+       }
+
+       /* Handle any expansions */
+       if (exp_op == 'L') {
+               debug_printf_expand("expand: length(%s)=", val);
+               val = utoa(val ? strlen(val) : 0);
+               debug_printf_expand("%s\n", val);
+       } else if (exp_op) {
+               if (exp_op == '%' || exp_op == '#') {
+                       /* Standard-mandated substring removal ops:
+                        * ${parameter%word} - remove smallest suffix pattern
+                        * ${parameter%%word} - remove largest suffix pattern
+                        * ${parameter#word} - remove smallest prefix pattern
+                        * ${parameter##word} - remove largest prefix pattern
+                        *
+                        * Word is expanded to produce a glob pattern.
+                        * Then var's value is matched to it and matching part removed.
+                        */
+                       if (val && val[0]) {
+                               char *t;
+                               char *exp_exp_word;
+                               char *loc;
+                               unsigned scan_flags = pick_scan(exp_op, *exp_word);
+                               if (exp_op == *exp_word)  /* ## or %% */
+                                       exp_word++;
+                               exp_exp_word = encode_then_expand_string(exp_word, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
+                               if (exp_exp_word)
+                                       exp_word = exp_exp_word;
+                               /* HACK ALERT. We depend here on the fact that
+                                * G.global_argv and results of utoa and get_local_var_value
+                                * are actually in writable memory:
+                                * scan_and_match momentarily stores NULs there. */
+                               t = (char*)val;
+                               loc = scan_and_match(t, exp_word, scan_flags);
+                               //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'",
+                               //              exp_op, t, exp_word, loc);
+                               free(exp_exp_word);
+                               if (loc) { /* match was found */
+                                       if (scan_flags & SCAN_MATCH_LEFT_HALF) /* #[#] */
+                                               val = loc; /* take right part */
+                                       else /* %[%] */
+                                               val = to_be_freed = xstrndup(val, loc - val); /* left */
                                }
-                               argv++;
                        }
-                       continue;
-               }
-               if (rword == RES_CASE_BODY) { /* inside of a case branch */
-                       if (cond_code != 0)
-                               continue; /* not matched yet, skip this pipe */
                }
-#endif
-               /* Just pressing <enter> in shell should check for jobs.
-                * OTOH, in non-interactive shell this is useless
-                * and only leads to extra job checks */
-               if (pi->num_cmds == 0) {
-                       if (G_interactive_fd)
-                               goto check_jobs_and_continue;
-                       continue;
+#if ENABLE_HUSH_BASH_COMPAT
+               else if (exp_op == '/' || exp_op == '\\') {
+                       /* It's ${var/[/]pattern[/repl]} thing.
+                        * Note that in encoded form it has TWO parts:
+                        * var/pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
+                        * and if // is used, it is encoded as \:
+                        * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
+                        */
+                       /* Empty variable always gives nothing: */
+                       // "v=''; echo ${v/*/w}" prints "", not "w"
+                       if (val && val[0]) {
+                               /* pattern uses non-standard expansion.
+                                * repl should be unbackslashed and globbed
+                                * by the usual expansion rules:
+                                * >az; >bz;
+                                * v='a bz'; echo "${v/a*z/a*z}" prints "a*z"
+                                * v='a bz'; echo "${v/a*z/\z}"  prints "\z"
+                                * v='a bz'; echo ${v/a*z/a*z}   prints "az"
+                                * v='a bz'; echo ${v/a*z/\z}    prints "z"
+                                * (note that a*z _pattern_ is never globbed!)
+                                */
+                               char *pattern, *repl, *t;
+                               pattern = encode_then_expand_string(exp_word, /*process_bkslash:*/ 0, /*unbackslash:*/ 0);
+                               if (!pattern)
+                                       pattern = xstrdup(exp_word);
+                               debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern);
+                               *p++ = SPECIAL_VAR_SYMBOL;
+                               exp_word = p;
+                               p = strchr(p, SPECIAL_VAR_SYMBOL);
+                               *p = '\0';
+                               repl = encode_then_expand_string(exp_word, /*process_bkslash:*/ arg0 & 0x80, /*unbackslash:*/ 1);
+                               debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl);
+                               /* HACK ALERT. We depend here on the fact that
+                                * G.global_argv and results of utoa and get_local_var_value
+                                * are actually in writable memory:
+                                * replace_pattern momentarily stores NULs there. */
+                               t = (char*)val;
+                               to_be_freed = replace_pattern(t,
+                                               pattern,
+                                               (repl ? repl : exp_word),
+                                               exp_op);
+                               if (to_be_freed) /* at least one replace happened */
+                                       val = to_be_freed;
+                               free(pattern);
+                               free(repl);
+                       }
                }
-
-               /* After analyzing all keywords and conditions, we decided
-                * to execute this pipe. NB: have to do checkjobs(NULL)
-                * after run_pipe to collect any background children,
-                * even if list execution is to be stopped. */
-               debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds);
-               {
-                       int r;
-#if ENABLE_HUSH_LOOPS
-                       G.flag_break_continue = 0;
 #endif
-                       rcode = r = run_pipe(pi); /* NB: rcode is a smallint */
-                       if (r != -1) {
-                               /* We ran a builtin, function, or group.
-                                * rcode is already known
-                                * and we don't need to wait for anything. */
-                               G.last_exitcode = rcode;
-                               debug_printf_exec(": builtin/func exitcode %d\n", rcode);
-                               check_and_run_traps(0);
-#if ENABLE_HUSH_LOOPS
-                               /* Was it "break" or "continue"? */
-                               if (G.flag_break_continue) {
-                                       smallint fbc = G.flag_break_continue;
-                                       /* We might fall into outer *loop*,
-                                        * don't want to break it too */
-                                       if (loop_top) {
-                                               G.depth_break_continue--;
-                                               if (G.depth_break_continue == 0)
-                                                       G.flag_break_continue = 0;
-                                               /* else: e.g. "continue 2" should *break* once, *then* continue */
-                                       } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
-                                       if (G.depth_break_continue != 0 || fbc == BC_BREAK)
-                                               goto check_jobs_and_break;
-                                       /* "continue": simulate end of loop */
-                                       rword = RES_DONE;
-                                       continue;
+               else if (exp_op == ':') {
+#if ENABLE_HUSH_BASH_COMPAT && ENABLE_SH_MATH_SUPPORT
+                       /* It's ${var:N[:M]} bashism.
+                        * Note that in encoded form it has TWO parts:
+                        * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
+                        */
+                       arith_t beg, len;
+                       const char *errmsg;
+
+                       beg = expand_and_evaluate_arith(exp_word, &errmsg);
+                       if (errmsg)
+                               goto arith_err;
+                       debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
+                       *p++ = SPECIAL_VAR_SYMBOL;
+                       exp_word = p;
+                       p = strchr(p, SPECIAL_VAR_SYMBOL);
+                       *p = '\0';
+                       len = expand_and_evaluate_arith(exp_word, &errmsg);
+                       if (errmsg)
+                               goto arith_err;
+                       debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
+                       if (len >= 0) { /* bash compat: len < 0 is illegal */
+                               if (beg < 0) /* bash compat */
+                                       beg = 0;
+                               debug_printf_varexp("from val:'%s'\n", val);
+                               if (len == 0 || !val || beg >= strlen(val)) {
+ arith_err:
+                                       val = NULL;
+                               } else {
+                                       /* Paranoia. What if user entered 9999999999999
+                                        * which fits in arith_t but not int? */
+                                       if (len >= INT_MAX)
+                                               len = INT_MAX;
+                                       val = to_be_freed = xstrndup(val + beg, len);
                                }
+                               debug_printf_varexp("val:'%s'\n", val);
+                       } else
 #endif
-#if ENABLE_HUSH_FUNCTIONS
-                               if (G.flag_return_in_progress == 1) {
-                                       /* same as "goto check_jobs_and_break" */
-                                       checkjobs(NULL);
-                                       break;
+                       {
+                               die_if_script("malformed ${%s:...}", var);
+                               val = NULL;
+                       }
+               } else { /* one of "-=+?" */
+                       /* Standard-mandated substitution ops:
+                        * ${var?word} - indicate error if unset
+                        *      If var is unset, word (or a message indicating it is unset
+                        *      if word is null) is written to standard error
+                        *      and the shell exits with a non-zero exit status.
+                        *      Otherwise, the value of var is substituted.
+                        * ${var-word} - use default value
+                        *      If var is unset, word is substituted.
+                        * ${var=word} - assign and use default value
+                        *      If var is unset, word is assigned to var.
+                        *      In all cases, final value of var is substituted.
+                        * ${var+word} - use alternative value
+                        *      If var is unset, null is substituted.
+                        *      Otherwise, word is substituted.
+                        *
+                        * Word is subjected to tilde expansion, parameter expansion,
+                        * command substitution, and arithmetic expansion.
+                        * If word is not needed, it is not expanded.
+                        *
+                        * Colon forms (${var:-word}, ${var:=word} etc) do the same,
+                        * but also treat null var as if it is unset.
+                        */
+                       int use_word = (!val || ((exp_save == ':') && !val[0]));
+                       if (exp_op == '+')
+                               use_word = !use_word;
+                       debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
+                                       (exp_save == ':') ? "true" : "false", use_word);
+                       if (use_word) {
+                               to_be_freed = encode_then_expand_string(exp_word, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
+                               if (to_be_freed)
+                                       exp_word = to_be_freed;
+                               if (exp_op == '?') {
+                                       /* mimic bash message */
+                                       die_if_script("%s: %s",
+                                               var,
+                                               exp_word[0] ? exp_word : "parameter null or not set"
+                                       );
+//TODO: how interactive bash aborts expansion mid-command?
+                               } else {
+                                       val = exp_word;
                                }
-#endif
-                       } else if (pi->followup == PIPE_BG) {
-                               /* What does bash do with attempts to background builtins? */
-                               /* even bash 3.2 doesn't do that well with nested bg:
-                                * try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
-                                * I'm NOT treating inner &'s as jobs */
-                               check_and_run_traps(0);
-#if ENABLE_HUSH_JOB
-                               if (G.run_list_level == 1)
-                                       insert_bg_job(pi);
-#endif
-                               G.last_exitcode = rcode = EXIT_SUCCESS;
-                               debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n");
-                       } else {
-#if ENABLE_HUSH_JOB
-                               if (G.run_list_level == 1 && G_interactive_fd) {
-                                       /* Waits for completion, then fg's main shell */
-                                       rcode = checkjobs_and_fg_shell(pi);
-                                       debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode);
-                                       check_and_run_traps(0);
-                               } else
-#endif
-                               { /* This one just waits for completion */
-                                       rcode = checkjobs(pi);
-                                       debug_printf_exec(": checkjobs exitcode %d\n", rcode);
-                                       check_and_run_traps(0);
+
+                               if (exp_op == '=') {
+                                       /* ${var=[word]} or ${var:=[word]} */
+                                       if (isdigit(var[0]) || var[0] == '#') {
+                                               /* mimic bash message */
+                                               die_if_script("$%s: cannot assign in this way", var);
+                                               val = NULL;
+                                       } else {
+                                               char *new_var = xasprintf("%s=%s", var, val);
+                                               set_local_var(new_var, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
+                                       }
                                }
-                               G.last_exitcode = rcode;
                        }
-               }
+               } /* one of "-=+?" */
 
-               /* Analyze how result affects subsequent commands */
-#if ENABLE_HUSH_IF
-               if (rword == RES_IF || rword == RES_ELIF)
-                       cond_code = rcode;
+               *exp_saveptr = exp_save;
+       } /* if (exp_op) */
+
+       arg[0] = arg0;
+
+       *pp = p;
+       *to_be_freed_pp = to_be_freed;
+       return val;
+}
+
+/* Expand all variable references in given string, adding words to list[]
+ * at n, n+1,... positions. Return updated n (so that list[n] is next one
+ * to be filled). This routine is extremely tricky: has to deal with
+ * variables/parameters with whitespace, $* and $@, and constructs like
+ * 'echo -$*-'. If you play here, you must run testsuite afterwards! */
+static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
+{
+       /* output->o_expflags & EXP_FLAG_SINGLEWORD (0x80) if we are in
+        * expansion of right-hand side of assignment == 1-element expand.
+        */
+       char cant_be_null = 0; /* only bit 0x80 matters */
+       int ended_in_ifs = 0;  /* did last unquoted expansion end with IFS chars? */
+       char *p;
+
+       debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg,
+                       !!(output->o_expflags & EXP_FLAG_SINGLEWORD));
+       debug_print_list("expand_vars_to_list", output, n);
+       n = o_save_ptr(output, n);
+       debug_print_list("expand_vars_to_list[0]", output, n);
+
+       while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) {
+               char first_ch;
+               char *to_be_freed = NULL;
+               const char *val = NULL;
+#if ENABLE_HUSH_TICK
+               o_string subst_result = NULL_O_STRING;
 #endif
-#if ENABLE_HUSH_LOOPS
-               /* Beware of "while false; true; do ..."! */
-               if (pi->next && pi->next->res_word == RES_DO) {
-                       if (rword == RES_WHILE) {
-                               if (rcode) {
-                                       /* "while false; do...done" - exitcode 0 */
-                                       G.last_exitcode = rcode = EXIT_SUCCESS;
-                                       debug_printf_exec(": while expr is false: breaking (exitcode:EXIT_SUCCESS)\n");
-                                       goto check_jobs_and_break;
+#if ENABLE_SH_MATH_SUPPORT
+               char arith_buf[sizeof(arith_t)*3 + 2];
+#endif
+
+               if (ended_in_ifs) {
+                       o_addchr(output, '\0');
+                       n = o_save_ptr(output, n);
+                       ended_in_ifs = 0;
+               }
+
+               o_addblock(output, arg, p - arg);
+               debug_print_list("expand_vars_to_list[1]", output, n);
+               arg = ++p;
+               p = strchr(p, SPECIAL_VAR_SYMBOL);
+
+               /* Fetch special var name (if it is indeed one of them)
+                * and quote bit, force the bit on if singleword expansion -
+                * important for not getting v=$@ expand to many words. */
+               first_ch = arg[0] | (output->o_expflags & EXP_FLAG_SINGLEWORD);
+
+               /* Is this variable quoted and thus expansion can't be null?
+                * "$@" is special. Even if quoted, it can still
+                * expand to nothing (not even an empty string),
+                * thus it is excluded. */
+               if ((first_ch & 0x7f) != '@')
+                       cant_be_null |= first_ch;
+
+               switch (first_ch & 0x7f) {
+               /* Highest bit in first_ch indicates that var is double-quoted */
+               case '*':
+               case '@': {
+                       int i;
+                       if (!G.global_argv[1])
+                               break;
+                       i = 1;
+                       cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
+                       if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
+                               while (G.global_argv[i]) {
+                                       n = expand_on_ifs(NULL, output, n, G.global_argv[i]);
+                                       debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1);
+                                       if (G.global_argv[i++][0] && G.global_argv[i]) {
+                                               /* this argv[] is not empty and not last:
+                                                * put terminating NUL, start new word */
+                                               o_addchr(output, '\0');
+                                               debug_print_list("expand_vars_to_list[2]", output, n);
+                                               n = o_save_ptr(output, n);
+                                               debug_print_list("expand_vars_to_list[3]", output, n);
+                                       }
                                }
-                       }
-                       if (rword == RES_UNTIL) {
-                               if (!rcode) {
-                                       debug_printf_exec(": until expr is true: breaking\n");
- check_jobs_and_break:
-                                       checkjobs(NULL);
-                                       break;
+                       } else
+                       /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....'
+                        * and in this case should treat it like '$*' - see 'else...' below */
+                       if (first_ch == ('@'|0x80)  /* quoted $@ */
+                        && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */
+                       ) {
+                               while (1) {
+                                       o_addQstr(output, G.global_argv[i]);
+                                       if (++i >= G.global_argc)
+                                               break;
+                                       o_addchr(output, '\0');
+                                       debug_print_list("expand_vars_to_list[4]", output, n);
+                                       n = o_save_ptr(output, n);
+                               }
+                       } else { /* quoted $* (or v="$@" case): add as one word */
+                               while (1) {
+                                       o_addQstr(output, G.global_argv[i]);
+                                       if (!G.global_argv[++i])
+                                               break;
+                                       if (G.ifs[0])
+                                               o_addchr(output, G.ifs[0]);
                                }
+                               output->has_quoted_part = 1;
                        }
+                       break;
                }
+               case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */
+                       /* "Empty variable", used to make "" etc to not disappear */
+                       output->has_quoted_part = 1;
+                       arg++;
+                       cant_be_null = 0x80;
+                       break;
+#if ENABLE_HUSH_TICK
+               case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */
+                       *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
+                       arg++;
+                       /* Can't just stuff it into output o_string,
+                        * expanded result may need to be globbed
+                        * and $IFS-splitted */
+                       debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch);
+                       G.last_exitcode = process_command_subs(&subst_result, arg);
+                       debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data);
+                       val = subst_result.data;
+                       goto store_val;
 #endif
+#if ENABLE_SH_MATH_SUPPORT
+               case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */
+                       arith_t res;
 
- check_jobs_and_continue:
-               checkjobs(NULL);
-       } /* for (pi) */
-
-#if ENABLE_HUSH_JOB
-       G.run_list_level--;
-#endif
-#if ENABLE_HUSH_LOOPS
-       if (loop_top)
-               G.depth_of_loop--;
-       free(for_list);
+                       arg++; /* skip '+' */
+                       *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
+                       debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch);
+                       res = expand_and_evaluate_arith(arg, NULL);
+                       debug_printf_subst("ARITH RES '"ARITH_FMT"'\n", res);
+                       sprintf(arith_buf, ARITH_FMT, res);
+                       val = arith_buf;
+                       break;
+               }
 #endif
-#if ENABLE_HUSH_CASE
-       free(case_word);
+               default:
+                       val = expand_one_var(&to_be_freed, arg, &p);
+ IF_HUSH_TICK(store_val:)
+                       if (!(first_ch & 0x80)) { /* unquoted $VAR */
+                               debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val,
+                                               !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
+                               if (val && val[0]) {
+                                       n = expand_on_ifs(&ended_in_ifs, output, n, val);
+                                       val = NULL;
+                               }
+                       } else { /* quoted $VAR, val will be appended below */
+                               output->has_quoted_part = 1;
+                               debug_printf_expand("quoted '%s', output->o_escape:%d\n", val,
+                                               !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
+                       }
+                       break;
+
+               } /* switch (char after <SPECIAL_VAR_SYMBOL>) */
+
+               if (val && val[0]) {
+                       o_addQstr(output, val);
+               }
+               free(to_be_freed);
+
+               /* Restore NULL'ed SPECIAL_VAR_SYMBOL.
+                * Do the check to avoid writing to a const string. */
+               if (*p != SPECIAL_VAR_SYMBOL)
+                       *p = SPECIAL_VAR_SYMBOL;
+
+#if ENABLE_HUSH_TICK
+               o_free(&subst_result);
 #endif
-       debug_leave();
-       debug_printf_exec("run_list lvl %d return %d\n", G.run_list_level + 1, rcode);
-       return rcode;
+               arg = ++p;
+       } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */
+
+       if (arg[0]) {
+               if (ended_in_ifs) {
+                       o_addchr(output, '\0');
+                       n = o_save_ptr(output, n);
+               }
+               debug_print_list("expand_vars_to_list[a]", output, n);
+               /* this part is literal, and it was already pre-quoted
+                * if needed (much earlier), do not use o_addQstr here! */
+               o_addstr_with_NUL(output, arg);
+               debug_print_list("expand_vars_to_list[b]", output, n);
+       } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */
+        && !(cant_be_null & 0x80) /* and all vars were not quoted. */
+       ) {
+               n--;
+               /* allow to reuse list[n] later without re-growth */
+               output->has_empty_slot = 1;
+       } else {
+               o_addchr(output, '\0');
+       }
+
+       return n;
 }
 
-/* Select which version we will use */
-static int run_and_free_list(struct pipe *pi)
+static char **expand_variables(char **argv, unsigned expflags)
 {
-       int rcode = 0;
-       debug_printf_exec("run_and_free_list entered\n");
-       if (!G.fake_mode) {
-               debug_printf_exec(": run_list: 1st pipe with %d cmds\n", pi->num_cmds);
-               rcode = run_list(pi);
+       int n;
+       char **list;
+       o_string output = NULL_O_STRING;
+
+       output.o_expflags = expflags;
+
+       n = 0;
+       while (*argv) {
+               n = expand_vars_to_list(&output, n, *argv);
+               argv++;
        }
-       /* free_pipe_list has the side effect of clearing memory.
-        * In the long run that function can be merged with run_list,
-        * but doing that now would hobble the debugging effort. */
-       free_pipe_list(pi);
-       debug_printf_exec("run_and_free_list return %d\n", rcode);
-       return rcode;
+       debug_print_list("expand_variables", &output, n);
+
+       /* output.data (malloced in one block) gets returned in "list" */
+       list = o_finalize_list(&output, n);
+       debug_print_strings("expand_variables[1]", list);
+       return list;
 }
 
+static char **expand_strvec_to_strvec(char **argv)
+{
+       return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS);
+}
 
-static struct pipe *new_pipe(void)
+#if ENABLE_HUSH_BASH_COMPAT
+static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
 {
-       struct pipe *pi;
-       pi = xzalloc(sizeof(struct pipe));
-       /*pi->followup = 0; - deliberately invalid value */
-       /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */
-       return pi;
+       return expand_variables(argv, EXP_FLAG_SINGLEWORD);
 }
+#endif
 
-/* Command (member of a pipe) is complete, or we start a new pipe
- * if ctx->command is NULL.
- * No errors possible here.
+/* Used for expansion of right hand of assignments,
+ * $((...)), heredocs, variable espansion parts.
+ *
+ * NB: should NOT do globbing!
+ * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*"
  */
-static int done_command(struct parse_context *ctx)
+static char *expand_string_to_string(const char *str, int do_unbackslash)
 {
-       /* The command is really already in the pipe structure, so
-        * advance the pipe counter and make a new, null command. */
-       struct pipe *pi = ctx->pipe;
-       struct command *command = ctx->command;
+#if !ENABLE_HUSH_BASH_COMPAT
+       const int do_unbackslash = 1;
+#endif
+       char *argv[2], **list;
 
-       if (command) {
-               if (IS_NULL_CMD(command)) {
-                       debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
-                       goto clear_and_ret;
-               }
-               pi->num_cmds++;
-               debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
-               //debug_print_tree(ctx->list_head, 20);
-       } else {
-               debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds);
+       debug_printf_expand("string_to_string<='%s'\n", str);
+       /* This is generally an optimization, but it also
+        * handles "", which otherwise trips over !list[0] check below.
+        * (is this ever happens that we actually get str="" here?)
+        */
+       if (!strchr(str, SPECIAL_VAR_SYMBOL) && !strchr(str, '\\')) {
+               //TODO: Can use on strings with \ too, just unbackslash() them?
+               debug_printf_expand("string_to_string(fast)=>'%s'\n", str);
+               return xstrdup(str);
        }
 
-       /* Only real trickiness here is that the uncommitted
-        * command structure is not counted in pi->num_cmds. */
-       pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
-       ctx->command = command = &pi->cmds[pi->num_cmds];
- clear_and_ret:
-       memset(command, 0, sizeof(*command));
-       return pi->num_cmds; /* used only for 0/nonzero check */
+       argv[0] = (char*)str;
+       argv[1] = NULL;
+       list = expand_variables(argv, do_unbackslash
+                       ? EXP_FLAG_ESC_GLOB_CHARS | EXP_FLAG_SINGLEWORD
+                       : EXP_FLAG_SINGLEWORD
+       );
+       if (HUSH_DEBUG)
+               if (!list[0] || list[1])
+                       bb_error_msg_and_die("BUG in varexp2");
+       /* actually, just move string 2*sizeof(char*) bytes back */
+       overlapping_strcpy((char*)list, list[0]);
+       if (do_unbackslash)
+               unbackslash((char*)list);
+       debug_printf_expand("string_to_string=>'%s'\n", (char*)list);
+       return (char*)list;
 }
 
-static void done_pipe(struct parse_context *ctx, pipe_style type)
+/* Used for "eval" builtin */
+static char* expand_strvec_to_string(char **argv)
 {
-       int not_null;
+       char **list;
 
-       debug_printf_parse("done_pipe entered, followup %d\n", type);
-       /* Close previous command */
-       not_null = done_command(ctx);
-       ctx->pipe->followup = type;
-#if HAS_KEYWORDS
-       ctx->pipe->pi_inverted = ctx->ctx_inverted;
-       ctx->ctx_inverted = 0;
-       ctx->pipe->res_word = ctx->ctx_res_w;
-#endif
+       list = expand_variables(argv, EXP_FLAG_SINGLEWORD);
+       /* Convert all NULs to spaces */
+       if (list[0]) {
+               int n = 1;
+               while (list[n]) {
+                       if (HUSH_DEBUG)
+                               if (list[n-1] + strlen(list[n-1]) + 1 != list[n])
+                                       bb_error_msg_and_die("BUG in varexp3");
+                       /* bash uses ' ' regardless of $IFS contents */
+                       list[n][-1] = ' ';
+                       n++;
+               }
+       }
+       overlapping_strcpy((char*)list, list[0]);
+       debug_printf_expand("strvec_to_string='%s'\n", (char*)list);
+       return (char*)list;
+}
 
-       /* Without this check, even just <enter> on command line generates
-        * tree of three NOPs (!). Which is harmless but annoying.
-        * IOW: it is safe to do it unconditionally. */
-       if (not_null
-#if ENABLE_HUSH_IF
-        || ctx->ctx_res_w == RES_FI
-#endif
-#if ENABLE_HUSH_LOOPS
-        || ctx->ctx_res_w == RES_DONE
-        || ctx->ctx_res_w == RES_FOR
-        || ctx->ctx_res_w == RES_IN
-#endif
-#if ENABLE_HUSH_CASE
-        || ctx->ctx_res_w == RES_ESAC
-#endif
-       ) {
-               struct pipe *new_p;
-               debug_printf_parse("done_pipe: adding new pipe: "
-                               "not_null:%d ctx->ctx_res_w:%d\n",
-                               not_null, ctx->ctx_res_w);
-               new_p = new_pipe();
-               ctx->pipe->next = new_p;
-               ctx->pipe = new_p;
-               /* RES_THEN, RES_DO etc are "sticky" -
-                * they remain set for pipes inside if/while.
-                * This is used to control execution.
-                * RES_FOR and RES_IN are NOT sticky (needed to support
-                * cases where variable or value happens to match a keyword):
-                */
-#if ENABLE_HUSH_LOOPS
-               if (ctx->ctx_res_w == RES_FOR
-                || ctx->ctx_res_w == RES_IN)
-                       ctx->ctx_res_w = RES_NONE;
-#endif
-#if ENABLE_HUSH_CASE
-               if (ctx->ctx_res_w == RES_MATCH)
-                       ctx->ctx_res_w = RES_CASE_BODY;
-               if (ctx->ctx_res_w == RES_CASE)
-                       ctx->ctx_res_w = RES_CASE_IN;
-#endif
-               ctx->command = NULL; /* trick done_command below */
-               /* Create the memory for command, roughly:
-                * ctx->pipe->cmds = new struct command;
-                * ctx->command = &ctx->pipe->cmds[0];
-                */
-               done_command(ctx);
-               //debug_print_tree(ctx->list_head, 10);
+static char **expand_assignments(char **argv, int count)
+{
+       int i;
+       char **p;
+
+       G.expanded_assignments = p = NULL;
+       /* Expand assignments into one string each */
+       for (i = 0; i < count; i++) {
+               G.expanded_assignments = p = add_string_to_strings(p, expand_string_to_string(argv[i], /*unbackslash:*/ 1));
        }
-       debug_printf_parse("done_pipe return\n");
+       G.expanded_assignments = NULL;
+       return p;
 }
 
-static void initialize_context(struct parse_context *ctx)
+
+static void switch_off_special_sigs(unsigned mask)
 {
-       memset(ctx, 0, sizeof(*ctx));
-       ctx->pipe = ctx->list_head = new_pipe();
-       /* Create the memory for command, roughly:
-        * ctx->pipe->cmds = new struct command;
-        * ctx->command = &ctx->pipe->cmds[0];
-        */
-       done_command(ctx);
+       unsigned sig = 0;
+       while ((mask >>= 1) != 0) {
+               sig++;
+               if (!(mask & 1))
+                       continue;
+               if (G.traps) {
+                       if (G.traps[sig] && !G.traps[sig][0])
+                               /* trap is '', has to remain SIG_IGN */
+                               continue;
+                       free(G.traps[sig]);
+                       G.traps[sig] = NULL;
+               }
+               /* We are here only if no trap or trap was not '' */
+               install_sighandler(sig, SIG_DFL);
+       }
 }
 
-/* If a reserved word is found and processed, parse context is modified
- * and 1 is returned.
- */
-#if HAS_KEYWORDS
-struct reserved_combo {
-       char literal[6];
-       unsigned char res;
-       unsigned char assignment_flag;
-       int flag;
-};
-enum {
-       FLAG_END   = (1 << RES_NONE ),
-# if ENABLE_HUSH_IF
-       FLAG_IF    = (1 << RES_IF   ),
-       FLAG_THEN  = (1 << RES_THEN ),
-       FLAG_ELIF  = (1 << RES_ELIF ),
-       FLAG_ELSE  = (1 << RES_ELSE ),
-       FLAG_FI    = (1 << RES_FI   ),
-# endif
-# if ENABLE_HUSH_LOOPS
-       FLAG_FOR   = (1 << RES_FOR  ),
-       FLAG_WHILE = (1 << RES_WHILE),
-       FLAG_UNTIL = (1 << RES_UNTIL),
-       FLAG_DO    = (1 << RES_DO   ),
-       FLAG_DONE  = (1 << RES_DONE ),
-       FLAG_IN    = (1 << RES_IN   ),
-# endif
-# if ENABLE_HUSH_CASE
-       FLAG_MATCH = (1 << RES_MATCH),
-       FLAG_ESAC  = (1 << RES_ESAC ),
-# endif
-       FLAG_START = (1 << RES_XXXX ),
-};
+#if BB_MMU
+/* never called */
+void re_execute_shell(char ***to_free, const char *s,
+               char *g_argv0, char **g_argv,
+               char **builtin_argv) NORETURN;
 
-static const struct reserved_combo* match_reserved_word(o_string *word)
+static void reset_traps_to_defaults(void)
 {
-       /* Mostly a list of accepted follow-up reserved words.
-        * FLAG_END means we are done with the sequence, and are ready
-        * to turn the compound list into a command.
-        * FLAG_START means the word must start a new compound list.
+       /* This function is always called in a child shell
+        * after fork (not vfork, NOMMU doesn't use this function).
+        */
+       unsigned sig;
+       unsigned mask;
+
+       /* Child shells are not interactive.
+        * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling.
+        * Testcase: (while :; do :; done) + ^Z should background.
+        * Same goes for SIGTERM, SIGHUP, SIGINT.
         */
-       static const struct reserved_combo reserved_list[] = {
-# if ENABLE_HUSH_IF
-               { "!",     RES_NONE,  NOT_ASSIGNMENT , 0 },
-               { "if",    RES_IF,    WORD_IS_KEYWORD, FLAG_THEN | FLAG_START },
-               { "then",  RES_THEN,  WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
-               { "elif",  RES_ELIF,  WORD_IS_KEYWORD, FLAG_THEN },
-               { "else",  RES_ELSE,  WORD_IS_KEYWORD, FLAG_FI   },
-               { "fi",    RES_FI,    NOT_ASSIGNMENT , FLAG_END  },
-# endif
-# if ENABLE_HUSH_LOOPS
-               { "for",   RES_FOR,   NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
-               { "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
-               { "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
-               { "in",    RES_IN,    NOT_ASSIGNMENT , FLAG_DO   },
-               { "do",    RES_DO,    WORD_IS_KEYWORD, FLAG_DONE },
-               { "done",  RES_DONE,  NOT_ASSIGNMENT , FLAG_END  },
-# endif
-# if ENABLE_HUSH_CASE
-               { "case",  RES_CASE,  NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
-               { "esac",  RES_ESAC,  NOT_ASSIGNMENT , FLAG_END  },
-# endif
-       };
-       const struct reserved_combo *r;
+       mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask;
+       if (!G.traps && !mask)
+               return; /* already no traps and no special sigs */
 
-       for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) {
-               if (strcmp(word->data, r->literal) == 0)
-                       return r;
+       /* Switch off special sigs */
+       switch_off_special_sigs(mask);
+#if ENABLE_HUSH_JOB
+       G_fatal_sig_mask = 0;
+#endif
+       G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS;
+       /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS
+        * remain set in G.special_sig_mask */
+
+       if (!G.traps)
+               return;
+
+       /* Reset all sigs to default except ones with empty traps */
+       for (sig = 0; sig < NSIG; sig++) {
+               if (!G.traps[sig])
+                       continue; /* no trap: nothing to do */
+               if (!G.traps[sig][0])
+                       continue; /* empty trap: has to remain SIG_IGN */
+               /* sig has non-empty trap, reset it: */
+               free(G.traps[sig]);
+               G.traps[sig] = NULL;
+               /* There is no signal for trap 0 (EXIT) */
+               if (sig == 0)
+                       continue;
+               install_sighandler(sig, pick_sighandler(sig));
        }
-       return NULL;
 }
-/* Return 0: not a keyword, 1: keyword
- */
-static int reserved_word(o_string *word, struct parse_context *ctx)
+
+#else /* !BB_MMU */
+
+static void re_execute_shell(char ***to_free, const char *s,
+               char *g_argv0, char **g_argv,
+               char **builtin_argv) NORETURN;
+static void re_execute_shell(char ***to_free, const char *s,
+               char *g_argv0, char **g_argv,
+               char **builtin_argv)
 {
-# if ENABLE_HUSH_CASE
-       static const struct reserved_combo reserved_match = {
-               "",        RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC
-       };
+# define NOMMU_HACK_FMT ("-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x"))
+       /* delims + 2 * (number of bytes in printed hex numbers) */
+       char param_buf[sizeof(NOMMU_HACK_FMT) + 2 * (sizeof(int)*6 + sizeof(long long)*1)];
+       char *heredoc_argv[4];
+       struct variable *cur;
+# if ENABLE_HUSH_FUNCTIONS
+       struct function *funcp;
 # endif
-       const struct reserved_combo *r;
+       char **argv, **pp;
+       unsigned cnt;
+       unsigned long long empty_trap_mask;
 
-       if (word->o_quoted)
-               return 0;
-       r = match_reserved_word(word);
-       if (!r)
-               return 0;
+       if (!g_argv0) { /* heredoc */
+               argv = heredoc_argv;
+               argv[0] = (char *) G.argv0_for_re_execing;
+               argv[1] = (char *) "-<";
+               argv[2] = (char *) s;
+               argv[3] = NULL;
+               pp = &argv[3]; /* used as pointer to empty environment */
+               goto do_exec;
+       }
 
-       debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
-# if ENABLE_HUSH_CASE
-       if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) {
-               /* "case word IN ..." - IN part starts first MATCH part */
-               r = &reserved_match;
-       } else
-# endif
-       if (r->flag == 0) { /* '!' */
-               if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
-                       syntax_error("! ! command");
-                       ctx->ctx_res_w = RES_SNTX;
+       cnt = 0;
+       pp = builtin_argv;
+       if (pp) while (*pp++)
+               cnt++;
+
+       empty_trap_mask = 0;
+       if (G.traps) {
+               int sig;
+               for (sig = 1; sig < NSIG; sig++) {
+                       if (G.traps[sig] && !G.traps[sig][0])
+                               empty_trap_mask |= 1LL << sig;
                }
-               ctx->ctx_inverted = 1;
-               return 1;
        }
-       if (r->flag & FLAG_START) {
-               struct parse_context *old;
 
-               old = xmalloc(sizeof(*old));
-               debug_printf_parse("push stack %p\n", old);
-               *old = *ctx;   /* physical copy */
-               initialize_context(ctx);
-               ctx->stack = old;
-       } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
-               syntax_error_at(word->data);
-               ctx->ctx_res_w = RES_SNTX;
-               return 1;
-       } else {
-               /* "{...} fi" is ok. "{...} if" is not
-                * Example:
-                * if { echo foo; } then { echo bar; } fi */
-               if (ctx->command->group)
-                       done_pipe(ctx, PIPE_SEQ);
+       sprintf(param_buf, NOMMU_HACK_FMT
+                       , (unsigned) G.root_pid
+                       , (unsigned) G.root_ppid
+                       , (unsigned) G.last_bg_pid
+                       , (unsigned) G.last_exitcode
+                       , cnt
+                       , empty_trap_mask
+                       IF_HUSH_LOOPS(, G.depth_of_loop)
+                       );
+# undef NOMMU_HACK_FMT
+       /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<etc...> <vars...> <funcs...>
+        * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL
+        */
+       cnt += 6;
+       for (cur = G.top_var; cur; cur = cur->next) {
+               if (!cur->flg_export || cur->flg_read_only)
+                       cnt += 2;
+       }
+# if ENABLE_HUSH_FUNCTIONS
+       for (funcp = G.top_func; funcp; funcp = funcp->next)
+               cnt += 3;
+# endif
+       pp = g_argv;
+       while (*pp++)
+               cnt++;
+       *to_free = argv = pp = xzalloc(sizeof(argv[0]) * cnt);
+       *pp++ = (char *) G.argv0_for_re_execing;
+       *pp++ = param_buf;
+       for (cur = G.top_var; cur; cur = cur->next) {
+               if (strcmp(cur->varstr, hush_version_str) == 0)
+                       continue;
+               if (cur->flg_read_only) {
+                       *pp++ = (char *) "-R";
+                       *pp++ = cur->varstr;
+               } else if (!cur->flg_export) {
+                       *pp++ = (char *) "-V";
+                       *pp++ = cur->varstr;
+               }
+       }
+# if ENABLE_HUSH_FUNCTIONS
+       for (funcp = G.top_func; funcp; funcp = funcp->next) {
+               *pp++ = (char *) "-F";
+               *pp++ = funcp->name;
+               *pp++ = funcp->body_as_string;
+       }
+# endif
+       /* We can pass activated traps here. Say, -Tnn:trap_string
+        *
+        * However, POSIX says that subshells reset signals with traps
+        * to SIG_DFL.
+        * I tested bash-3.2 and it not only does that with true subshells
+        * of the form ( list ), but with any forked children shells.
+        * I set trap "echo W" WINCH; and then tried:
+        *
+        * { echo 1; sleep 20; echo 2; } &
+        * while true; do echo 1; sleep 20; echo 2; break; done &
+        * true | { echo 1; sleep 20; echo 2; } | cat
+        *
+        * In all these cases sending SIGWINCH to the child shell
+        * did not run the trap. If I add trap "echo V" WINCH;
+        * _inside_ group (just before echo 1), it works.
+        *
+        * I conclude it means we don't need to pass active traps here.
+        */
+       *pp++ = (char *) "-c";
+       *pp++ = (char *) s;
+       if (builtin_argv) {
+               while (*++builtin_argv)
+                       *pp++ = *builtin_argv;
+               *pp++ = (char *) "";
        }
+       *pp++ = g_argv0;
+       while (*g_argv)
+               *pp++ = *g_argv++;
+       /* *pp = NULL; - is already there */
+       pp = environ;
 
-       ctx->ctx_res_w = r->res;
-       ctx->old_flag = r->flag;
-       word->o_assignment = r->assignment_flag;
+ do_exec:
+       debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s);
+       /* Don't propagate SIG_IGN to the child */
+       if (SPECIAL_JOBSTOP_SIGS != 0)
+               switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
+       execve(bb_busybox_exec_path, argv, pp);
+       /* Fallback. Useful for init=/bin/hush usage etc */
+       if (argv[0][0] == '/')
+               execve(argv[0], argv, pp);
+       xfunc_error_retval = 127;
+       bb_error_msg_and_die("can't re-execute the shell");
+}
+#endif  /* !BB_MMU */
 
-       if (ctx->old_flag & FLAG_END) {
-               struct parse_context *old;
 
-               done_pipe(ctx, PIPE_SEQ);
-               debug_printf_parse("pop stack %p\n", ctx->stack);
-               old = ctx->stack;
-               old->command->group = ctx->list_head;
-               old->command->cmd_type = CMD_NORMAL;
-# if !BB_MMU
-               o_addstr(&old->as_string, ctx->as_string.data);
-               o_free_unsafe(&ctx->as_string);
-               old->command->group_as_string = xstrdup(old->as_string.data);
-               debug_printf_parse("pop, remembering as:'%s'\n",
-                               old->command->group_as_string);
-# endif
-               *ctx = *old;   /* physical copy */
-               free(old);
+static int run_and_free_list(struct pipe *pi);
+
+/* Executing from string: eval, sh -c '...'
+ *          or from file: /etc/profile, . file, sh <script>, sh (intereactive)
+ * end_trigger controls how often we stop parsing
+ * NUL: parse all, execute, return
+ * ';': parse till ';' or newline, execute, repeat till EOF
+ */
+static void parse_and_run_stream(struct in_str *inp, int end_trigger)
+{
+       /* Why we need empty flag?
+        * An obscure corner case "false; ``; echo $?":
+        * empty command in `` should still set $? to 0.
+        * But we can't just set $? to 0 at the start,
+        * this breaks "false; echo `echo $?`" case.
+        */
+       bool empty = 1;
+       while (1) {
+               struct pipe *pipe_list;
+
+#if ENABLE_HUSH_INTERACTIVE
+               if (end_trigger == ';')
+                       inp->promptmode = 0; /* PS1 */
+#endif
+               pipe_list = parse_stream(NULL, inp, end_trigger);
+               if (!pipe_list || pipe_list == ERR_PTR) { /* EOF/error */
+                       /* If we are in "big" script
+                        * (not in `cmd` or something similar)...
+                        */
+                       if (pipe_list == ERR_PTR && end_trigger == ';') {
+                               /* Discard cached input (rest of line) */
+                               int ch = inp->last_char;
+                               while (ch != EOF && ch != '\n') {
+                                       //bb_error_msg("Discarded:'%c'", ch);
+                                       ch = i_getch(inp);
+                               }
+                               /* Force prompt */
+                               inp->p = NULL;
+                               /* This stream isn't empty */
+                               empty = 0;
+                               continue;
+                       }
+                       if (!pipe_list && empty)
+                               G.last_exitcode = 0;
+                       break;
+               }
+               debug_print_tree(pipe_list, 0);
+               debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
+               run_and_free_list(pipe_list);
+               empty = 0;
+#if ENABLE_HUSH_FUNCTIONS
+               if (G.flag_return_in_progress == 1)
+                       break;
+#endif
        }
-       return 1;
 }
-#endif /* HAS_KEYWORDS */
 
-/* Word is complete, look at it and update parsing context.
- * Normal return is 0. Syntax errors return 1.
- * Note: on return, word is reset, but not o_free'd!
- */
-static int done_word(o_string *word, struct parse_context *ctx)
+static void parse_and_run_string(const char *s)
+{
+       struct in_str input;
+       setup_string_in_str(&input, s);
+       parse_and_run_stream(&input, '\0');
+}
+
+static void parse_and_run_file(FILE *f)
 {
-       struct command *command = ctx->command;
+       struct in_str input;
+       setup_file_in_str(&input, f);
+       parse_and_run_stream(&input, ';');
+}
 
-       debug_printf_parse("done_word entered: '%s' %p\n", word->data, command);
-       if (word->length == 0 && word->o_quoted == 0) {
-               debug_printf_parse("done_word return 0: true null, ignored\n");
-               return 0;
-       }
+#if ENABLE_HUSH_TICK
+static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
+{
+       pid_t pid;
+       int channel[2];
+# if !BB_MMU
+       char **to_free = NULL;
+# endif
 
-       if (ctx->pending_redirect) {
-               /* We do not glob in e.g. >*.tmp case. bash seems to glob here
-                * only if run as "bash", not "sh" */
-               /* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
-                * "2.7 Redirection
-                * ...the word that follows the redirection operator
-                * shall be subjected to tilde expansion, parameter expansion,
-                * command substitution, arithmetic expansion, and quote
-                * removal. Pathname expansion shall not be performed
-                * on the word by a non-interactive shell; an interactive
-                * shell may perform it, but shall do so only when
-                * the expansion would result in one word."
+       xpipe(channel);
+       pid = BB_MMU ? xfork() : xvfork();
+       if (pid == 0) { /* child */
+               disable_restore_tty_pgrp_on_exit();
+               /* Process substitution is not considered to be usual
+                * 'command execution'.
+                * SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
                 */
-               ctx->pending_redirect->rd_filename = xstrdup(word->data);
-               /* Cater for >\file case:
-                * >\a creates file a; >\\a, >"\a", >"\\a" create file \a
-                * Same with heredocs:
-                * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H
+               bb_signals(0
+                       + (1 << SIGTSTP)
+                       + (1 << SIGTTIN)
+                       + (1 << SIGTTOU)
+                       , SIG_IGN);
+               CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
+               close(channel[0]); /* NB: close _first_, then move fd! */
+               xmove_fd(channel[1], 1);
+               /* Prevent it from trying to handle ctrl-z etc */
+               IF_HUSH_JOB(G.run_list_level = 1;)
+               /* Awful hack for `trap` or $(trap).
+                *
+                * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
+                * contains an example where "trap" is executed in a subshell:
+                *
+                * save_traps=$(trap)
+                * ...
+                * eval "$save_traps"
+                *
+                * Standard does not say that "trap" in subshell shall print
+                * parent shell's traps. It only says that its output
+                * must have suitable form, but then, in the above example
+                * (which is not supposed to be normative), it implies that.
+                *
+                * bash (and probably other shell) does implement it
+                * (traps are reset to defaults, but "trap" still shows them),
+                * but as a result, "trap" logic is hopelessly messed up:
+                *
+                * # trap
+                * trap -- 'echo Ho' SIGWINCH  <--- we have a handler
+                * # (trap)        <--- trap is in subshell - no output (correct, traps are reset)
+                * # true | trap   <--- trap is in subshell - no output (ditto)
+                * # echo `true | trap`    <--- in subshell - output (but traps are reset!)
+                * trap -- 'echo Ho' SIGWINCH
+                * # echo `(trap)`         <--- in subshell in subshell - output
+                * trap -- 'echo Ho' SIGWINCH
+                * # echo `true | (trap)`  <--- in subshell in subshell in subshell - output!
+                * trap -- 'echo Ho' SIGWINCH
+                *
+                * The rules when to forget and when to not forget traps
+                * get really complex and nonsensical.
+                *
+                * Our solution: ONLY bare $(trap) or `trap` is special.
                 */
-               if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) {
-                       unbackslash(ctx->pending_redirect->rd_filename);
-                       /* Is it <<"HEREDOC"? */
-                       if (word->o_quoted) {
-                               ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED;
-                       }
-               }
-               debug_printf_parse("word stored in rd_filename: '%s'\n", word->data);
-               ctx->pending_redirect = NULL;
-       } else {
-               /* If this word wasn't an assignment, next ones definitely
-                * can't be assignments. Even if they look like ones. */
-               if (word->o_assignment != DEFINITELY_ASSIGNMENT
-                && word->o_assignment != WORD_IS_KEYWORD
+               s = skip_whitespace(s);
+               if (strncmp(s, "trap", 4) == 0
+                && skip_whitespace(s + 4)[0] == '\0'
                ) {
-                       word->o_assignment = NOT_ASSIGNMENT;
-               } else {
-                       if (word->o_assignment == DEFINITELY_ASSIGNMENT)
-                               command->assignment_cnt++;
-                       word->o_assignment = MAYBE_ASSIGNMENT;
+                       static const char *const argv[] = { NULL, NULL };
+                       builtin_trap((char**)argv);
+                       exit(0); /* not _exit() - we need to fflush */
                }
-
-#if HAS_KEYWORDS
-# if ENABLE_HUSH_CASE
-               if (ctx->ctx_dsemicolon
-                && strcmp(word->data, "esac") != 0 /* not "... pattern) cmd;; esac" */
-               ) {
-                       /* already done when ctx_dsemicolon was set to 1: */
-                       /* ctx->ctx_res_w = RES_MATCH; */
-                       ctx->ctx_dsemicolon = 0;
-               } else
-# endif
-               if (!command->argv /* if it's the first word... */
-# if ENABLE_HUSH_LOOPS
-                && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
-                && ctx->ctx_res_w != RES_IN
-# endif
-# if ENABLE_HUSH_CASE
-                && ctx->ctx_res_w != RES_CASE
+# if BB_MMU
+               reset_traps_to_defaults();
+               parse_and_run_string(s);
+               _exit(G.last_exitcode);
+# else
+       /* We re-execute after vfork on NOMMU. This makes this script safe:
+        * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG
+        * huge=`cat BIG` # was blocking here forever
+        * echo OK
+        */
+               re_execute_shell(&to_free,
+                               s,
+                               G.global_argv[0],
+                               G.global_argv + 1,
+                               NULL);
 # endif
-               ) {
-                       debug_printf_parse("checking '%s' for reserved-ness\n", word->data);
-                       if (reserved_word(word, ctx)) {
-                               o_reset_to_empty_unquoted(word);
-                               debug_printf_parse("done_word return %d\n",
-                                               (ctx->ctx_res_w == RES_SNTX));
-                               return (ctx->ctx_res_w == RES_SNTX);
-                       }
-# ifdef CMD_SINGLEWORD_NOGLOB_COND
-                       if (strcmp(word->data, "export") == 0
-#  if ENABLE_HUSH_LOCAL
-                        || strcmp(word->data, "local") == 0
-#  endif
-                       ) {
-                               command->cmd_type = CMD_SINGLEWORD_NOGLOB_COND;
-                       } else
+       }
+
+       /* parent */
+       *pid_p = pid;
+# if ENABLE_HUSH_FAST
+       G.count_SIGCHLD++;
+//bb_error_msg("[%d] fork in generate_stream_from_string:"
+//             " G.count_SIGCHLD:%d G.handled_SIGCHLD:%d",
+//             getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
 # endif
-# if ENABLE_HUSH_BASH_COMPAT
-                       if (strcmp(word->data, "[[") == 0) {
-                               command->cmd_type = CMD_SINGLEWORD_NOGLOB;
-                       }
-                       /* fall through */
+       enable_restore_tty_pgrp_on_exit();
+# if !BB_MMU
+       free(to_free);
 # endif
+       close(channel[1]);
+       close_on_exec_on(channel[0]);
+       return xfdopen_for_read(channel[0]);
+}
+
+/* Return code is exit status of the process that is run. */
+static int process_command_subs(o_string *dest, const char *s)
+{
+       FILE *fp;
+       struct in_str pipe_str;
+       pid_t pid;
+       int status, ch, eol_cnt;
+
+       fp = generate_stream_from_string(s, &pid);
+
+       /* Now send results of command back into original context */
+       setup_file_in_str(&pipe_str, fp);
+       eol_cnt = 0;
+       while ((ch = i_getch(&pipe_str)) != EOF) {
+               if (ch == '\n') {
+                       eol_cnt++;
+                       continue;
                }
-#endif
-               if (command->group) {
-                       /* "{ echo foo; } echo bar" - bad */
-                       syntax_error_at(word->data);
-                       debug_printf_parse("done_word return 1: syntax error, "
-                                       "groups and arglists don't mix\n");
-                       return 1;
-               }
-               if (word->o_quoted /* word had "xx" or 'xx' at least as part of it. */
-                /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */
-                && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL)
-                /* (otherwise it's known to be not empty and is already safe) */
-               ) {
-                       /* exclude "$@" - it can expand to no word despite "" */
-                       char *p = word->data;
-                       while (p[0] == SPECIAL_VAR_SYMBOL
-                           && (p[1] & 0x7f) == '@'
-                           && p[2] == SPECIAL_VAR_SYMBOL
-                       ) {
-                               p += 3;
-                       }
-                       if (p == word->data || p[0] != '\0') {
-                               /* saw no "$@", or not only "$@" but some
-                                * real text is there too */
-                               /* insert "empty variable" reference, this makes
-                                * e.g. "", $empty"" etc to not disappear */
-                               o_addchr(word, SPECIAL_VAR_SYMBOL);
-                               o_addchr(word, SPECIAL_VAR_SYMBOL);
-                       }
+               while (eol_cnt) {
+                       o_addchr(dest, '\n');
+                       eol_cnt--;
                }
-               command->argv = add_string_to_strings(command->argv, xstrdup(word->data));
-               debug_print_strings("word appended to argv", command->argv);
+               o_addQchr(dest, ch);
        }
 
-#if ENABLE_HUSH_LOOPS
-       if (ctx->ctx_res_w == RES_FOR) {
-               if (word->o_quoted
-                || !is_well_formed_var_name(command->argv[0], '\0')
-               ) {
-                       /* bash says just "not a valid identifier" */
-                       syntax_error("not a valid identifier in for");
-                       return 1;
-               }
-               /* Force FOR to have just one word (variable name) */
-               /* NB: basically, this makes hush see "for v in ..."
-                * syntax as if it is "for v; in ...". FOR and IN become
-                * two pipe structs in parse tree. */
-               done_pipe(ctx, PIPE_SEQ);
-       }
-#endif
-#if ENABLE_HUSH_CASE
-       /* Force CASE to have just one word */
-       if (ctx->ctx_res_w == RES_CASE) {
-               done_pipe(ctx, PIPE_SEQ);
-       }
+       debug_printf("done reading from `cmd` pipe, closing it\n");
+       fclose(fp);
+       /* We need to extract exitcode. Test case
+        * "true; echo `sleep 1; false` $?"
+        * should print 1 */
+       safe_waitpid(pid, &status, 0);
+       debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status));
+       return WEXITSTATUS(status);
+}
+#endif /* ENABLE_HUSH_TICK */
+
+
+static void setup_heredoc(struct redir_struct *redir)
+{
+       struct fd_pair pair;
+       pid_t pid;
+       int len, written;
+       /* the _body_ of heredoc (misleading field name) */
+       const char *heredoc = redir->rd_filename;
+       char *expanded;
+#if !BB_MMU
+       char **to_free;
 #endif
 
-       o_reset_to_empty_unquoted(word);
+       expanded = NULL;
+       if (!(redir->rd_dup & HEREDOC_QUOTED)) {
+               expanded = encode_then_expand_string(heredoc, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
+               if (expanded)
+                       heredoc = expanded;
+       }
+       len = strlen(heredoc);
 
-       debug_printf_parse("done_word return 0\n");
-       return 0;
-}
+       close(redir->rd_fd); /* often saves dup2+close in xmove_fd */
+       xpiped_pair(pair);
+       xmove_fd(pair.rd, redir->rd_fd);
 
+       /* Try writing without forking. Newer kernels have
+        * dynamically growing pipes. Must use non-blocking write! */
+       ndelay_on(pair.wr);
+       while (1) {
+               written = write(pair.wr, heredoc, len);
+               if (written <= 0)
+                       break;
+               len -= written;
+               if (len == 0) {
+                       close(pair.wr);
+                       free(expanded);
+                       return;
+               }
+               heredoc += written;
+       }
+       ndelay_off(pair.wr);
 
-/* Peek ahead in the input to find out if we have a "&n" construct,
- * as in "2>&1", that represents duplicating a file descriptor.
- * Return:
- * REDIRFD_CLOSE if >&- "close fd" construct is seen,
- * REDIRFD_SYNTAX_ERR if syntax error,
- * REDIRFD_TO_FILE if no & was seen,
- * or the number found.
- */
+       /* Okay, pipe buffer was not big enough */
+       /* Note: we must not create a stray child (bastard? :)
+        * for the unsuspecting parent process. Child creates a grandchild
+        * and exits before parent execs the process which consumes heredoc
+        * (that exec happens after we return from this function) */
+#if !BB_MMU
+       to_free = NULL;
+#endif
+       pid = xvfork();
+       if (pid == 0) {
+               /* child */
+               disable_restore_tty_pgrp_on_exit();
+               pid = BB_MMU ? xfork() : xvfork();
+               if (pid != 0)
+                       _exit(0);
+               /* grandchild */
+               close(redir->rd_fd); /* read side of the pipe */
 #if BB_MMU
-#define parse_redir_right_fd(as_string, input) \
-       parse_redir_right_fd(input)
+               full_write(pair.wr, heredoc, len); /* may loop or block */
+               _exit(0);
+#else
+               /* Delegate blocking writes to another process */
+               xmove_fd(pair.wr, STDOUT_FILENO);
+               re_execute_shell(&to_free, heredoc, NULL, NULL, NULL);
 #endif
-static int parse_redir_right_fd(o_string *as_string, struct in_str *input)
-{
-       int ch, d, ok;
-
-       ch = i_peek(input);
-       if (ch != '&')
-               return REDIRFD_TO_FILE;
-
-       ch = i_getch(input);  /* get the & */
-       nommu_addchr(as_string, ch);
-       ch = i_peek(input);
-       if (ch == '-') {
-               ch = i_getch(input);
-               nommu_addchr(as_string, ch);
-               return REDIRFD_CLOSE;
-       }
-       d = 0;
-       ok = 0;
-       while (ch != EOF && isdigit(ch)) {
-               d = d*10 + (ch-'0');
-               ok = 1;
-               ch = i_getch(input);
-               nommu_addchr(as_string, ch);
-               ch = i_peek(input);
        }
-       if (ok) return d;
-
-//TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2)
-
-       bb_error_msg("ambiguous redirect");
-       return REDIRFD_SYNTAX_ERR;
+       /* parent */
+#if ENABLE_HUSH_FAST
+       G.count_SIGCHLD++;
+//bb_error_msg("[%d] fork in setup_heredoc: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
+#endif
+       enable_restore_tty_pgrp_on_exit();
+#if !BB_MMU
+       free(to_free);
+#endif
+       close(pair.wr);
+       free(expanded);
+       wait(NULL); /* wait till child has died */
 }
 
-/* Return code is 0 normal, 1 if a syntax error is detected
- */
-static int parse_redirect(struct parse_context *ctx,
-               int fd,
-               redir_type style,
-               struct in_str *input)
+/* squirrel != NULL means we squirrel away copies of stdin, stdout,
+ * and stderr if they are redirected. */
+static int setup_redirects(struct command *prog, int squirrel[])
 {
-       struct command *command = ctx->command;
+       int openfd, mode;
        struct redir_struct *redir;
-       struct redir_struct **redirp;
-       int dup_num;
 
-       dup_num = REDIRFD_TO_FILE;
-       if (style != REDIRECT_HEREDOC) {
-               /* Check for a '>&1' type redirect */
-               dup_num = parse_redir_right_fd(&ctx->as_string, input);
-               if (dup_num == REDIRFD_SYNTAX_ERR)
-                       return 1;
-       } else {
-               int ch = i_peek(input);
-               dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */
-               if (dup_num) { /* <<-... */
-                       ch = i_getch(input);
-                       nommu_addchr(&ctx->as_string, ch);
-                       ch = i_peek(input);
+       for (redir = prog->redirects; redir; redir = redir->next) {
+               if (redir->rd_type == REDIRECT_HEREDOC2) {
+                       /* rd_fd<<HERE case */
+                       if (squirrel && redir->rd_fd < 3
+                        && squirrel[redir->rd_fd] < 0
+                       ) {
+                               squirrel[redir->rd_fd] = dup(redir->rd_fd);
+                       }
+                       /* for REDIRECT_HEREDOC2, rd_filename holds _contents_
+                        * of the heredoc */
+                       debug_printf_parse("set heredoc '%s'\n",
+                                       redir->rd_filename);
+                       setup_heredoc(redir);
+                       continue;
                }
-       }
 
-       if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
-               int ch = i_peek(input);
-               if (ch == '|') {
-                       /* >|FILE redirect ("clobbering" >).
-                        * Since we do not support "set -o noclobber" yet,
-                        * >| and > are the same for now. Just eat |.
-                        */
-                       ch = i_getch(input);
-                       nommu_addchr(&ctx->as_string, ch);
+               if (redir->rd_dup == REDIRFD_TO_FILE) {
+                       /* rd_fd<*>file case (<*> is <,>,>>,<>) */
+                       char *p;
+                       if (redir->rd_filename == NULL) {
+                               /* Something went wrong in the parse.
+                                * Pretend it didn't happen */
+                               bb_error_msg("bug in redirect parse");
+                               continue;
+                       }
+                       mode = redir_table[redir->rd_type].mode;
+                       p = expand_string_to_string(redir->rd_filename, /*unbackslash:*/ 1);
+                       openfd = open_or_warn(p, mode);
+                       free(p);
+                       if (openfd < 0) {
+                       /* this could get lost if stderr has been redirected, but
+                        * bash and ash both lose it as well (though zsh doesn't!) */
+//what the above comment tries to say?
+                               return 1;
+                       }
+               } else {
+                       /* rd_fd<*>rd_dup or rd_fd<*>- cases */
+                       openfd = redir->rd_dup;
                }
-       }
-
-       /* Create a new redir_struct and append it to the linked list */
-       redirp = &command->redirects;
-       while ((redir = *redirp) != NULL) {
-               redirp = &(redir->next);
-       }
-       *redirp = redir = xzalloc(sizeof(*redir));
-       /* redir->next = NULL; */
-       /* redir->rd_filename = NULL; */
-       redir->rd_type = style;
-       redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd;
-
-       debug_printf_parse("redirect type %d %s\n", redir->rd_fd,
-                               redir_table[style].descrip);
 
-       redir->rd_dup = dup_num;
-       if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) {
-               /* Erik had a check here that the file descriptor in question
-                * is legit; I postpone that to "run time"
-                * A "-" representation of "close me" shows up as a -3 here */
-               debug_printf_parse("duplicating redirect '%d>&%d'\n",
-                               redir->rd_fd, redir->rd_dup);
-       } else {
-               /* Set ctx->pending_redirect, so we know what to do at the
-                * end of the next parsed word. */
-               ctx->pending_redirect = redir;
+               if (openfd != redir->rd_fd) {
+                       if (squirrel && redir->rd_fd < 3
+                        && squirrel[redir->rd_fd] < 0
+                       ) {
+                               squirrel[redir->rd_fd] = dup(redir->rd_fd);
+                       }
+                       if (openfd == REDIRFD_CLOSE) {
+                               /* "n>-" means "close me" */
+                               close(redir->rd_fd);
+                       } else {
+                               xdup2(openfd, redir->rd_fd);
+                               if (redir->rd_dup == REDIRFD_TO_FILE)
+                                       close(openfd);
+                       }
+               }
        }
        return 0;
 }
 
-/* If a redirect is immediately preceded by a number, that number is
- * supposed to tell which file descriptor to redirect.  This routine
- * looks for such preceding numbers.  In an ideal world this routine
- * needs to handle all the following classes of redirects...
- *     echo 2>foo     # redirects fd  2 to file "foo", nothing passed to echo
- *     echo 49>foo    # redirects fd 49 to file "foo", nothing passed to echo
- *     echo -2>foo    # redirects fd  1 to file "foo",    "-2" passed to echo
- *     echo 49x>foo   # redirects fd  1 to file "foo",   "49x" passed to echo
- *
- * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
- * "2.7 Redirection
- * ... If n is quoted, the number shall not be recognized as part of
- * the redirection expression. For example:
- * echo \2>a
- * writes the character 2 into file a"
- * We are getting it right by setting ->o_quoted on any \<char>
- *
- * A -1 return means no valid number was found,
- * the caller should use the appropriate default for this redirection.
- */
-static int redirect_opt_num(o_string *o)
+static void restore_redirects(int squirrel[])
 {
-       int num;
-
-       if (o->data == NULL)
-               return -1;
-       num = bb_strtou(o->data, NULL, 10);
-       if (errno || num < 0)
-               return -1;
-       o_reset_to_empty_unquoted(o);
-       return num;
+       int i, fd;
+       for (i = 0; i < 3; i++) {
+               fd = squirrel[i];
+               if (fd != -1) {
+                       /* We simply die on error */
+                       xmove_fd(fd, i);
+               }
+       }
 }
 
-#if BB_MMU
-#define fetch_till_str(as_string, input, word, skip_tabs) \
-       fetch_till_str(input, word, skip_tabs)
-#endif
-static char *fetch_till_str(o_string *as_string,
-               struct in_str *input,
-               const char *word,
-               int skip_tabs)
+static char *find_in_path(const char *arg)
 {
-       o_string heredoc = NULL_O_STRING;
-       int past_EOL = 0;
-       int ch;
+       char *ret = NULL;
+       const char *PATH = get_local_var_value("PATH");
+
+       if (!PATH)
+               return NULL;
 
-       goto jump_in;
        while (1) {
-               ch = i_getch(input);
-               nommu_addchr(as_string, ch);
-               if (ch == '\n') {
-                       if (strcmp(heredoc.data + past_EOL, word) == 0) {
-                               heredoc.data[past_EOL] = '\0';
-                               debug_printf_parse("parsed heredoc '%s'\n", heredoc.data);
-                               return heredoc.data;
-                       }
-                       do {
-                               o_addchr(&heredoc, ch);
-                               past_EOL = heredoc.length;
- jump_in:
-                               do {
-                                       ch = i_getch(input);
-                                       nommu_addchr(as_string, ch);
-                               } while (skip_tabs && ch == '\t');
-                       } while (ch == '\n');
+               const char *end = strchrnul(PATH, ':');
+               int sz = end - PATH; /* must be int! */
+
+               free(ret);
+               if (sz != 0) {
+                       ret = xasprintf("%.*s/%s", sz, PATH, arg);
+               } else {
+                       /* We have xxx::yyyy in $PATH,
+                        * it means "use current dir" */
+                       ret = xstrdup(arg);
                }
-               if (ch == EOF) {
-                       o_free_unsafe(&heredoc);
+               if (access(ret, F_OK) == 0)
+                       break;
+
+               if (*end == '\0') {
+                       free(ret);
                        return NULL;
                }
-               o_addchr(&heredoc, ch);
-               nommu_addchr(as_string, ch);
+               PATH = end + 1;
        }
+
+       return ret;
 }
 
-/* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs
- * and load them all. There should be exactly heredoc_cnt of them.
- */
-static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_str *input)
+static const struct built_in_command *find_builtin_helper(const char *name,
+               const struct built_in_command *x,
+               const struct built_in_command *end)
 {
-       struct pipe *pi = ctx->list_head;
+       while (x != end) {
+               if (strcmp(name, x->b_cmd) != 0) {
+                       x++;
+                       continue;
+               }
+               debug_printf_exec("found builtin '%s'\n", name);
+               return x;
+       }
+       return NULL;
+}
+static const struct built_in_command *find_builtin1(const char *name)
+{
+       return find_builtin_helper(name, bltins1, &bltins1[ARRAY_SIZE(bltins1)]);
+}
+static const struct built_in_command *find_builtin(const char *name)
+{
+       const struct built_in_command *x = find_builtin1(name);
+       if (x)
+               return x;
+       return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
+}
 
-       while (pi && heredoc_cnt) {
-               int i;
-               struct command *cmd = pi->cmds;
+#if ENABLE_HUSH_FUNCTIONS
+static struct function **find_function_slot(const char *name)
+{
+       struct function **funcpp = &G.top_func;
+       while (*funcpp) {
+               if (strcmp(name, (*funcpp)->name) == 0) {
+                       break;
+               }
+               funcpp = &(*funcpp)->next;
+       }
+       return funcpp;
+}
 
-               debug_printf_parse("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n",
-                               pi->num_cmds,
-                               cmd->argv ? cmd->argv[0] : "NONE");
-               for (i = 0; i < pi->num_cmds; i++) {
-                       struct redir_struct *redir = cmd->redirects;
+static const struct function *find_function(const char *name)
+{
+       const struct function *funcp = *find_function_slot(name);
+       if (funcp)
+               debug_printf_exec("found function '%s'\n", name);
+       return funcp;
+}
 
-                       debug_printf_parse("fetch_heredocs: %d cmd argv0:'%s'\n",
-                                       i, cmd->argv ? cmd->argv[0] : "NONE");
-                       while (redir) {
-                               if (redir->rd_type == REDIRECT_HEREDOC) {
-                                       char *p;
+/* Note: takes ownership on name ptr */
+static struct function *new_function(char *name)
+{
+       struct function **funcpp = find_function_slot(name);
+       struct function *funcp = *funcpp;
 
-                                       redir->rd_type = REDIRECT_HEREDOC2;
-                                       /* redir->rd_dup is (ab)used to indicate <<- */
-                                       p = fetch_till_str(&ctx->as_string, input,
-                                               redir->rd_filename, redir->rd_dup & HEREDOC_SKIPTABS);
-                                       if (!p) {
-                                               syntax_error("unexpected EOF in here document");
-                                               return 1;
-                                       }
-                                       free(redir->rd_filename);
-                                       redir->rd_filename = p;
-                                       heredoc_cnt--;
-                               }
-                               redir = redir->next;
+       if (funcp != NULL) {
+               struct command *cmd = funcp->parent_cmd;
+               debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd);
+               if (!cmd) {
+                       debug_printf_exec("freeing & replacing function '%s'\n", funcp->name);
+                       free(funcp->name);
+                       /* Note: if !funcp->body, do not free body_as_string!
+                        * This is a special case of "-F name body" function:
+                        * body_as_string was not malloced! */
+                       if (funcp->body) {
+                               free_pipe_list(funcp->body);
+# if !BB_MMU
+                               free(funcp->body_as_string);
+# endif
                        }
-                       cmd++;
+               } else {
+                       debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name);
+                       cmd->argv[0] = funcp->name;
+                       cmd->group = funcp->body;
+# if !BB_MMU
+                       cmd->group_as_string = funcp->body_as_string;
+# endif
                }
-               pi = pi->next;
+       } else {
+               debug_printf_exec("remembering new function '%s'\n", name);
+               funcp = *funcpp = xzalloc(sizeof(*funcp));
+               /*funcp->next = NULL;*/
        }
-#if 0
-       /* Should be 0. If it isn't, it's a parse error */
-       if (heredoc_cnt)
-               bb_error_msg_and_die("heredoc BUG 2");
-#endif
-       return 0;
-}
 
+       funcp->name = name;
+       return funcp;
+}
 
-#if ENABLE_HUSH_TICK
-static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
+static void unset_func(const char *name)
 {
-       pid_t pid;
-       int channel[2];
+       struct function **funcpp = find_function_slot(name);
+       struct function *funcp = *funcpp;
+
+       if (funcp != NULL) {
+               debug_printf_exec("freeing function '%s'\n", funcp->name);
+               *funcpp = funcp->next;
+               /* funcp is unlinked now, deleting it.
+                * Note: if !funcp->body, the function was created by
+                * "-F name body", do not free ->body_as_string
+                * and ->name as they were not malloced. */
+               if (funcp->body) {
+                       free_pipe_list(funcp->body);
+                       free(funcp->name);
 # if !BB_MMU
-       char **to_free = NULL;
+                       free(funcp->body_as_string);
 # endif
-
-       xpipe(channel);
-       pid = BB_MMU ? xfork() : xvfork();
-       if (pid == 0) { /* child */
-               disable_restore_tty_pgrp_on_exit();
-               /* Process substitution is not considered to be usual
-                * 'command execution'.
-                * SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
-                */
-               bb_signals(0
-                       + (1 << SIGTSTP)
-                       + (1 << SIGTTIN)
-                       + (1 << SIGTTOU)
-                       , SIG_IGN);
-               CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
-               close(channel[0]); /* NB: close _first_, then move fd! */
-               xmove_fd(channel[1], 1);
-               /* Prevent it from trying to handle ctrl-z etc */
-               IF_HUSH_JOB(G.run_list_level = 1;)
-               /* Awful hack for `trap` or $(trap).
-                *
-                * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
-                * contains an example where "trap" is executed in a subshell:
-                *
-                * save_traps=$(trap)
-                * ...
-                * eval "$save_traps"
-                *
-                * Standard does not say that "trap" in subshell shall print
-                * parent shell's traps. It only says that its output
-                * must have suitable form, but then, in the above example
-                * (which is not supposed to be normative), it implies that.
-                *
-                * bash (and probably other shell) does implement it
-                * (traps are reset to defaults, but "trap" still shows them),
-                * but as a result, "trap" logic is hopelessly messed up:
-                *
-                * # trap
-                * trap -- 'echo Ho' SIGWINCH  <--- we have a handler
-                * # (trap)        <--- trap is in subshell - no output (correct, traps are reset)
-                * # true | trap   <--- trap is in subshell - no output (ditto)
-                * # echo `true | trap`    <--- in subshell - output (but traps are reset!)
-                * trap -- 'echo Ho' SIGWINCH
-                * # echo `(trap)`         <--- in subshell in subshell - output
-                * trap -- 'echo Ho' SIGWINCH
-                * # echo `true | (trap)`  <--- in subshell in subshell in subshell - output!
-                * trap -- 'echo Ho' SIGWINCH
-                *
-                * The rules when to forget and when to not forget traps
-                * get really complex and nonsensical.
-                *
-                * Our solution: ONLY bare $(trap) or `trap` is special.
-                */
-               s = skip_whitespace(s);
-               if (strncmp(s, "trap", 4) == 0 && (*skip_whitespace(s + 4) == '\0'))
-               {
-                       static const char *const argv[] = { NULL, NULL };
-                       builtin_trap((char**)argv);
-                       exit(0); /* not _exit() - we need to fflush */
                }
+               free(funcp);
+       }
+}
+
+# if BB_MMU
+#define exec_function(to_free, funcp, argv) \
+       exec_function(funcp, argv)
+# endif
+static void exec_function(char ***to_free,
+               const struct function *funcp,
+               char **argv) NORETURN;
+static void exec_function(char ***to_free,
+               const struct function *funcp,
+               char **argv)
+{
 # if BB_MMU
-               reset_traps_to_defaults();
-               parse_and_run_string(s);
-               _exit(G.last_exitcode);
+       int n = 1;
+
+       argv[0] = G.global_argv[0];
+       G.global_argv = argv;
+       while (*++argv)
+               n++;
+       G.global_argc = n;
+       /* On MMU, funcp->body is always non-NULL */
+       n = run_list(funcp->body);
+       fflush_all();
+       _exit(n);
 # else
-       /* We re-execute after vfork on NOMMU. This makes this script safe:
-        * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG
-        * huge=`cat BIG` # was blocking here forever
-        * echo OK
-        */
-               re_execute_shell(&to_free,
-                               s,
-                               G.global_argv[0],
-                               G.global_argv + 1,
-                               NULL);
+       re_execute_shell(to_free,
+                       funcp->body_as_string,
+                       G.global_argv[0],
+                       argv + 1,
+                       NULL);
 # endif
-       }
+}
 
-       /* parent */
-       *pid_p = pid;
-# if ENABLE_HUSH_FAST
-       G.count_SIGCHLD++;
-//bb_error_msg("[%d] fork in generate_stream_from_string: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
+static int run_function(const struct function *funcp, char **argv)
+{
+       int rc;
+       save_arg_t sv;
+       smallint sv_flg;
+
+       save_and_replace_G_args(&sv, argv);
+
+       /* "we are in function, ok to use return" */
+       sv_flg = G.flag_return_in_progress;
+       G.flag_return_in_progress = -1;
+# if ENABLE_HUSH_LOCAL
+       G.func_nest_level++;
 # endif
-       enable_restore_tty_pgrp_on_exit();
+
+       /* On MMU, funcp->body is always non-NULL */
 # if !BB_MMU
-       free(to_free);
+       if (!funcp->body) {
+               /* Function defined by -F */
+               parse_and_run_string(funcp->body_as_string);
+               rc = G.last_exitcode;
+       } else
 # endif
-       close(channel[1]);
-       close_on_exec_on(channel[0]);
-       return xfdopen_for_read(channel[0]);
+       {
+               rc = run_list(funcp->body);
+       }
+
+# if ENABLE_HUSH_LOCAL
+       {
+               struct variable *var;
+               struct variable **var_pp;
+
+               var_pp = &G.top_var;
+               while ((var = *var_pp) != NULL) {
+                       if (var->func_nest_level < G.func_nest_level) {
+                               var_pp = &var->next;
+                               continue;
+                       }
+                       /* Unexport */
+                       if (var->flg_export)
+                               bb_unsetenv(var->varstr);
+                       /* Remove from global list */
+                       *var_pp = var->next;
+                       /* Free */
+                       if (!var->max_len)
+                               free(var->varstr);
+                       free(var);
+               }
+               G.func_nest_level--;
+       }
+# endif
+       G.flag_return_in_progress = sv_flg;
+
+       restore_G_args(&sv, argv);
+
+       return rc;
 }
+#endif /* ENABLE_HUSH_FUNCTIONS */
 
-/* Return code is exit status of the process that is run. */
-static int process_command_subs(o_string *dest, const char *s)
+
+#if BB_MMU
+#define exec_builtin(to_free, x, argv) \
+       exec_builtin(x, argv)
+#else
+#define exec_builtin(to_free, x, argv) \
+       exec_builtin(to_free, argv)
+#endif
+static void exec_builtin(char ***to_free,
+               const struct built_in_command *x,
+               char **argv) NORETURN;
+static void exec_builtin(char ***to_free,
+               const struct built_in_command *x,
+               char **argv)
 {
-       FILE *fp;
-       struct in_str pipe_str;
-       pid_t pid;
-       int status, ch, eol_cnt;
+#if BB_MMU
+       int rcode;
+       fflush_all();
+       rcode = x->b_function(argv);
+       fflush_all();
+       _exit(rcode);
+#else
+       fflush_all();
+       /* On NOMMU, we must never block!
+        * Example: { sleep 99 | read line; } & echo Ok
+        */
+       re_execute_shell(to_free,
+                       argv[0],
+                       G.global_argv[0],
+                       G.global_argv + 1,
+                       argv);
+#endif
+}
 
-       fp = generate_stream_from_string(s, &pid);
 
-       /* Now send results of command back into original context */
-       setup_file_in_str(&pipe_str, fp);
-       eol_cnt = 0;
-       while ((ch = i_getch(&pipe_str)) != EOF) {
-               if (ch == '\n') {
-                       eol_cnt++;
-                       continue;
-               }
-               while (eol_cnt) {
-                       o_addchr(dest, '\n');
-                       eol_cnt--;
-               }
-               o_addQchr(dest, ch);
-       }
+static void execvp_or_die(char **argv) NORETURN;
+static void execvp_or_die(char **argv)
+{
+       debug_printf_exec("execing '%s'\n", argv[0]);
+       /* Don't propagate SIG_IGN to the child */
+       if (SPECIAL_JOBSTOP_SIGS != 0)
+               switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
+       execvp(argv[0], argv);
+       bb_perror_msg("can't execute '%s'", argv[0]);
+       _exit(127); /* bash compat */
+}
 
-       debug_printf("done reading from `cmd` pipe, closing it\n");
-       fclose(fp);
-       /* We need to extract exitcode. Test case
-        * "true; echo `sleep 1; false` $?"
-        * should print 1 */
-       safe_waitpid(pid, &status, 0);
-       debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status));
-       return WEXITSTATUS(status);
+#if ENABLE_HUSH_MODE_X
+static void dump_cmd_in_x_mode(char **argv)
+{
+       if (G_x_mode && argv) {
+               /* We want to output the line in one write op */
+               char *buf, *p;
+               int len;
+               int n;
+
+               len = 3;
+               n = 0;
+               while (argv[n])
+                       len += strlen(argv[n++]) + 1;
+               buf = xmalloc(len);
+               buf[0] = '+';
+               p = buf + 1;
+               n = 0;
+               while (argv[n])
+                       p += sprintf(p, " %s", argv[n++]);
+               *p++ = '\n';
+               *p = '\0';
+               fputs(buf, stderr);
+               free(buf);
+       }
 }
-#endif /* ENABLE_HUSH_TICK */
+#else
+# define dump_cmd_in_x_mode(argv) ((void)0)
+#endif
 
-#if !ENABLE_HUSH_FUNCTIONS
-#define parse_group(dest, ctx, input, ch) \
-       parse_group(ctx, input, ch)
+#if BB_MMU
+#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \
+       pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
+#define pseudo_exec(nommu_save, command, argv_expanded) \
+       pseudo_exec(command, argv_expanded)
 #endif
-static int parse_group(o_string *dest, struct parse_context *ctx,
-       struct in_str *input, int ch)
+
+/* Called after [v]fork() in run_pipe, or from builtin_exec.
+ * Never returns.
+ * Don't exit() here.  If you don't exec, use _exit instead.
+ * The at_exit handlers apparently confuse the calling process,
+ * in particular stdin handling.  Not sure why? -- because of vfork! (vda) */
+static void pseudo_exec_argv(nommu_save_t *nommu_save,
+               char **argv, int assignment_cnt,
+               char **argv_expanded) NORETURN;
+static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
+               char **argv, int assignment_cnt,
+               char **argv_expanded)
 {
-       /* dest contains characters seen prior to ( or {.
-        * Typically it's empty, but for function defs,
-        * it contains function name (without '()'). */
-       struct pipe *pipe_list;
-       int endch;
-       struct command *command = ctx->command;
+       char **new_env;
 
-       debug_printf_parse("parse_group entered\n");
-#if ENABLE_HUSH_FUNCTIONS
-       if (ch == '(' && !dest->o_quoted) {
-               if (dest->length)
-                       if (done_word(dest, ctx))
-                               return 1;
-               if (!command->argv)
-                       goto skip; /* (... */
-               if (command->argv[1]) { /* word word ... (... */
-                       syntax_error_unexpected_ch('(');
-                       return 1;
-               }
-               /* it is "word(..." or "word (..." */
-               do
-                       ch = i_getch(input);
-               while (ch == ' ' || ch == '\t');
-               if (ch != ')') {
-                       syntax_error_unexpected_ch(ch);
-                       return 1;
+       new_env = expand_assignments(argv, assignment_cnt);
+       dump_cmd_in_x_mode(new_env);
+
+       if (!argv[assignment_cnt]) {
+               /* Case when we are here: ... | var=val | ...
+                * (note that we do not exit early, i.e., do not optimize out
+                * expand_assignments(): think about ... | var=`sleep 1` | ...
+                */
+               free_strings(new_env);
+               _exit(EXIT_SUCCESS);
+       }
+
+#if BB_MMU
+       set_vars_and_save_old(new_env);
+       free(new_env); /* optional */
+       /* we can also destroy set_vars_and_save_old's return value,
+        * to save memory */
+#else
+       nommu_save->new_env = new_env;
+       nommu_save->old_vars = set_vars_and_save_old(new_env);
+#endif
+
+       if (argv_expanded) {
+               argv = argv_expanded;
+       } else {
+               argv = expand_strvec_to_strvec(argv + assignment_cnt);
+#if !BB_MMU
+               nommu_save->argv = argv;
+#endif
+       }
+       dump_cmd_in_x_mode(argv);
+
+#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
+       if (strchr(argv[0], '/') != NULL)
+               goto skip;
+#endif
+
+       /* Check if the command matches any of the builtins.
+        * Depending on context, this might be redundant.  But it's
+        * easier to waste a few CPU cycles than it is to figure out
+        * if this is one of those cases.
+        */
+       {
+               /* On NOMMU, it is more expensive to re-execute shell
+                * just in order to run echo or test builtin.
+                * It's better to skip it here and run corresponding
+                * non-builtin later. */
+               const struct built_in_command *x;
+               x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]);
+               if (x) {
+                       exec_builtin(&nommu_save->argv_from_re_execing, x, argv);
                }
-               nommu_addchr(&ctx->as_string, ch);
-               do
-                       ch = i_getch(input);
-               while (ch == ' ' || ch == '\t' || ch == '\n');
-               if (ch != '{') {
-                       syntax_error_unexpected_ch(ch);
-                       return 1;
+       }
+#if ENABLE_HUSH_FUNCTIONS
+       /* Check if the command matches any functions */
+       {
+               const struct function *funcp = find_function(argv[0]);
+               if (funcp) {
+                       exec_function(&nommu_save->argv_from_re_execing, funcp, argv);
                }
-               nommu_addchr(&ctx->as_string, ch);
-               command->cmd_type = CMD_FUNCDEF;
-               goto skip;
        }
 #endif
 
-#if 0 /* Prevented by caller */
-       if (command->argv /* word [word]{... */
-        || dest->length /* word{... */
-        || dest->o_quoted /* ""{... */
-       ) {
-               syntax_error(NULL);
-               debug_printf_parse("parse_group return 1: "
-                       "syntax error, groups and arglists don't mix\n");
-               return 1;
+#if ENABLE_FEATURE_SH_STANDALONE
+       /* Check if the command matches any busybox applets */
+       {
+               int a = find_applet_by_name(argv[0]);
+               if (a >= 0) {
+# if BB_MMU /* see above why on NOMMU it is not allowed */
+                       if (APPLET_IS_NOEXEC(a)) {
+                               debug_printf_exec("running applet '%s'\n", argv[0]);
+                               run_applet_no_and_exit(a, argv);
+                       }
+# endif
+                       /* Re-exec ourselves */
+                       debug_printf_exec("re-execing applet '%s'\n", argv[0]);
+                       /* Don't propagate SIG_IGN to the child */
+                       if (SPECIAL_JOBSTOP_SIGS != 0)
+                               switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
+                       execv(bb_busybox_exec_path, argv);
+                       /* If they called chroot or otherwise made the binary no longer
+                        * executable, fall through */
+               }
        }
 #endif
 
-#if ENABLE_HUSH_FUNCTIONS
+#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
  skip:
 #endif
-       endch = '}';
-       if (ch == '(') {
-               endch = ')';
-               command->cmd_type = CMD_SUBSHELL;
-       } else {
-               /* bash does not allow "{echo...", requires whitespace */
-               ch = i_getch(input);
-               if (ch != ' ' && ch != '\t' && ch != '\n') {
-                       syntax_error_unexpected_ch(ch);
-                       return 1;
-               }
-               nommu_addchr(&ctx->as_string, ch);
+       execvp_or_die(argv);
+}
+
+/* Called after [v]fork() in run_pipe
+ */
+static void pseudo_exec(nommu_save_t *nommu_save,
+               struct command *command,
+               char **argv_expanded) NORETURN;
+static void pseudo_exec(nommu_save_t *nommu_save,
+               struct command *command,
+               char **argv_expanded)
+{
+       if (command->argv) {
+               pseudo_exec_argv(nommu_save, command->argv,
+                               command->assignment_cnt, argv_expanded);
        }
 
-       {
+       if (command->group) {
+               /* Cases when we are here:
+                * ( list )
+                * { list } &
+                * ... | ( list ) | ...
+                * ... | { list } | ...
+                */
 #if BB_MMU
-# define as_string NULL
+               int rcode;
+               debug_printf_exec("pseudo_exec: run_list\n");
+               reset_traps_to_defaults();
+               rcode = run_list(command->group);
+               /* OK to leak memory by not calling free_pipe_list,
+                * since this process is about to exit */
+               _exit(rcode);
 #else
-               char *as_string = NULL;
-#endif
-               pipe_list = parse_stream(&as_string, input, endch);
-#if !BB_MMU
-               if (as_string)
-                       o_addstr(&ctx->as_string, as_string);
-#endif
-               /* empty ()/{} or parse error? */
-               if (!pipe_list || pipe_list == ERR_PTR) {
-                       /* parse_stream already emitted error msg */
-                       if (!BB_MMU)
-                               free(as_string);
-                       debug_printf_parse("parse_group return 1: "
-                               "parse_stream returned %p\n", pipe_list);
-                       return 1;
-               }
-               command->group = pipe_list;
-#if !BB_MMU
-               as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */
-               command->group_as_string = as_string;
-               debug_printf_parse("end of group, remembering as:'%s'\n",
-                               command->group_as_string);
+               re_execute_shell(&nommu_save->argv_from_re_execing,
+                               command->group_as_string,
+                               G.global_argv[0],
+                               G.global_argv + 1,
+                               NULL);
 #endif
-#undef as_string
        }
-       debug_printf_parse("parse_group return 0\n");
-       return 0;
-       /* command remains "open", available for possible redirects */
+
+       /* Case when we are here: ... | >file */
+       debug_printf_exec("pseudo_exec'ed null command\n");
+       _exit(EXIT_SUCCESS);
 }
 
-#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS
-/* Subroutines for copying $(...) and `...` things */
-static void add_till_backquote(o_string *dest, struct in_str *input);
-/* '...' */
-static void add_till_single_quote(o_string *dest, struct in_str *input)
+#if ENABLE_HUSH_JOB
+static const char *get_cmdtext(struct pipe *pi)
 {
-       while (1) {
-               int ch = i_getch(input);
-               if (ch == EOF) {
-                       syntax_error_unterm_ch('\'');
-                       /*xfunc_die(); - redundant */
-               }
-               if (ch == '\'')
-                       return;
-               o_addchr(dest, ch);
+       char **argv;
+       char *p;
+       int len;
+
+       /* This is subtle. ->cmdtext is created only on first backgrounding.
+        * (Think "cat, <ctrl-z>, fg, <ctrl-z>, fg, <ctrl-z>...." here...)
+        * On subsequent bg argv is trashed, but we won't use it */
+       if (pi->cmdtext)
+               return pi->cmdtext;
+       argv = pi->cmds[0].argv;
+       if (!argv || !argv[0]) {
+               pi->cmdtext = xzalloc(1);
+               return pi->cmdtext;
        }
+
+       len = 0;
+       do {
+               len += strlen(*argv) + 1;
+       } while (*++argv);
+       p = xmalloc(len);
+       pi->cmdtext = p;
+       argv = pi->cmds[0].argv;
+       do {
+               len = strlen(*argv);
+               memcpy(p, *argv, len);
+               p += len;
+               *p++ = ' ';
+       } while (*++argv);
+       p[-1] = '\0';
+       return pi->cmdtext;
 }
-/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
-static void add_till_double_quote(o_string *dest, struct in_str *input)
+
+static void insert_bg_job(struct pipe *pi)
 {
-       while (1) {
-               int ch = i_getch(input);
-               if (ch == EOF) {
-                       syntax_error_unterm_ch('"');
-                       /*xfunc_die(); - redundant */
-               }
-               if (ch == '"')
-                       return;
-               if (ch == '\\') {  /* \x. Copy both chars. */
-                       o_addchr(dest, ch);
-                       ch = i_getch(input);
+       struct pipe *job, **jobp;
+       int i;
+
+       /* Linear search for the ID of the job to use */
+       pi->jobid = 1;
+       for (job = G.job_list; job; job = job->next)
+               if (job->jobid >= pi->jobid)
+                       pi->jobid = job->jobid + 1;
+
+       /* Add job to the list of running jobs */
+       jobp = &G.job_list;
+       while ((job = *jobp) != NULL)
+               jobp = &job->next;
+       job = *jobp = xmalloc(sizeof(*job));
+
+       *job = *pi; /* physical copy */
+       job->next = NULL;
+       job->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds);
+       /* Cannot copy entire pi->cmds[] vector! This causes double frees */
+       for (i = 0; i < pi->num_cmds; i++) {
+               job->cmds[i].pid = pi->cmds[i].pid;
+               /* all other fields are not used and stay zero */
+       }
+       job->cmdtext = xstrdup(get_cmdtext(pi));
+
+       if (G_interactive_fd)
+               printf("[%d] %d %s\n", job->jobid, job->cmds[0].pid, job->cmdtext);
+       G.last_jobid = job->jobid;
+}
+
+static void remove_bg_job(struct pipe *pi)
+{
+       struct pipe *prev_pipe;
+
+       if (pi == G.job_list) {
+               G.job_list = pi->next;
+       } else {
+               prev_pipe = G.job_list;
+               while (prev_pipe->next != pi)
+                       prev_pipe = prev_pipe->next;
+               prev_pipe->next = pi->next;
+       }
+       if (G.job_list)
+               G.last_jobid = G.job_list->jobid;
+       else
+               G.last_jobid = 0;
+}
+
+/* Remove a backgrounded job */
+static void delete_finished_bg_job(struct pipe *pi)
+{
+       remove_bg_job(pi);
+       free_pipe(pi);
+}
+#endif /* JOB */
+
+/* Check to see if any processes have exited -- if they
+ * have, figure out why and see if a job has completed */
+static int checkjobs(struct pipe *fg_pipe)
+{
+       int attributes;
+       int status;
+#if ENABLE_HUSH_JOB
+       struct pipe *pi;
+#endif
+       pid_t childpid;
+       int rcode = 0;
+
+       debug_printf_jobs("checkjobs %p\n", fg_pipe);
+
+       attributes = WUNTRACED;
+       if (fg_pipe == NULL)
+               attributes |= WNOHANG;
+
+       errno = 0;
+#if ENABLE_HUSH_FAST
+       if (G.handled_SIGCHLD == G.count_SIGCHLD) {
+//bb_error_msg("[%d] checkjobs: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d children?:%d fg_pipe:%p",
+//getpid(), G.count_SIGCHLD, G.handled_SIGCHLD, G.we_have_children, fg_pipe);
+               /* There was neither fork nor SIGCHLD since last waitpid */
+               /* Avoid doing waitpid syscall if possible */
+               if (!G.we_have_children) {
+                       errno = ECHILD;
+                       return -1;
                }
-               o_addchr(dest, ch);
-               if (ch == '`') {
-                       add_till_backquote(dest, input);
-                       o_addchr(dest, ch);
-                       continue;
+               if (fg_pipe == NULL) { /* is WNOHANG set? */
+                       /* We have children, but they did not exit
+                        * or stop yet (we saw no SIGCHLD) */
+                       return 0;
                }
-               //if (ch == '$') ...
+               /* else: !WNOHANG, waitpid will block, can't short-circuit */
        }
-}
-/* Process `cmd` - copy contents until "`" is seen. Complicated by
- * \` quoting.
- * "Within the backquoted style of command substitution, backslash
- * shall retain its literal meaning, except when followed by: '$', '`', or '\'.
- * The search for the matching backquote shall be satisfied by the first
- * backquote found without a preceding backslash; during this search,
- * if a non-escaped backquote is encountered within a shell comment,
- * a here-document, an embedded command substitution of the $(command)
- * form, or a quoted string, undefined results occur. A single-quoted
- * or double-quoted string that begins, but does not end, within the
- * "`...`" sequence produces undefined results."
- * Example                               Output
- * echo `echo '\'TEST\`echo ZZ\`BEST`    \TESTZZBEST
+#endif
+
+/* Do we do this right?
+ * bash-3.00# sleep 20 | false
+ * <ctrl-Z pressed>
+ * [3]+  Stopped          sleep 20 | false
+ * bash-3.00# echo $?
+ * 1   <========== bg pipe is not fully done, but exitcode is already known!
+ * [hush 1.14.0: yes we do it right]
  */
-static void add_till_backquote(o_string *dest, struct in_str *input)
-{
+ wait_more:
        while (1) {
-               int ch = i_getch(input);
-               if (ch == EOF) {
-                       syntax_error_unterm_ch('`');
-                       /*xfunc_die(); - redundant */
-               }
-               if (ch == '`')
-                       return;
-               if (ch == '\\') {
-                       /* \x. Copy both chars unless it is \` */
-                       int ch2 = i_getch(input);
-                       if (ch2 == EOF) {
-                               syntax_error_unterm_ch('`');
-                               /*xfunc_die(); - redundant */
+               int i;
+               int dead;
+
+#if ENABLE_HUSH_FAST
+               i = G.count_SIGCHLD;
+#endif
+               childpid = waitpid(-1, &status, attributes);
+               if (childpid <= 0) {
+                       if (childpid && errno != ECHILD)
+                               bb_perror_msg("waitpid");
+#if ENABLE_HUSH_FAST
+                       else { /* Until next SIGCHLD, waitpid's are useless */
+                               G.we_have_children = (childpid == 0);
+                               G.handled_SIGCHLD = i;
+//bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
                        }
-                       if (ch2 != '`' && ch2 != '$' && ch2 != '\\')
-                               o_addchr(dest, ch);
-                       ch = ch2;
+#endif
+                       break;
                }
-               o_addchr(dest, ch);
-       }
-}
-/* Process $(cmd) - copy contents until ")" is seen. Complicated by
- * quoting and nested ()s.
- * "With the $(command) style of command substitution, all characters
- * following the open parenthesis to the matching closing parenthesis
- * constitute the command. Any valid shell script can be used for command,
- * except a script consisting solely of redirections which produces
- * unspecified results."
- * Example                              Output
- * echo $(echo '(TEST)' BEST)           (TEST) BEST
- * echo $(echo 'TEST)' BEST)            TEST) BEST
- * echo $(echo \(\(TEST\) BEST)         ((TEST) BEST
- *
- * Also adapted to eat ${var%...} and $((...)) constructs, since ... part
- * can contain arbitrary constructs, just like $(cmd).
- * In bash compat mode, it needs to also be able to stop on '}' or ':'
- * for ${var:N[:M]} parsing.
- */
-#define DOUBLE_CLOSE_CHAR_FLAG 0x80
-static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch)
-{
-       int ch;
-       char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
-# if ENABLE_HUSH_BASH_COMPAT
-       char end_char2 = end_ch >> 8;
-# endif
-       end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
+               dead = WIFEXITED(status) || WIFSIGNALED(status);
 
-       while (1) {
-               ch = i_getch(input);
-               if (ch == EOF) {
-                       syntax_error_unterm_ch(end_ch);
-                       /*xfunc_die(); - redundant */
-               }
-               if (ch == end_ch  IF_HUSH_BASH_COMPAT( || ch == end_char2)) {
-                       if (!dbl)
-                               break;
-                       /* we look for closing )) of $((EXPR)) */
-                       if (i_peek(input) == end_ch) {
-                               i_getch(input); /* eat second ')' */
-                               break;
+#if DEBUG_JOBS
+               if (WIFSTOPPED(status))
+                       debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n",
+                                       childpid, WSTOPSIG(status), WEXITSTATUS(status));
+               if (WIFSIGNALED(status))
+                       debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n",
+                                       childpid, WTERMSIG(status), WEXITSTATUS(status));
+               if (WIFEXITED(status))
+                       debug_printf_jobs("pid %d exited, exitcode %d\n",
+                                       childpid, WEXITSTATUS(status));
+#endif
+               /* Were we asked to wait for fg pipe? */
+               if (fg_pipe) {
+                       i = fg_pipe->num_cmds;
+                       while (--i >= 0) {
+                               debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
+                               if (fg_pipe->cmds[i].pid != childpid)
+                                       continue;
+                               if (dead) {
+                                       int ex;
+                                       fg_pipe->cmds[i].pid = 0;
+                                       fg_pipe->alive_cmds--;
+                                       ex = WEXITSTATUS(status);
+                                       /* bash prints killer signal's name for *last*
+                                        * process in pipe (prints just newline for SIGINT/SIGPIPE).
+                                        * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
+                                        */
+                                       if (WIFSIGNALED(status)) {
+                                               int sig = WTERMSIG(status);
+                                               if (i == fg_pipe->num_cmds-1)
+                                                       /* TODO: use strsignal() instead for bash compat? but that's bloat... */
+                                                       printf("%s\n", sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig));
+                                               /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
+                                               /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
+                                                * Maybe we need to use sig | 128? */
+                                               ex = sig + 128;
+                                       }
+                                       fg_pipe->cmds[i].cmd_exitcode = ex;
+                               } else {
+                                       fg_pipe->stopped_cmds++;
+                               }
+                               debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
+                                               fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
+                               if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) {
+                                       /* All processes in fg pipe have exited or stopped */
+                                       i = fg_pipe->num_cmds;
+                                       while (--i >= 0) {
+                                               rcode = fg_pipe->cmds[i].cmd_exitcode;
+                                               /* usually last process gives overall exitstatus,
+                                                * but with "set -o pipefail", last *failed* process does */
+                                               if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0)
+                                                       break;
+                                       }
+                                       IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
+/* Note: *non-interactive* bash does not continue if all processes in fg pipe
+ * are stopped. Testcase: "cat | cat" in a script (not on command line!)
+ * and "killall -STOP cat" */
+                                       if (G_interactive_fd) {
+#if ENABLE_HUSH_JOB
+                                               if (fg_pipe->alive_cmds != 0)
+                                                       insert_bg_job(fg_pipe);
+#endif
+                                               return rcode;
+                                       }
+                                       if (fg_pipe->alive_cmds == 0)
+                                               return rcode;
+                               }
+                               /* There are still running processes in the fg pipe */
+                               goto wait_more; /* do waitpid again */
                        }
+                       /* it wasnt fg_pipe, look for process in bg pipes */
                }
-               o_addchr(dest, ch);
-               if (ch == '(' || ch == '{') {
-                       ch = (ch == '(' ? ')' : '}');
-                       add_till_closing_bracket(dest, input, ch);
-                       o_addchr(dest, ch);
-                       continue;
-               }
-               if (ch == '\'') {
-                       add_till_single_quote(dest, input);
-                       o_addchr(dest, ch);
-                       continue;
-               }
-               if (ch == '"') {
-                       add_till_double_quote(dest, input);
-                       o_addchr(dest, ch);
-                       continue;
-               }
-               if (ch == '`') {
-                       add_till_backquote(dest, input);
-                       o_addchr(dest, ch);
-                       continue;
+
+#if ENABLE_HUSH_JOB
+               /* We asked to wait for bg or orphaned children */
+               /* No need to remember exitcode in this case */
+               for (pi = G.job_list; pi; pi = pi->next) {
+                       for (i = 0; i < pi->num_cmds; i++) {
+                               if (pi->cmds[i].pid == childpid)
+                                       goto found_pi_and_prognum;
+                       }
                }
-               if (ch == '\\') {
-                       /* \x. Copy verbatim. Important for  \(, \) */
-                       ch = i_getch(input);
-                       if (ch == EOF) {
-                               syntax_error_unterm_ch(')');
-                               /*xfunc_die(); - redundant */
+               /* Happens when shell is used as init process (init=/bin/sh) */
+               debug_printf("checkjobs: pid %d was not in our list!\n", childpid);
+               continue; /* do waitpid again */
+
+ found_pi_and_prognum:
+               if (dead) {
+                       /* child exited */
+                       pi->cmds[i].pid = 0;
+                       pi->alive_cmds--;
+                       if (!pi->alive_cmds) {
+                               if (G_interactive_fd)
+                                       printf(JOB_STATUS_FORMAT, pi->jobid,
+                                                       "Done", pi->cmdtext);
+                               delete_finished_bg_job(pi);
                        }
-                       o_addchr(dest, ch);
-                       continue;
+               } else {
+                       /* child stopped */
+                       pi->stopped_cmds++;
                }
+#endif
+       } /* while (waitpid succeeds)... */
+
+       return rcode;
+}
+
+#if ENABLE_HUSH_JOB
+static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
+{
+       pid_t p;
+       int rcode = checkjobs(fg_pipe);
+       if (G_saved_tty_pgrp) {
+               /* Job finished, move the shell to the foreground */
+               p = getpgrp(); /* our process group id */
+               debug_printf_jobs("fg'ing ourself: getpgrp()=%d\n", (int)p);
+               tcsetpgrp(G_interactive_fd, p);
        }
-       return ch;
+       return rcode;
 }
-#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS */
+#endif
 
-/* Return code: 0 for OK, 1 for syntax error */
-#if BB_MMU
-#define parse_dollar(as_string, dest, input) \
-       parse_dollar(dest, input)
-#define as_string NULL
+/* Start all the jobs, but don't wait for anything to finish.
+ * See checkjobs().
+ *
+ * Return code is normally -1, when the caller has to wait for children
+ * to finish to determine the exit status of the pipe.  If the pipe
+ * is a simple builtin command, however, the action is done by the
+ * time run_pipe returns, and the exit code is provided as the
+ * return value.
+ *
+ * Returns -1 only if started some children. IOW: we have to
+ * mask out retvals of builtins etc with 0xff!
+ *
+ * The only case when we do not need to [v]fork is when the pipe
+ * is single, non-backgrounded, non-subshell command. Examples:
+ * cmd ; ...   { list } ; ...
+ * cmd && ...  { list } && ...
+ * cmd || ...  { list } || ...
+ * If it is, then we can run cmd as a builtin, NOFORK,
+ * or (if SH_STANDALONE) an applet, and we can run the { list }
+ * with run_list. If it isn't one of these, we fork and exec cmd.
+ *
+ * Cases when we must fork:
+ * non-single:   cmd | cmd
+ * backgrounded: cmd &     { list } &
+ * subshell:     ( list ) [&]
+ */
+#if !ENABLE_HUSH_MODE_X
+#define redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel, argv_expanded) \
+       redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel)
 #endif
-static int parse_dollar(o_string *as_string,
-               o_string *dest,
-               struct in_str *input)
+static int redirect_and_varexp_helper(char ***new_env_p,
+               struct variable **old_vars_p,
+               struct command *command,
+               int squirrel[3],
+               char **argv_expanded)
+{
+       /* setup_redirects acts on file descriptors, not FILEs.
+        * This is perfect for work that comes after exec().
+        * Is it really safe for inline use?  Experimentally,
+        * things seem to work. */
+       int rcode = setup_redirects(command, squirrel);
+       if (rcode == 0) {
+               char **new_env = expand_assignments(command->argv, command->assignment_cnt);
+               *new_env_p = new_env;
+               dump_cmd_in_x_mode(new_env);
+               dump_cmd_in_x_mode(argv_expanded);
+               if (old_vars_p)
+                       *old_vars_p = set_vars_and_save_old(new_env);
+       }
+       return rcode;
+}
+static NOINLINE int run_pipe(struct pipe *pi)
 {
-       int ch = i_peek(input);  /* first character after the $ */
-       unsigned char quote_mask = dest->o_escape ? 0x80 : 0;
+       static const char *const null_ptr = NULL;
 
-       debug_printf_parse("parse_dollar entered: ch='%c'\n", ch);
-       if (isalpha(ch)) {
-               ch = i_getch(input);
-               nommu_addchr(as_string, ch);
- make_var:
-               o_addchr(dest, SPECIAL_VAR_SYMBOL);
-               while (1) {
-                       debug_printf_parse(": '%c'\n", ch);
-                       o_addchr(dest, ch | quote_mask);
-                       quote_mask = 0;
-                       ch = i_peek(input);
-                       if (!isalnum(ch) && ch != '_')
-                               break;
-                       ch = i_getch(input);
-                       nommu_addchr(as_string, ch);
-               }
-               o_addchr(dest, SPECIAL_VAR_SYMBOL);
-       } else if (isdigit(ch)) {
- make_one_char_var:
-               ch = i_getch(input);
-               nommu_addchr(as_string, ch);
-               o_addchr(dest, SPECIAL_VAR_SYMBOL);
-               debug_printf_parse(": '%c'\n", ch);
-               o_addchr(dest, ch | quote_mask);
-               o_addchr(dest, SPECIAL_VAR_SYMBOL);
-       } else switch (ch) {
-       case '$': /* pid */
-       case '!': /* last bg pid */
-       case '?': /* last exit code */
-       case '#': /* number of args */
-       case '*': /* args */
-       case '@': /* args */
-               goto make_one_char_var;
-       case '{': {
-               o_addchr(dest, SPECIAL_VAR_SYMBOL);
+       int cmd_no;
+       int next_infd;
+       struct command *command;
+       char **argv_expanded;
+       char **argv;
+       /* it is not always needed, but we aim to smaller code */
+       int squirrel[] = { -1, -1, -1 };
+       int rcode;
 
-               ch = i_getch(input); /* eat '{' */
-               nommu_addchr(as_string, ch);
+       debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds);
+       debug_enter();
 
-               ch = i_getch(input); /* first char after '{' */
-               nommu_addchr(as_string, ch);
-               /* It should be ${?}, or ${#var},
-                * or even ${?+subst} - operator acting on a special variable,
-                * or the beginning of variable name.
-                */
-               if (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) { /* not one of those */
- bad_dollar_syntax:
-                       syntax_error_unterm_str("${name}");
-                       debug_printf_parse("parse_dollar return 1: unterminated ${name}\n");
-                       return 1;
-               }
-               ch |= quote_mask;
+       /* Testcase: set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*"
+        * Result should be 3 lines: q w e, qwe, q w e
+        */
+       G.ifs = get_local_var_value("IFS");
+       if (!G.ifs)
+               G.ifs = defifs;
 
-               /* It's possible to just call add_till_closing_bracket() at this point.
-                * However, this regresses some of our testsuite cases
-                * which check invalid constructs like ${%}.
-                * Oh well... let's check that the var name part is fine... */
+       IF_HUSH_JOB(pi->pgrp = -1;)
+       pi->stopped_cmds = 0;
+       command = &pi->cmds[0];
+       argv_expanded = NULL;
 
-               while (1) {
-                       unsigned pos;
+       if (pi->num_cmds != 1
+        || pi->followup == PIPE_BG
+        || command->cmd_type == CMD_SUBSHELL
+       ) {
+               goto must_fork;
+       }
 
-                       o_addchr(dest, ch);
-                       debug_printf_parse(": '%c'\n", ch);
+       pi->alive_cmds = 1;
 
-                       ch = i_getch(input);
-                       nommu_addchr(as_string, ch);
-                       if (ch == '}')
-                               break;
+       debug_printf_exec(": group:%p argv:'%s'\n",
+               command->group, command->argv ? command->argv[0] : "NONE");
 
-                       if (!isalnum(ch) && ch != '_') {
-                               unsigned end_ch;
-                               unsigned char last_ch;
-                               /* handle parameter expansions
-                                * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
-                                */
-                               if (!strchr("%#:-=+?", ch)) /* ${var<bad_char>... */
-                                       goto bad_dollar_syntax;
-                               o_addchr(dest, ch);
+       if (command->group) {
+#if ENABLE_HUSH_FUNCTIONS
+               if (command->cmd_type == CMD_FUNCDEF) {
+                       /* "executing" func () { list } */
+                       struct function *funcp;
 
-                               /* Eat everything until closing '}' (or ':') */
-                               end_ch = '}';
-                               if (ENABLE_HUSH_BASH_COMPAT
-                                && ch == ':'
-                                && !strchr("%#:-=+?"+3, i_peek(input))
-                               ) {
-                                       /* It's ${var:N[:M]} thing */
-                                       end_ch = '}' * 0x100 + ':';
-                               }
- again:
-                               if (!BB_MMU)
-                                       pos = dest->length;
-#if ENABLE_HUSH_DOLLAR_OPS
-                               last_ch = add_till_closing_bracket(dest, input, end_ch);
+                       funcp = new_function(command->argv[0]);
+                       /* funcp->name is already set to argv[0] */
+                       funcp->body = command->group;
+# if !BB_MMU
+                       funcp->body_as_string = command->group_as_string;
+                       command->group_as_string = NULL;
+# endif
+                       command->group = NULL;
+                       command->argv[0] = NULL;
+                       debug_printf_exec("cmd %p has child func at %p\n", command, funcp);
+                       funcp->parent_cmd = command;
+                       command->child_func = funcp;
+
+                       debug_printf_exec("run_pipe: return EXIT_SUCCESS\n");
+                       debug_leave();
+                       return EXIT_SUCCESS;
+               }
+#endif
+               /* { list } */
+               debug_printf("non-subshell group\n");
+               rcode = 1; /* exitcode if redir failed */
+               if (setup_redirects(command, squirrel) == 0) {
+                       debug_printf_exec(": run_list\n");
+                       rcode = run_list(command->group) & 0xff;
+               }
+               restore_redirects(squirrel);
+               IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
+               debug_leave();
+               debug_printf_exec("run_pipe: return %d\n", rcode);
+               return rcode;
+       }
+
+       argv = command->argv ? command->argv : (char **) &null_ptr;
+       {
+               const struct built_in_command *x;
+#if ENABLE_HUSH_FUNCTIONS
+               const struct function *funcp;
 #else
-#error Simple code to only allow ${var} is not implemented
+               enum { funcp = 0 };
 #endif
-                               if (as_string) {
-                                       o_addstr(as_string, dest->data + pos);
-                                       o_addchr(as_string, last_ch);
-                               }
+               char **new_env = NULL;
+               struct variable *old_vars = NULL;
 
-                               if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) {
-                                       /* close the first block: */
-                                       o_addchr(dest, SPECIAL_VAR_SYMBOL);
-                                       /* while parsing N from ${var:N[:M]}... */
-                                       if ((end_ch & 0xff) == last_ch) {
-                                               /* ...got ':' - parse the rest */
-                                               end_ch = '}';
-                                               goto again;
-                                       }
-                                       /* ...got '}', not ':' - it's ${var:N}! emulate :999999999 */
-                                       o_addstr(dest, "999999999");
+               if (argv[command->assignment_cnt] == NULL) {
+                       /* Assignments, but no command */
+                       /* Ensure redirects take effect (that is, create files).
+                        * Try "a=t >file" */
+#if 0 /* A few cases in testsuite fail with this code. FIXME */
+                       rcode = redirect_and_varexp_helper(&new_env, /*old_vars:*/ NULL, command, squirrel, /*argv_expanded:*/ NULL);
+                       /* Set shell variables */
+                       if (new_env) {
+                               argv = new_env;
+                               while (*argv) {
+                                       set_local_var(*argv, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
+                                       /* Do we need to flag set_local_var() errors?
+                                        * "assignment to readonly var" and "putenv error"
+                                        */
+                                       argv++;
                                }
-                               break;
                        }
-               }
-               o_addchr(dest, SPECIAL_VAR_SYMBOL);
-               break;
-       }
-#if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK
-       case '(': {
-               unsigned pos;
+                       /* Redirect error sets $? to 1. Otherwise,
+                        * if evaluating assignment value set $?, retain it.
+                        * Try "false; q=`exit 2`; echo $?" - should print 2: */
+                       if (rcode == 0)
+                               rcode = G.last_exitcode;
+                       /* Exit, _skipping_ variable restoring code: */
+                       goto clean_up_and_ret0;
 
-               ch = i_getch(input);
-               nommu_addchr(as_string, ch);
-# if ENABLE_SH_MATH_SUPPORT
-               if (i_peek(input) == '(') {
-                       ch = i_getch(input);
-                       nommu_addchr(as_string, ch);
-                       o_addchr(dest, SPECIAL_VAR_SYMBOL);
-                       o_addchr(dest, /*quote_mask |*/ '+');
-                       if (!BB_MMU)
-                               pos = dest->length;
-                       add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG);
-                       if (as_string) {
-                               o_addstr(as_string, dest->data + pos);
-                               o_addchr(as_string, ')');
-                               o_addchr(as_string, ')');
+#else /* Older, bigger, but more correct code */
+
+                       rcode = setup_redirects(command, squirrel);
+                       restore_redirects(squirrel);
+                       /* Set shell variables */
+                       if (G_x_mode)
+                               bb_putchar_stderr('+');
+                       while (*argv) {
+                               char *p = expand_string_to_string(*argv, /*unbackslash:*/ 1);
+                               if (G_x_mode)
+                                       fprintf(stderr, " %s", p);
+                               debug_printf_exec("set shell var:'%s'->'%s'\n",
+                                               *argv, p);
+                               set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
+                               /* Do we need to flag set_local_var() errors?
+                                * "assignment to readonly var" and "putenv error"
+                                */
+                               argv++;
                        }
-                       o_addchr(dest, SPECIAL_VAR_SYMBOL);
-                       break;
+                       if (G_x_mode)
+                               bb_putchar_stderr('\n');
+                       /* Redirect error sets $? to 1. Otherwise,
+                        * if evaluating assignment value set $?, retain it.
+                        * Try "false; q=`exit 2`; echo $?" - should print 2: */
+                       if (rcode == 0)
+                               rcode = G.last_exitcode;
+                       IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
+                       debug_leave();
+                       debug_printf_exec("run_pipe: return %d\n", rcode);
+                       return rcode;
+#endif
+               }
+
+               /* Expand the rest into (possibly) many strings each */
+#if ENABLE_HUSH_BASH_COMPAT
+               if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) {
+                       argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
+               } else
+#endif
+               {
+                       argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
+               }
+
+               /* if someone gives us an empty string: `cmd with empty output` */
+               if (!argv_expanded[0]) {
+                       free(argv_expanded);
+                       debug_leave();
+                       return G.last_exitcode;
                }
+
+               x = find_builtin(argv_expanded[0]);
+#if ENABLE_HUSH_FUNCTIONS
+               funcp = NULL;
+               if (!x)
+                       funcp = find_function(argv_expanded[0]);
+#endif
+               if (x || funcp) {
+                       if (!funcp) {
+                               if (x->b_function == builtin_exec && argv_expanded[1] == NULL) {
+                                       debug_printf("exec with redirects only\n");
+                                       rcode = setup_redirects(command, NULL);
+                                       goto clean_up_and_ret1;
+                               }
+                       }
+                       rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded);
+                       if (rcode == 0) {
+                               if (!funcp) {
+                                       debug_printf_exec(": builtin '%s' '%s'...\n",
+                                               x->b_cmd, argv_expanded[1]);
+                                       fflush_all();
+                                       rcode = x->b_function(argv_expanded) & 0xff;
+                                       fflush_all();
+                               }
+#if ENABLE_HUSH_FUNCTIONS
+                               else {
+# if ENABLE_HUSH_LOCAL
+                                       struct variable **sv;
+                                       sv = G.shadowed_vars_pp;
+                                       G.shadowed_vars_pp = &old_vars;
 # endif
-# if ENABLE_HUSH_TICK
-               o_addchr(dest, SPECIAL_VAR_SYMBOL);
-               o_addchr(dest, quote_mask | '`');
-               if (!BB_MMU)
-                       pos = dest->length;
-               add_till_closing_bracket(dest, input, ')');
-               if (as_string) {
-                       o_addstr(as_string, dest->data + pos);
-                       o_addchr(as_string, ')');
-               }
-               o_addchr(dest, SPECIAL_VAR_SYMBOL);
+                                       debug_printf_exec(": function '%s' '%s'...\n",
+                                               funcp->name, argv_expanded[1]);
+                                       rcode = run_function(funcp, argv_expanded) & 0xff;
+# if ENABLE_HUSH_LOCAL
+                                       G.shadowed_vars_pp = sv;
 # endif
-               break;
-       }
+                               }
 #endif
-       case '_':
-               ch = i_getch(input);
-               nommu_addchr(as_string, ch);
-               ch = i_peek(input);
-               if (isalnum(ch)) { /* it's $_name or $_123 */
-                       ch = '_';
-                       goto make_var;
+                       }
+ clean_up_and_ret:
+                       unset_vars(new_env);
+                       add_vars(old_vars);
+/* clean_up_and_ret0: */
+                       restore_redirects(squirrel);
+ clean_up_and_ret1:
+                       free(argv_expanded);
+                       IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
+                       debug_leave();
+                       debug_printf_exec("run_pipe return %d\n", rcode);
+                       return rcode;
                }
-               /* else: it's $_ */
-       /* TODO: $_ and $-: */
-       /* $_ Shell or shell script name; or last argument of last command
-        * (if last command wasn't a pipe; if it was, bash sets $_ to "");
-        * but in command's env, set to full pathname used to invoke it */
-       /* $- Option flags set by set builtin or shell options (-i etc) */
-       default:
-               o_addQchr(dest, '$');
-       }
-       debug_printf_parse("parse_dollar return 0\n");
-       return 0;
-#undef as_string
-}
-
-#if BB_MMU
-#define parse_stream_dquoted(as_string, dest, input, dquote_end) \
-       parse_stream_dquoted(dest, input, dquote_end)
-#define as_string NULL
-#endif
-static int parse_stream_dquoted(o_string *as_string,
-               o_string *dest,
-               struct in_str *input,
-               int dquote_end)
-{
-       int ch;
-       int next;
 
- again:
-       ch = i_getch(input);
-       if (ch != EOF)
-               nommu_addchr(as_string, ch);
-       if (ch == dquote_end) { /* may be only '"' or EOF */
-               if (dest->o_assignment == NOT_ASSIGNMENT)
-                       dest->o_escape ^= 1;
-               debug_printf_parse("parse_stream_dquoted return 0\n");
-               return 0;
-       }
-       /* note: can't move it above ch == dquote_end check! */
-       if (ch == EOF) {
-               syntax_error_unterm_ch('"');
-               /*xfunc_die(); - redundant */
-       }
-       next = '\0';
-       if (ch != '\n') {
-               next = i_peek(input);
-       }
-       debug_printf_parse("\" ch=%c (%d) escape=%d\n",
-                                       ch, ch, dest->o_escape);
-       if (ch == '\\') {
-               if (next == EOF) {
-                       syntax_error("\\<eof>");
-                       xfunc_die();
-               }
-               /* bash:
-                * "The backslash retains its special meaning [in "..."]
-                * only when followed by one of the following characters:
-                * $, `, ", \, or <newline>.  A double quote may be quoted
-                * within double quotes by preceding it with a backslash."
-                */
-               if (strchr("$`\"\\\n", next) != NULL) {
-                       ch = i_getch(input);
-                       if (ch != '\n') {
-                               o_addqchr(dest, ch);
-                               nommu_addchr(as_string, ch);
+               if (ENABLE_FEATURE_SH_NOFORK) {
+                       int n = find_applet_by_name(argv_expanded[0]);
+                       if (n >= 0 && APPLET_IS_NOFORK(n)) {
+                               rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded);
+                               if (rcode == 0) {
+                                       debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
+                                               argv_expanded[0], argv_expanded[1]);
+                                       rcode = run_nofork_applet(n, argv_expanded);
+                               }
+                               goto clean_up_and_ret;
                        }
-               } else {
-                       o_addqchr(dest, '\\');
-                       nommu_addchr(as_string, '\\');
-               }
-               goto again;
-       }
-       if (ch == '$') {
-               if (parse_dollar(as_string, dest, input) != 0) {
-                       debug_printf_parse("parse_stream_dquoted return 1: "
-                                       "parse_dollar returned non-0\n");
-                       return 1;
                }
-               goto again;
-       }
-#if ENABLE_HUSH_TICK
-       if (ch == '`') {
-               //unsigned pos = dest->length;
-               o_addchr(dest, SPECIAL_VAR_SYMBOL);
-               o_addchr(dest, 0x80 | '`');
-               add_till_backquote(dest, input);
-               o_addchr(dest, SPECIAL_VAR_SYMBOL);
-               //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
-               goto again;
-       }
-#endif
-       o_addQchr(dest, ch);
-       if (ch == '='
-        && (dest->o_assignment == MAYBE_ASSIGNMENT
-           || dest->o_assignment == WORD_IS_KEYWORD)
-        && is_well_formed_var_name(dest->data, '=')
-       ) {
-               dest->o_assignment = DEFINITELY_ASSIGNMENT;
+               /* It is neither builtin nor applet. We must fork. */
        }
-       goto again;
-#undef as_string
-}
-
-/*
- * Scan input until EOF or end_trigger char.
- * Return a list of pipes to execute, or NULL on EOF
- * or if end_trigger character is met.
- * On syntax error, exit is shell is not interactive,
- * reset parsing machinery and start parsing anew,
- * or return ERR_PTR.
- */
-static struct pipe *parse_stream(char **pstring,
-               struct in_str *input,
-               int end_trigger)
-{
-       struct parse_context ctx;
-       o_string dest = NULL_O_STRING;
-       int is_in_dquote;
-       int heredoc_cnt;
-
-       /* Double-quote state is handled in the state variable is_in_dquote.
-        * A single-quote triggers a bypass of the main loop until its mate is
-        * found.  When recursing, quote state is passed in via dest->o_escape.
-        */
-       debug_printf_parse("parse_stream entered, end_trigger='%c'\n",
-                       end_trigger ? end_trigger : 'X');
-       debug_enter();
 
-       /* If very first arg is "" or '', dest.data may end up NULL.
-        * Preventing this: */
-       o_addchr(&dest, '\0');
-       dest.length = 0;
+ must_fork:
+       /* NB: argv_expanded may already be created, and that
+        * might include `cmd` runs! Do not rerun it! We *must*
+        * use argv_expanded if it's non-NULL */
 
-       G.ifs = get_local_var_value("IFS");
-       if (G.ifs == NULL)
-               G.ifs = defifs;
+       /* Going to fork a child per each pipe member */
+       pi->alive_cmds = 0;
+       next_infd = 0;
 
- reset:
-#if ENABLE_HUSH_INTERACTIVE
-       input->promptmode = 0; /* PS1 */
+       cmd_no = 0;
+       while (cmd_no < pi->num_cmds) {
+               struct fd_pair pipefds;
+#if !BB_MMU
+               volatile nommu_save_t nommu_save;
+               nommu_save.new_env = NULL;
+               nommu_save.old_vars = NULL;
+               nommu_save.argv = NULL;
+               nommu_save.argv_from_re_execing = NULL;
 #endif
-       /* dest.o_assignment = MAYBE_ASSIGNMENT; - already is */
-       initialize_context(&ctx);
-       is_in_dquote = 0;
-       heredoc_cnt = 0;
-       while (1) {
-               const char *is_ifs;
-               const char *is_special;
-               int ch;
-               int next;
-               int redir_fd;
-               redir_type redir_style;
-
-               if (is_in_dquote) {
-                       /* dest.o_quoted = 1; - already is (see below) */
-                       if (parse_stream_dquoted(&ctx.as_string, &dest, input, '"')) {
-                               goto parse_error;
-                       }
-                       /* We reached closing '"' */
-                       is_in_dquote = 0;
+               command = &pi->cmds[cmd_no];
+               cmd_no++;
+               if (command->argv) {
+                       debug_printf_exec(": pipe member '%s' '%s'...\n",
+                                       command->argv[0], command->argv[1]);
+               } else {
+                       debug_printf_exec(": pipe member with no argv\n");
                }
-               ch = i_getch(input);
-               debug_printf_parse(": ch=%c (%d) escape=%d\n",
-                                               ch, ch, dest.o_escape);
-               if (ch == EOF) {
-                       struct pipe *pi;
 
-                       if (heredoc_cnt) {
-                               syntax_error_unterm_str("here document");
-                               goto parse_error;
-                       }
-                       /* end_trigger == '}' case errors out earlier,
-                        * checking only ')' */
-                       if (end_trigger == ')') {
-                               syntax_error_unterm_ch('('); /* exits */
-                               /* goto parse_error; */
-                       }
+               /* pipes are inserted between pairs of commands */
+               pipefds.rd = 0;
+               pipefds.wr = 1;
+               if (cmd_no < pi->num_cmds)
+                       xpiped_pair(pipefds);
 
-                       if (done_word(&dest, &ctx)) {
-                               goto parse_error;
+               command->pid = BB_MMU ? fork() : vfork();
+               if (!command->pid) { /* child */
+#if ENABLE_HUSH_JOB
+                       disable_restore_tty_pgrp_on_exit();
+                       CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
+
+                       /* Every child adds itself to new process group
+                        * with pgid == pid_of_first_child_in_pipe */
+                       if (G.run_list_level == 1 && G_interactive_fd) {
+                               pid_t pgrp;
+                               pgrp = pi->pgrp;
+                               if (pgrp < 0) /* true for 1st process only */
+                                       pgrp = getpid();
+                               if (setpgid(0, pgrp) == 0
+                                && pi->followup != PIPE_BG
+                                && G_saved_tty_pgrp /* we have ctty */
+                               ) {
+                                       /* We do it in *every* child, not just first,
+                                        * to avoid races */
+                                       tcsetpgrp(G_interactive_fd, pgrp);
+                               }
                        }
-                       o_free(&dest);
-                       done_pipe(&ctx, PIPE_SEQ);
-                       pi = ctx.list_head;
-                       /* If we got nothing... */
-                       /* (this makes bare "&" cmd a no-op.
-                        * bash says: "syntax error near unexpected token '&'") */
-                       if (pi->num_cmds == 0
-                           IF_HAS_KEYWORDS( && pi->res_word == RES_NONE)
-                       ) {
-                               free_pipe_list(pi);
-                               pi = NULL;
+#endif
+                       if (pi->alive_cmds == 0 && pi->followup == PIPE_BG) {
+                               /* 1st cmd in backgrounded pipe
+                                * should have its stdin /dev/null'ed */
+                               close(0);
+                               if (open(bb_dev_null, O_RDONLY))
+                                       xopen("/", O_RDONLY);
+                       } else {
+                               xmove_fd(next_infd, 0);
                        }
+                       xmove_fd(pipefds.wr, 1);
+                       if (pipefds.rd > 1)
+                               close(pipefds.rd);
+                       /* Like bash, explicit redirects override pipes,
+                        * and the pipe fd is available for dup'ing. */
+                       if (setup_redirects(command, NULL))
+                               _exit(1);
+
+                       /* Stores to nommu_save list of env vars putenv'ed
+                        * (NOMMU, on MMU we don't need that) */
+                       /* cast away volatility... */
+                       pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded);
+                       /* pseudo_exec() does not return */
+               }
+
+               /* parent or error */
+#if ENABLE_HUSH_FAST
+               G.count_SIGCHLD++;
+//bb_error_msg("[%d] fork in run_pipe: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
+#endif
+               enable_restore_tty_pgrp_on_exit();
 #if !BB_MMU
-                       debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
-                       if (pstring)
-                               *pstring = ctx.as_string.data;
-                       else
-                               o_free_unsafe(&ctx.as_string);
+               /* Clean up after vforked child */
+               free(nommu_save.argv);
+               free(nommu_save.argv_from_re_execing);
+               unset_vars(nommu_save.new_env);
+               add_vars(nommu_save.old_vars);
+#endif
+               free(argv_expanded);
+               argv_expanded = NULL;
+               if (command->pid < 0) { /* [v]fork failed */
+                       /* Clearly indicate, was it fork or vfork */
+                       bb_perror_msg(BB_MMU ? "vfork"+1 : "vfork");
+               } else {
+                       pi->alive_cmds++;
+#if ENABLE_HUSH_JOB
+                       /* Second and next children need to know pid of first one */
+                       if (pi->pgrp < 0)
+                               pi->pgrp = command->pid;
 #endif
-                       debug_leave();
-                       debug_printf_parse("parse_stream return %p\n", pi);
-                       return pi;
                }
-               nommu_addchr(&ctx.as_string, ch);
 
-               next = '\0';
-               if (ch != '\n')
-                       next = i_peek(input);
+               if (cmd_no > 1)
+                       close(next_infd);
+               if (cmd_no < pi->num_cmds)
+                       close(pipefds.wr);
+               /* Pass read (output) pipe end to next iteration */
+               next_infd = pipefds.rd;
+       }
 
-               is_special = "{}<>;&|()#'" /* special outside of "str" */
-                               "\\$\"" IF_HUSH_TICK("`"); /* always special */
-               /* Are { and } special here? */
-               if (ctx.command->argv /* word [word]{... - non-special */
-                || dest.length       /* word{... - non-special */
-                || dest.o_quoted     /* ""{... - non-special */
-                || (next != ';'            /* }; - special */
-                   && next != ')'          /* }) - special */
-                   && next != '&'          /* }& and }&& ... - special */
-                   && next != '|'          /* }|| ... - special */
-                   && !strchr(G.ifs, next) /* {word - non-special */
-                   )
-               ) {
-                       /* They are not special, skip "{}" */
-                       is_special += 2;
-               }
-               is_special = strchr(is_special, ch);
-               is_ifs = strchr(G.ifs, ch);
+       if (!pi->alive_cmds) {
+               debug_leave();
+               debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n");
+               return 1;
+       }
 
-               if (!is_special && !is_ifs) { /* ordinary char */
- ordinary_char:
-                       o_addQchr(&dest, ch);
-                       if ((dest.o_assignment == MAYBE_ASSIGNMENT
-                           || dest.o_assignment == WORD_IS_KEYWORD)
-                        && ch == '='
-                        && is_well_formed_var_name(dest.data, '=')
-                       ) {
-                               dest.o_assignment = DEFINITELY_ASSIGNMENT;
-                       }
-                       continue;
-               }
+       debug_leave();
+       debug_printf_exec("run_pipe return -1 (%u children started)\n", pi->alive_cmds);
+       return -1;
+}
 
-               if (is_ifs) {
-                       if (done_word(&dest, &ctx)) {
-                               goto parse_error;
-                       }
-                       if (ch == '\n') {
+/* NB: called by pseudo_exec, and therefore must not modify any
+ * global data until exec/_exit (we can be a child after vfork!) */
+static int run_list(struct pipe *pi)
+{
 #if ENABLE_HUSH_CASE
-                               /* "case ... in <newline> word) ..." -
-                                * newlines are ignored (but ';' wouldn't be) */
-                               if (ctx.command->argv == NULL
-                                && ctx.ctx_res_w == RES_MATCH
-                               ) {
-                                       continue;
-                               }
+       char *case_word = NULL;
+#endif
+#if ENABLE_HUSH_LOOPS
+       struct pipe *loop_top = NULL;
+       char **for_lcur = NULL;
+       char **for_list = NULL;
+#endif
+       smallint last_followup;
+       smalluint rcode;
+#if ENABLE_HUSH_IF || ENABLE_HUSH_CASE
+       smalluint cond_code = 0;
+#else
+       enum { cond_code = 0 };
+#endif
+#if HAS_KEYWORDS
+       smallint rword;      /* RES_foo */
+       smallint last_rword; /* ditto */
 #endif
-                               /* Treat newline as a command separator. */
-                               done_pipe(&ctx, PIPE_SEQ);
-                               debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt);
-                               if (heredoc_cnt) {
-                                       if (fetch_heredocs(heredoc_cnt, &ctx, input)) {
-                                               goto parse_error;
-                                       }
-                                       heredoc_cnt = 0;
-                               }
-                               dest.o_assignment = MAYBE_ASSIGNMENT;
-                               ch = ';';
-                               /* note: if (is_ifs) continue;
-                                * will still trigger for us */
-                       }
-               }
 
-               /* "cmd}" or "cmd }..." without semicolon or &:
-                * } is an ordinary char in this case, even inside { cmd; }
-                * Pathological example: { ""}; } should exec "}" cmd
-                */
-               if (ch == '}') {
-                       if (!IS_NULL_CMD(ctx.command) /* cmd } */
-                        || dest.length != 0 /* word} */
-                        || dest.o_quoted    /* ""} */
-                       ) {
-                               goto ordinary_char;
-                       }
-                       if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
-                               goto skip_end_trigger;
-                       /* else: } does terminate a group */
-               }
+       debug_printf_exec("run_list start lvl %d\n", G.run_list_level);
+       debug_enter();
 
-               if (end_trigger && end_trigger == ch
-                && (ch != ';' || heredoc_cnt == 0)
-#if ENABLE_HUSH_CASE
-                && (ch != ')'
-                   || ctx.ctx_res_w != RES_MATCH
-                   || (!dest.o_quoted && strcmp(dest.data, "esac") == 0)
-                   )
-#endif
-               ) {
-                       if (heredoc_cnt) {
-                               /* This is technically valid:
-                                * { cat <<HERE; }; echo Ok
-                                * heredoc
-                                * heredoc
-                                * HERE
-                                * but we don't support this.
-                                * We require heredoc to be in enclosing {}/(),
-                                * if any.
-                                */
-                               syntax_error_unterm_str("here document");
-                               goto parse_error;
-                       }
-                       if (done_word(&dest, &ctx)) {
-                               goto parse_error;
+#if ENABLE_HUSH_LOOPS
+       /* Check syntax for "for" */
+       {
+               struct pipe *cpipe;
+               for (cpipe = pi; cpipe; cpipe = cpipe->next) {
+                       if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN)
+                               continue;
+                       /* current word is FOR or IN (BOLD in comments below) */
+                       if (cpipe->next == NULL) {
+                               syntax_error("malformed for");
+                               debug_leave();
+                               debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
+                               return 1;
                        }
-                       done_pipe(&ctx, PIPE_SEQ);
-                       dest.o_assignment = MAYBE_ASSIGNMENT;
-                       /* Do we sit outside of any if's, loops or case's? */
-                       if (!HAS_KEYWORDS
-                        IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
+                       /* "FOR v; do ..." and "for v IN a b; do..." are ok */
+                       if (cpipe->next->res_word == RES_DO)
+                               continue;
+                       /* next word is not "do". It must be "in" then ("FOR v in ...") */
+                       if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */
+                        || cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */
                        ) {
-                               o_free(&dest);
-#if !BB_MMU
-                               debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
-                               if (pstring)
-                                       *pstring = ctx.as_string.data;
-                               else
-                                       o_free_unsafe(&ctx.as_string);
-#endif
+                               syntax_error("malformed for");
                                debug_leave();
-                               debug_printf_parse("parse_stream return %p: "
-                                               "end_trigger char found\n",
-                                               ctx.list_head);
-                               return ctx.list_head;
+                               debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
+                               return 1;
                        }
                }
- skip_end_trigger:
-               if (is_ifs)
-                       continue;
+       }
+#endif
 
-               /* Catch <, > before deciding whether this word is
-                * an assignment. a=1 2>z b=2: b=2 is still assignment */
-               switch (ch) {
-               case '>':
-                       redir_fd = redirect_opt_num(&dest);
-                       if (done_word(&dest, &ctx)) {
-                               goto parse_error;
-                       }
-                       redir_style = REDIRECT_OVERWRITE;
-                       if (next == '>') {
-                               redir_style = REDIRECT_APPEND;
-                               ch = i_getch(input);
-                               nommu_addchr(&ctx.as_string, ch);
-                       }
-#if 0
-                       else if (next == '(') {
-                               syntax_error(">(process) not supported");
-                               goto parse_error;
-                       }
+       /* Past this point, all code paths should jump to ret: label
+        * in order to return, no direct "return" statements please.
+        * This helps to ensure that no memory is leaked. */
+
+#if ENABLE_HUSH_JOB
+       G.run_list_level++;
 #endif
-                       if (parse_redirect(&ctx, redir_fd, redir_style, input))
-                               goto parse_error;
-                       continue; /* back to top of while (1) */
-               case '<':
-                       redir_fd = redirect_opt_num(&dest);
-                       if (done_word(&dest, &ctx)) {
-                               goto parse_error;
+
+#if HAS_KEYWORDS
+       rword = RES_NONE;
+       last_rword = RES_XXXX;
+#endif
+       last_followup = PIPE_SEQ;
+       rcode = G.last_exitcode;
+
+       /* Go through list of pipes, (maybe) executing them. */
+       for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) {
+               if (G.flag_SIGINT)
+                       break;
+
+               IF_HAS_KEYWORDS(rword = pi->res_word;)
+               debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n",
+                               rword, cond_code, last_rword);
+#if ENABLE_HUSH_LOOPS
+               if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR)
+                && loop_top == NULL /* avoid bumping G.depth_of_loop twice */
+               ) {
+                       /* start of a loop: remember where loop starts */
+                       loop_top = pi;
+                       G.depth_of_loop++;
+               }
+#endif
+               /* Still in the same "if...", "then..." or "do..." branch? */
+               if (IF_HAS_KEYWORDS(rword == last_rword &&) 1) {
+                       if ((rcode == 0 && last_followup == PIPE_OR)
+                        || (rcode != 0 && last_followup == PIPE_AND)
+                       ) {
+                               /* It is "<true> || CMD" or "<false> && CMD"
+                                * and we should not execute CMD */
+                               debug_printf_exec("skipped cmd because of || or &&\n");
+                               last_followup = pi->followup;
+                               goto dont_check_jobs_but_continue;
                        }
-                       redir_style = REDIRECT_INPUT;
-                       if (next == '<') {
-                               redir_style = REDIRECT_HEREDOC;
-                               heredoc_cnt++;
-                               debug_printf_parse("++heredoc_cnt=%d\n", heredoc_cnt);
-                               ch = i_getch(input);
-                               nommu_addchr(&ctx.as_string, ch);
-                       } else if (next == '>') {
-                               redir_style = REDIRECT_IO;
-                               ch = i_getch(input);
-                               nommu_addchr(&ctx.as_string, ch);
+               }
+               last_followup = pi->followup;
+               IF_HAS_KEYWORDS(last_rword = rword;)
+#if ENABLE_HUSH_IF
+               if (cond_code) {
+                       if (rword == RES_THEN) {
+                               /* if false; then ... fi has exitcode 0! */
+                               G.last_exitcode = rcode = EXIT_SUCCESS;
+                               /* "if <false> THEN cmd": skip cmd */
+                               continue;
                        }
-#if 0
-                       else if (next == '(') {
-                               syntax_error("<(process) not supported");
-                               goto parse_error;
+               } else {
+                       if (rword == RES_ELSE || rword == RES_ELIF) {
+                               /* "if <true> then ... ELSE/ELIF cmd":
+                                * skip cmd and all following ones */
+                               break;
                        }
-#endif
-                       if (parse_redirect(&ctx, redir_fd, redir_style, input))
-                               goto parse_error;
-                       continue; /* back to top of while (1) */
-               }
-
-               if (dest.o_assignment == MAYBE_ASSIGNMENT
-                /* check that we are not in word in "a=1 2>word b=1": */
-                && !ctx.pending_redirect
-               ) {
-                       /* ch is a special char and thus this word
-                        * cannot be an assignment */
-                       dest.o_assignment = NOT_ASSIGNMENT;
                }
+#endif
+#if ENABLE_HUSH_LOOPS
+               if (rword == RES_FOR) { /* && pi->num_cmds - always == 1 */
+                       if (!for_lcur) {
+                               /* first loop through for */
 
-               /* Note: nommu_addchr(&ctx.as_string, ch) is already done */
+                               static const char encoded_dollar_at[] ALIGN1 = {
+                                       SPECIAL_VAR_SYMBOL, '@' | 0x80, SPECIAL_VAR_SYMBOL, '\0'
+                               }; /* encoded representation of "$@" */
+                               static const char *const encoded_dollar_at_argv[] = {
+                                       encoded_dollar_at, NULL
+                               }; /* argv list with one element: "$@" */
+                               char **vals;
 
-               switch (ch) {
-               case '#':
-                       if (dest.length == 0) {
-                               while (1) {
-                                       ch = i_peek(input);
-                                       if (ch == EOF || ch == '\n')
+                               vals = (char**)encoded_dollar_at_argv;
+                               if (pi->next->res_word == RES_IN) {
+                                       /* if no variable values after "in" we skip "for" */
+                                       if (!pi->next->cmds[0].argv) {
+                                               G.last_exitcode = rcode = EXIT_SUCCESS;
+                                               debug_printf_exec(": null FOR: exitcode EXIT_SUCCESS\n");
                                                break;
-                                       i_getch(input);
-                                       /* note: we do not add it to &ctx.as_string */
-                               }
-                               nommu_addchr(&ctx.as_string, '\n');
-                       } else {
-                               o_addQchr(&dest, ch);
-                       }
-                       break;
-               case '\\':
-                       if (next == EOF) {
-                               syntax_error("\\<eof>");
-                               xfunc_die();
-                       }
-                       ch = i_getch(input);
-                       if (ch != '\n') {
-                               o_addchr(&dest, '\\');
-                               /*nommu_addchr(&ctx.as_string, '\\'); - already done */
-                               o_addchr(&dest, ch);
-                               nommu_addchr(&ctx.as_string, ch);
-                               /* Example: echo Hello \2>file
-                                * we need to know that word 2 is quoted */
-                               dest.o_quoted = 1;
+                                       }
+                                       vals = pi->next->cmds[0].argv;
+                               } /* else: "for var; do..." -> assume "$@" list */
+                               /* create list of variable values */
+                               debug_print_strings("for_list made from", vals);
+                               for_list = expand_strvec_to_strvec(vals);
+                               for_lcur = for_list;
+                               debug_print_strings("for_list", for_list);
                        }
-#if !BB_MMU
-                       else {
-                               /* It's "\<newline>". Remove trailing '\' from ctx.as_string */
-                               ctx.as_string.data[--ctx.as_string.length] = '\0';
+                       if (!*for_lcur) {
+                               /* "for" loop is over, clean up */
+                               free(for_list);
+                               for_list = NULL;
+                               for_lcur = NULL;
+                               break;
                        }
+                       /* Insert next value from for_lcur */
+                       /* note: *for_lcur already has quotes removed, $var expanded, etc */
+                       set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], *for_lcur++), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
+                       continue;
+               }
+               if (rword == RES_IN) {
+                       continue; /* "for v IN list;..." - "in" has no cmds anyway */
+               }
+               if (rword == RES_DONE) {
+                       continue; /* "done" has no cmds too */
+               }
 #endif
-                       break;
-               case '$':
-                       if (parse_dollar(&ctx.as_string, &dest, input) != 0) {
-                               debug_printf_parse("parse_stream parse error: "
-                                       "parse_dollar returned non-0\n");
-                               goto parse_error;
-                       }
-                       break;
-               case '\'':
-                       dest.o_quoted = 1;
-                       while (1) {
-                               ch = i_getch(input);
-                               if (ch == EOF) {
-                                       syntax_error_unterm_ch('\'');
-                                       /*xfunc_die(); - redundant */
-                               }
-                               nommu_addchr(&ctx.as_string, ch);
-                               if (ch == '\'')
+#if ENABLE_HUSH_CASE
+               if (rword == RES_CASE) {
+                       case_word = expand_strvec_to_string(pi->cmds->argv);
+                       continue;
+               }
+               if (rword == RES_MATCH) {
+                       char **argv;
+
+                       if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */
+                               break;
+                       /* all prev words didn't match, does this one match? */
+                       argv = pi->cmds->argv;
+                       while (*argv) {
+                               char *pattern = expand_string_to_string(*argv, /*unbackslash:*/ 1);
+                               /* TODO: which FNM_xxx flags to use? */
+                               cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0);
+                               free(pattern);
+                               if (cond_code == 0) { /* match! we will execute this branch */
+                                       free(case_word); /* make future "word)" stop */
+                                       case_word = NULL;
                                        break;
-                               o_addqchr(&dest, ch);
+                               }
+                               argv++;
                        }
-                       break;
-               case '"':
-                       dest.o_quoted = 1;
-                       is_in_dquote ^= 1; /* invert */
-                       if (dest.o_assignment == NOT_ASSIGNMENT)
-                               dest.o_escape ^= 1;
-                       break;
-#if ENABLE_HUSH_TICK
-               case '`': {
-                       unsigned pos;
-
-                       o_addchr(&dest, SPECIAL_VAR_SYMBOL);
-                       o_addchr(&dest, '`');
-                       pos = dest.length;
-                       add_till_backquote(&dest, input);
-# if !BB_MMU
-                       o_addstr(&ctx.as_string, dest.data + pos);
-                       o_addchr(&ctx.as_string, '`');
-# endif
-                       o_addchr(&dest, SPECIAL_VAR_SYMBOL);
-                       //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos);
-                       break;
+                       continue;
+               }
+               if (rword == RES_CASE_BODY) { /* inside of a case branch */
+                       if (cond_code != 0)
+                               continue; /* not matched yet, skip this pipe */
                }
 #endif
-               case ';':
-#if ENABLE_HUSH_CASE
- case_semi:
+               /* Just pressing <enter> in shell should check for jobs.
+                * OTOH, in non-interactive shell this is useless
+                * and only leads to extra job checks */
+               if (pi->num_cmds == 0) {
+                       if (G_interactive_fd)
+                               goto check_jobs_and_continue;
+                       continue;
+               }
+
+               /* After analyzing all keywords and conditions, we decided
+                * to execute this pipe. NB: have to do checkjobs(NULL)
+                * after run_pipe to collect any background children,
+                * even if list execution is to be stopped. */
+               debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds);
+               {
+                       int r;
+#if ENABLE_HUSH_LOOPS
+                       G.flag_break_continue = 0;
 #endif
-                       if (done_word(&dest, &ctx)) {
-                               goto parse_error;
-                       }
-                       done_pipe(&ctx, PIPE_SEQ);
-#if ENABLE_HUSH_CASE
-                       /* Eat multiple semicolons, detect
-                        * whether it means something special */
-                       while (1) {
-                               ch = i_peek(input);
-                               if (ch != ';')
-                                       break;
-                               ch = i_getch(input);
-                               nommu_addchr(&ctx.as_string, ch);
-                               if (ctx.ctx_res_w == RES_CASE_BODY) {
-                                       ctx.ctx_dsemicolon = 1;
-                                       ctx.ctx_res_w = RES_MATCH;
-                                       break;
+                       rcode = r = run_pipe(pi); /* NB: rcode is a smallint */
+                       if (r != -1) {
+                               /* We ran a builtin, function, or group.
+                                * rcode is already known
+                                * and we don't need to wait for anything. */
+                               G.last_exitcode = rcode;
+                               debug_printf_exec(": builtin/func exitcode %d\n", rcode);
+                               check_and_run_traps();
+#if ENABLE_HUSH_LOOPS
+                               /* Was it "break" or "continue"? */
+                               if (G.flag_break_continue) {
+                                       smallint fbc = G.flag_break_continue;
+                                       /* We might fall into outer *loop*,
+                                        * don't want to break it too */
+                                       if (loop_top) {
+                                               G.depth_break_continue--;
+                                               if (G.depth_break_continue == 0)
+                                                       G.flag_break_continue = 0;
+                                               /* else: e.g. "continue 2" should *break* once, *then* continue */
+                                       } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
+                                       if (G.depth_break_continue != 0 || fbc == BC_BREAK) {
+                                               checkjobs(NULL);
+                                               break;
+                                       }
+                                       /* "continue": simulate end of loop */
+                                       rword = RES_DONE;
+                                       continue;
                                }
-                       }
 #endif
- new_cmd:
-                       /* We just finished a cmd. New one may start
-                        * with an assignment */
-                       dest.o_assignment = MAYBE_ASSIGNMENT;
-                       break;
-               case '&':
-                       if (done_word(&dest, &ctx)) {
-                               goto parse_error;
-                       }
-                       if (next == '&') {
-                               ch = i_getch(input);
-                               nommu_addchr(&ctx.as_string, ch);
-                               done_pipe(&ctx, PIPE_AND);
-                       } else {
-                               done_pipe(&ctx, PIPE_BG);
-                       }
-                       goto new_cmd;
-               case '|':
-                       if (done_word(&dest, &ctx)) {
-                               goto parse_error;
-                       }
-#if ENABLE_HUSH_CASE
-                       if (ctx.ctx_res_w == RES_MATCH)
-                               break; /* we are in case's "word | word)" */
+#if ENABLE_HUSH_FUNCTIONS
+                               if (G.flag_return_in_progress == 1) {
+                                       checkjobs(NULL);
+                                       break;
+                               }
 #endif
-                       if (next == '|') { /* || */
-                               ch = i_getch(input);
-                               nommu_addchr(&ctx.as_string, ch);
-                               done_pipe(&ctx, PIPE_OR);
-                       } else {
-                               /* we could pick up a file descriptor choice here
-                                * with redirect_opt_num(), but bash doesn't do it.
-                                * "echo foo 2| cat" yields "foo 2". */
-                               done_command(&ctx);
-#if !BB_MMU
-                               o_reset_to_empty_unquoted(&ctx.as_string);
+                       } else if (pi->followup == PIPE_BG) {
+                               /* What does bash do with attempts to background builtins? */
+                               /* even bash 3.2 doesn't do that well with nested bg:
+                                * try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
+                                * I'm NOT treating inner &'s as jobs */
+                               check_and_run_traps();
+#if ENABLE_HUSH_JOB
+                               if (G.run_list_level == 1)
+                                       insert_bg_job(pi);
 #endif
-                       }
-                       goto new_cmd;
-               case '(':
-#if ENABLE_HUSH_CASE
-                       /* "case... in [(]word)..." - skip '(' */
-                       if (ctx.ctx_res_w == RES_MATCH
-                        && ctx.command->argv == NULL /* not (word|(... */
-                        && dest.length == 0 /* not word(... */
-                        && dest.o_quoted == 0 /* not ""(... */
-                       ) {
-                               continue;
-                       }
+                               /* Last command's pid goes to $! */
+                               G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid;
+                               G.last_exitcode = rcode = EXIT_SUCCESS;
+                               debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n");
+                       } else {
+#if ENABLE_HUSH_JOB
+                               if (G.run_list_level == 1 && G_interactive_fd) {
+                                       /* Waits for completion, then fg's main shell */
+                                       rcode = checkjobs_and_fg_shell(pi);
+                                       debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode);
+                                       check_and_run_traps();
+                               } else
 #endif
-               case '{':
-                       if (parse_group(&dest, &ctx, input, ch) != 0) {
-                               goto parse_error;
+                               { /* This one just waits for completion */
+                                       rcode = checkjobs(pi);
+                                       debug_printf_exec(": checkjobs exitcode %d\n", rcode);
+                                       check_and_run_traps();
+                               }
+                               G.last_exitcode = rcode;
                        }
-                       goto new_cmd;
-               case ')':
-#if ENABLE_HUSH_CASE
-                       if (ctx.ctx_res_w == RES_MATCH)
-                               goto case_semi;
-#endif
-               case '}':
-                       /* proper use of this character is caught by end_trigger:
-                        * if we see {, we call parse_group(..., end_trigger='}')
-                        * and it will match } earlier (not here). */
-                       syntax_error_unexpected_ch(ch);
-                       goto parse_error;
-               default:
-                       if (HUSH_DEBUG)
-                               bb_error_msg_and_die("BUG: unexpected %c\n", ch);
                }
-       } /* while (1) */
 
- parse_error:
-       {
-               struct parse_context *pctx;
-               IF_HAS_KEYWORDS(struct parse_context *p2;)
-
-               /* Clean up allocated tree.
-                * Sample for finding leaks on syntax error recovery path.
-                * Run it from interactive shell, watch pmap `pidof hush`.
-                * while if false; then false; fi; do break; fi
-                * Samples to catch leaks at execution:
-                * while if (true | {true;}); then echo ok; fi; do break; done
-                * while if (true | {true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done
-                */
-               pctx = &ctx;
-               do {
-                       /* Update pipe/command counts,
-                        * otherwise freeing may miss some */
-                       done_pipe(pctx, PIPE_SEQ);
-                       debug_printf_clean("freeing list %p from ctx %p\n",
-                                       pctx->list_head, pctx);
-                       debug_print_tree(pctx->list_head, 0);
-                       free_pipe_list(pctx->list_head);
-                       debug_printf_clean("freed list %p\n", pctx->list_head);
-#if !BB_MMU
-                       o_free_unsafe(&pctx->as_string);
+               /* Analyze how result affects subsequent commands */
+#if ENABLE_HUSH_IF
+               if (rword == RES_IF || rword == RES_ELIF)
+                       cond_code = rcode;
 #endif
-                       IF_HAS_KEYWORDS(p2 = pctx->stack;)
-                       if (pctx != &ctx) {
-                               free(pctx);
+ check_jobs_and_continue:
+               checkjobs(NULL);
+ dont_check_jobs_but_continue: ;
+#if ENABLE_HUSH_LOOPS
+               /* Beware of "while false; true; do ..."! */
+               if (pi->next
+                && (pi->next->res_word == RES_DO || pi->next->res_word == RES_DONE)
+                /* check for RES_DONE is needed for "while ...; do \n done" case */
+               ) {
+                       if (rword == RES_WHILE) {
+                               if (rcode) {
+                                       /* "while false; do...done" - exitcode 0 */
+                                       G.last_exitcode = rcode = EXIT_SUCCESS;
+                                       debug_printf_exec(": while expr is false: breaking (exitcode:EXIT_SUCCESS)\n");
+                                       break;
+                               }
+                       }
+                       if (rword == RES_UNTIL) {
+                               if (!rcode) {
+                                       debug_printf_exec(": until expr is true: breaking\n");
+                                       break;
+                               }
                        }
-                       IF_HAS_KEYWORDS(pctx = p2;)
-               } while (HAS_KEYWORDS && pctx);
-               /* Free text, clear all dest fields */
-               o_free(&dest);
-               /* If we are not in top-level parse, we return,
-                * our caller will propagate error.
-                */
-               if (end_trigger != ';') {
-#if !BB_MMU
-                       if (pstring)
-                               *pstring = NULL;
-#endif
-                       debug_leave();
-                       return ERR_PTR;
                }
-               /* Discard cached input, force prompt */
-               input->p = NULL;
-               IF_HUSH_INTERACTIVE(input->promptme = 1;)
-               goto reset;
-       }
+#endif
+       } /* for (pi) */
+
+#if ENABLE_HUSH_JOB
+       G.run_list_level--;
+#endif
+#if ENABLE_HUSH_LOOPS
+       if (loop_top)
+               G.depth_of_loop--;
+       free(for_list);
+#endif
+#if ENABLE_HUSH_CASE
+       free(case_word);
+#endif
+       debug_leave();
+       debug_printf_exec("run_list lvl %d return %d\n", G.run_list_level + 1, rcode);
+       return rcode;
 }
 
-/* Executing from string: eval, sh -c '...'
- *          or from file: /etc/profile, . file, sh <script>, sh (intereactive)
- * end_trigger controls how often we stop parsing
- * NUL: parse all, execute, return
- * ';': parse till ';' or newline, execute, repeat till EOF
- */
-static void parse_and_run_stream(struct in_str *inp, int end_trigger)
+/* Select which version we will use */
+static int run_and_free_list(struct pipe *pi)
 {
-       /* Why we need empty flag?
-        * An obscure corner case "false; ``; echo $?":
-        * empty command in `` should still set $? to 0.
-        * But we can't just set $? to 0 at the start,
-        * this breaks "false; echo `echo $?`" case.
-        */
-       bool empty = 1;
-       while (1) {
-               struct pipe *pipe_list;
-
-               pipe_list = parse_stream(NULL, inp, end_trigger);
-               if (!pipe_list) { /* EOF */
-                       if (empty)
-                               G.last_exitcode = 0;
-                       break;
-               }
-               debug_print_tree(pipe_list, 0);
-               debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
-               run_and_free_list(pipe_list);
-               empty = 0;
+       int rcode = 0;
+       debug_printf_exec("run_and_free_list entered\n");
+       if (!G.o_opt[OPT_O_NOEXEC]) {
+               debug_printf_exec(": run_list: 1st pipe with %d cmds\n", pi->num_cmds);
+               rcode = run_list(pi);
        }
+       /* free_pipe_list has the side effect of clearing memory.
+        * In the long run that function can be merged with run_list,
+        * but doing that now would hobble the debugging effort. */
+       free_pipe_list(pi);
+       debug_printf_exec("run_and_free_list return %d\n", rcode);
+       return rcode;
 }
 
-static void parse_and_run_string(const char *s)
-{
-       struct in_str input;
-       setup_string_in_str(&input, s);
-       parse_and_run_stream(&input, '\0');
-}
 
-static void parse_and_run_file(FILE *f)
+static void install_sighandlers(unsigned mask)
 {
-       struct in_str input;
-       setup_file_in_str(&input, f);
-       parse_and_run_stream(&input, ';');
+       sighandler_t old_handler;
+       unsigned sig = 0;
+       while ((mask >>= 1) != 0) {
+               sig++;
+               if (!(mask & 1))
+                       continue;
+               old_handler = install_sighandler(sig, pick_sighandler(sig));
+               /* POSIX allows shell to re-enable SIGCHLD
+                * even if it was SIG_IGN on entry.
+                * Therefore we skip IGN check for it:
+                */
+               if (sig == SIGCHLD)
+                       continue;
+               if (old_handler == SIG_IGN) {
+                       /* oops... restore back to IGN, and record this fact */
+                       install_sighandler(sig, old_handler);
+                       if (!G.traps)
+                               G.traps = xzalloc(sizeof(G.traps[0]) * NSIG);
+                       free(G.traps[sig]);
+                       G.traps[sig] = xzalloc(1); /* == xstrdup(""); */
+               }
+       }
 }
 
 /* Called a few times only (or even once if "sh -c") */
-static void init_sigmasks(void)
+static void install_special_sighandlers(void)
 {
-       unsigned sig;
        unsigned mask;
-       sigset_t old_blocked_set;
 
-       if (!G.inherited_set_is_saved) {
-               sigprocmask(SIG_SETMASK, NULL, &G.blocked_set);
-               G.inherited_set = G.blocked_set;
-       }
-       old_blocked_set = G.blocked_set;
-
-       mask = (1 << SIGQUIT);
+       /* Which signals are shell-special? */
+       mask = (1 << SIGQUIT) | (1 << SIGCHLD);
        if (G_interactive_fd) {
-               mask = (1 << SIGQUIT) | SPECIAL_INTERACTIVE_SIGS;
+               mask |= SPECIAL_INTERACTIVE_SIGS;
                if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */
-                       mask |= SPECIAL_JOB_SIGS;
+                       mask |= SPECIAL_JOBSTOP_SIGS;
        }
-       G.non_DFL_mask = mask;
-
-       sig = 0;
-       while (mask) {
-               if (mask & 1)
-                       sigaddset(&G.blocked_set, sig);
-               mask >>= 1;
-               sig++;
+       /* Careful, do not re-install handlers we already installed */
+       if (G.special_sig_mask != mask) {
+               unsigned diff = mask & ~G.special_sig_mask;
+               G.special_sig_mask = mask;
+               install_sighandlers(diff);
        }
-       sigdelset(&G.blocked_set, SIGCHLD);
-
-       if (memcmp(&old_blocked_set, &G.blocked_set, sizeof(old_blocked_set)) != 0)
-               sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
-
-       /* POSIX allows shell to re-enable SIGCHLD
-        * even if it was SIG_IGN on entry */
-#if ENABLE_HUSH_FAST
-       G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
-       if (!G.inherited_set_is_saved)
-               signal(SIGCHLD, SIGCHLD_handler);
-#else
-       if (!G.inherited_set_is_saved)
-               signal(SIGCHLD, SIG_DFL);
-#endif
-
-       G.inherited_set_is_saved = 1;
 }
 
 #if ENABLE_HUSH_JOB
 /* helper */
-static void maybe_set_to_sigexit(int sig)
-{
-       void (*handler)(int);
-       /* non_DFL_mask'ed signals are, well, masked,
-        * no need to set handler for them.
-        */
-       if (!((G.non_DFL_mask >> sig) & 1)) {
-               handler = signal(sig, sigexit);
-               if (handler == SIG_IGN) /* oops... restore back to IGN! */
-                       signal(sig, handler);
-       }
-}
 /* Set handlers to restore tty pgrp and exit */
-static void set_fatal_handlers(void)
-{
-       /* We _must_ restore tty pgrp on fatal signals */
-       if (HUSH_DEBUG) {
-               maybe_set_to_sigexit(SIGILL );
-               maybe_set_to_sigexit(SIGFPE );
-               maybe_set_to_sigexit(SIGBUS );
-               maybe_set_to_sigexit(SIGSEGV);
-               maybe_set_to_sigexit(SIGTRAP);
-       } /* else: hush is perfect. what SEGV? */
-       maybe_set_to_sigexit(SIGABRT);
+static void install_fatal_sighandlers(void)
+{
+       unsigned mask;
+
+       /* We will restore tty pgrp on these signals */
+       mask = 0
+               + (1 << SIGILL ) * HUSH_DEBUG
+               + (1 << SIGFPE ) * HUSH_DEBUG
+               + (1 << SIGBUS ) * HUSH_DEBUG
+               + (1 << SIGSEGV) * HUSH_DEBUG
+               + (1 << SIGTRAP) * HUSH_DEBUG
+               + (1 << SIGABRT)
        /* bash 3.2 seems to handle these just like 'fatal' ones */
-       maybe_set_to_sigexit(SIGPIPE);
-       maybe_set_to_sigexit(SIGALRM);
-       /* if we are interactive, SIGHUP, SIGTERM and SIGINT are masked.
+               + (1 << SIGPIPE)
+               + (1 << SIGALRM)
+       /* if we are interactive, SIGHUP, SIGTERM and SIGINT are special sigs.
         * if we aren't interactive... but in this case
-        * we never want to restore pgrp on exit, and this fn is not called */
-       /*maybe_set_to_sigexit(SIGHUP );*/
-       /*maybe_set_to_sigexit(SIGTERM);*/
-       /*maybe_set_to_sigexit(SIGINT );*/
+        * we never want to restore pgrp on exit, and this fn is not called
+        */
+               /*+ (1 << SIGHUP )*/
+               /*+ (1 << SIGTERM)*/
+               /*+ (1 << SIGINT )*/
+       ;
+       G_fatal_sig_mask = mask;
+
+       install_sighandlers(mask);
 }
 #endif
 
-static int set_mode(const char cstate, const char mode)
+static int set_mode(int state, char mode, const char *o_opt)
 {
-       int state = (cstate == '-' ? 1 : 0);
+       int idx;
        switch (mode) {
-               case 'n': G.fake_mode = state; break;
-               case 'x': /*G.debug_mode = state;*/ break;
-               default:  return EXIT_FAILURE;
+       case 'n':
+               G.o_opt[OPT_O_NOEXEC] = state;
+               break;
+       case 'x':
+               IF_HUSH_MODE_X(G_x_mode = state;)
+               break;
+       case 'o':
+               if (!o_opt) {
+                       /* "set -+o" without parameter.
+                        * in bash, set -o produces this output:
+                        *  pipefail        off
+                        * and set +o:
+                        *  set +o pipefail
+                        * We always use the second form.
+                        */
+                       const char *p = o_opt_strings;
+                       idx = 0;
+                       while (*p) {
+                               printf("set %co %s\n", (G.o_opt[idx] ? '-' : '+'), p);
+                               idx++;
+                               p += strlen(p) + 1;
+                       }
+                       break;
+               }
+               idx = index_in_strings(o_opt_strings, o_opt);
+               if (idx >= 0) {
+                       G.o_opt[idx] = state;
+                       break;
+               }
+       default:
+               return EXIT_FAILURE;
        }
        return EXIT_SUCCESS;
 }
@@ -6933,31 +7735,37 @@ static int set_mode(const char cstate, const char mode)
 int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int hush_main(int argc, char **argv)
 {
-       static const struct variable const_shell_ver = {
-               .next = NULL,
-               .varstr = (char*)hush_version_str,
-               .max_len = 1, /* 0 can provoke free(name) */
-               .flg_export = 1,
-               .flg_read_only = 1,
+       enum {
+               OPT_login = (1 << 0),
        };
+       unsigned flags;
        int opt;
        unsigned builtin_argc;
        char **e;
        struct variable *cur_var;
+       struct variable *shell_ver;
 
        INIT_G();
-       if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */
+       if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
                G.last_exitcode = EXIT_SUCCESS;
+#if ENABLE_HUSH_FAST
+       G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
+#endif
 #if !BB_MMU
        G.argv0_for_re_execing = argv[0];
 #endif
        /* Deal with HUSH_VERSION */
-       G.shell_ver = const_shell_ver; /* copying struct here */
-       G.top_var = &G.shell_ver;
+       shell_ver = xzalloc(sizeof(*shell_ver));
+       shell_ver->flg_export = 1;
+       shell_ver->flg_read_only = 1;
+       /* Code which handles ${var<op>...} needs writable values for all variables,
+        * therefore we xstrdup: */
+       shell_ver->varstr = xstrdup(hush_version_str);
+       /* Create shell local variables from the values
+        * currently living in the environment */
        debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
        unsetenv("HUSH_VERSION"); /* in case it exists in initial env */
-       /* Initialize our shell local variables with the values
-        * currently living in the environment */
+       G.top_var = shell_ver;
        cur_var = G.top_var;
        e = environ;
        if (e) while (*e) {
@@ -6971,9 +7779,9 @@ int hush_main(int argc, char **argv)
                }
                e++;
        }
-       /* reinstate HUSH_VERSION */
-       debug_printf_env("putenv '%s'\n", hush_version_str);
-       putenv((char *)hush_version_str);
+       /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */
+       debug_printf_env("putenv '%s'\n", shell_ver->varstr);
+       putenv(shell_ver->varstr);
 
        /* Export PWD */
        set_pwd_var(/*exp:*/ 1);
@@ -7017,8 +7825,7 @@ int hush_main(int argc, char **argv)
 #if ENABLE_FEATURE_EDITING
        G.line_input_state = new_line_input_t(FOR_SHELL);
 #endif
-       G.global_argc = argc;
-       G.global_argv = argv;
+
        /* Initialize some more globals to non-zero values */
        cmdedit_update_prompt();
 
@@ -7030,17 +7837,18 @@ int hush_main(int argc, char **argv)
        }
 
        /* Shell is non-interactive at first. We need to call
-        * init_sigmasks() if we are going to execute "sh <script>",
+        * install_special_sighandlers() if we are going to execute "sh <script>",
         * "sh -c <cmds>" or login shell's /etc/profile and friends.
-        * If we later decide that we are interactive, we run init_sigmasks()
+        * If we later decide that we are interactive, we run install_special_sighandlers()
         * in order to intercept (more) signals.
         */
 
        /* Parse options */
        /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
+       flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
        builtin_argc = 0;
        while (1) {
-               opt = getopt(argc, argv, "+c:xins"
+               opt = getopt(argc, argv, "+c:xinsl"
 #if !BB_MMU
                                "<:$:R:V:"
 # if ENABLE_HUSH_FUNCTIONS
@@ -7072,12 +7880,13 @@ int hush_main(int argc, char **argv)
                                /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */
                                const struct built_in_command *x;
 
-                               init_sigmasks();
+                               install_special_sighandlers();
                                x = find_builtin(optarg);
                                if (x) { /* paranoia */
                                        G.global_argc -= builtin_argc; /* skip [BARGV...] "" */
                                        G.global_argv += builtin_argc;
                                        G.global_argv[-1] = NULL; /* replace "" */
+                                       fflush_all();
                                        G.last_exitcode = x->b_function(argv + optind - 1);
                                }
                                goto final_return;
@@ -7088,7 +7897,7 @@ int hush_main(int argc, char **argv)
                                G.global_argv[0] = argv[0];
                                G.global_argc++;
                        } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
-                       init_sigmasks();
+                       install_special_sighandlers();
                        parse_and_run_string(optarg);
                        goto final_return;
                case 'i':
@@ -7100,6 +7909,9 @@ int hush_main(int argc, char **argv)
                        /* "-s" means "read from stdin", but this is how we always
                         * operate, so simply do nothing here. */
                        break;
+               case 'l':
+                       flags |= OPT_login;
+                       break;
 #if !BB_MMU
                case '<': /* "big heredoc" support */
                        full_write1_str(optarg);
@@ -7120,15 +7932,14 @@ int hush_main(int argc, char **argv)
                        empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
                        if (empty_trap_mask != 0) {
                                int sig;
-                               init_sigmasks();
+                               install_special_sighandlers();
                                G.traps = xzalloc(sizeof(G.traps[0]) * NSIG);
                                for (sig = 1; sig < NSIG; sig++) {
                                        if (empty_trap_mask & (1LL << sig)) {
                                                G.traps[sig] = xzalloc(1); /* == xstrdup(""); */
-                                               sigaddset(&G.blocked_set, sig);
+                                               install_sighandler(sig, SIG_IGN);
                                        }
                                }
-                               sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
                        }
 # if ENABLE_HUSH_LOOPS
                        optarg++;
@@ -7153,7 +7964,7 @@ int hush_main(int argc, char **argv)
 #endif
                case 'n':
                case 'x':
-                       if (!set_mode('-', opt))
+                       if (set_mode(1, opt, NULL) == 0) /* no error */
                                break;
                default:
 #ifndef BB_VER
@@ -7166,19 +7977,24 @@ int hush_main(int argc, char **argv)
                }
        } /* option parsing loop */
 
+       /* Skip options. Try "hush -l": $1 should not be "-l"! */
+       G.global_argc = argc - (optind - 1);
+       G.global_argv = argv + (optind - 1);
+       G.global_argv[0] = argv[0];
+
        if (!G.root_pid) {
                G.root_pid = getpid();
                G.root_ppid = getppid();
        }
 
        /* If we are login shell... */
-       if (argv[0] && argv[0][0] == '-') {
+       if (flags & OPT_login) {
                FILE *input;
                debug_printf("sourcing /etc/profile\n");
                input = fopen_for_read("/etc/profile");
                if (input != NULL) {
                        close_on_exec_on(fileno(input));
-                       init_sigmasks();
+                       install_special_sighandlers();
                        parse_and_run_file(input);
                        fclose(input);
                }
@@ -7191,19 +8007,19 @@ int hush_main(int argc, char **argv)
                 */
        }
 
-       if (argv[optind]) {
+       if (G.global_argv[1]) {
                FILE *input;
                /*
                 * "bash <script>" (which is never interactive (unless -i?))
                 * sources $BASH_ENV here (without scanning $PATH).
                 * If called as sh, does the same but with $ENV.
                 */
-               debug_printf("running script '%s'\n", argv[optind]);
-               G.global_argv = argv + optind;
-               G.global_argc = argc - optind;
-               input = xfopen_for_read(argv[optind]);
+               G.global_argc--;
+               G.global_argv++;
+               debug_printf("running script '%s'\n", G.global_argv[0]);
+               input = xfopen_for_read(G.global_argv[0]);
                close_on_exec_on(fileno(input));
-               init_sigmasks();
+               install_special_sighandlers();
                parse_and_run_file(input);
 #if ENABLE_FEATURE_CLEAN_UP
                fclose(input);
@@ -7212,7 +8028,7 @@ int hush_main(int argc, char **argv)
        }
 
        /* Up to here, shell was non-interactive. Now it may become one.
-        * NB: don't forget to (re)run init_sigmasks() as needed.
+        * NB: don't forget to (re)run install_special_sighandlers() as needed.
         */
 
        /* A shell is interactive if the '-i' flag was given,
@@ -7264,12 +8080,12 @@ int hush_main(int argc, char **argv)
                        }
                }
 
-               /* Block some signals */
-               init_sigmasks();
+               /* Install more signal handlers */
+               install_special_sighandlers();
 
                if (G_saved_tty_pgrp) {
                        /* Set other signals to restore saved_tty_pgrp */
-                       set_fatal_handlers();
+                       install_fatal_sighandlers();
                        /* Put ourselves in our own process group
                         * (bash, too, does this only if ctty is available) */
                        bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
@@ -7279,8 +8095,29 @@ int hush_main(int argc, char **argv)
                /* -1 is special - makes xfuncs longjmp, not exit
                 * (we reset die_sleep = 0 whereever we [v]fork) */
                enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */
+
+# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
+               {
+                       const char *hp = get_local_var_value("HISTFILE");
+                       if (!hp) {
+                               hp = get_local_var_value("HOME");
+                               if (hp)
+                                       hp = concat_path_file(hp, ".hush_history");
+                       } else {
+                               hp = xstrdup(hp);
+                       }
+                       if (hp) {
+                               G.line_input_state->hist_file = hp;
+                               //set_local_var(xasprintf("HISTFILE=%s", ...));
+                       }
+#  if ENABLE_FEATURE_SH_HISTFILESIZE
+                       hp = get_local_var_value("HISTFILESIZE");
+                       G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
+#  endif
+               }
+# endif
        } else {
-               init_sigmasks();
+               install_special_sighandlers();
        }
 #elif ENABLE_HUSH_INTERACTIVE
        /* No job control compiled in, only prompt/line editing */
@@ -7297,10 +8134,10 @@ int hush_main(int argc, char **argv)
        if (G_interactive_fd) {
                close_on_exec_on(G_interactive_fd);
        }
-       init_sigmasks();
+       install_special_sighandlers();
 #else
        /* We have interactiveness code disabled */
-       init_sigmasks();
+       install_special_sighandlers();
 #endif
        /* bash:
         * if interactive but not a login shell, sources ~/.bashrc
@@ -7320,31 +8157,10 @@ int hush_main(int argc, char **argv)
        parse_and_run_file(stdin);
 
  final_return:
-#if ENABLE_FEATURE_CLEAN_UP
-       if (G.cwd != bb_msg_unknown)
-               free((char*)G.cwd);
-       cur_var = G.top_var->next;
-       while (cur_var) {
-               struct variable *tmp = cur_var;
-               if (!cur_var->max_len)
-                       free(cur_var->varstr);
-               cur_var = cur_var->next;
-               free(tmp);
-       }
-#endif
        hush_exit(G.last_exitcode);
 }
 
 
-#if ENABLE_LASH
-int lash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int lash_main(int argc, char **argv)
-{
-       bb_error_msg("lash is deprecated, please use hush instead");
-       return hush_main(argc, argv);
-}
-#endif
-
 #if ENABLE_MSH
 int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int msh_main(int argc, char **argv)
@@ -7455,7 +8271,7 @@ static int FAST_FUNC builtin_exec(char **argv)
                tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
 
        /* TODO: if exec fails, bash does NOT exit! We do.
-        * We'll need to undo sigprocmask (it's inside execvp_or_die)
+        * We'll need to undo trap cleanup (it's inside execvp_or_die)
         * and tcsetpgrp, and this is inherently racy.
         */
        execvp_or_die(argv);
@@ -7473,9 +8289,9 @@ static int FAST_FUNC builtin_exit(char **argv)
         * (if there are _stopped_ jobs, running ones don't count)
         * # exit
         * exit
-        # EEE (then bash exits)
+        * EEE (then bash exits)
         *
-        * we can use G.exiting = -1 as indicator "last cmd was exit"
+        * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
         */
 
        /* note: EXIT trap is run by hush_exit */
@@ -7515,13 +8331,16 @@ static void helper_export_local(char **argv, int exp, int lvl)
 {
        do {
                char *name = *argv;
+               char *name_end = strchrnul(name, '=');
 
                /* So far we do not check that name is valid (TODO?) */
 
-               if (strchr(name, '=') == NULL) {
-                       struct variable *var;
+               if (*name_end == '\0') {
+                       struct variable *var, **vpp;
+
+                       vpp = get_ptr_to_local_var(name, name_end - name);
+                       var = vpp ? *vpp : NULL;
 
-                       var = get_local_var(name);
                        if (exp == -1) { /* unexporting? */
                                /* export -n NAME (without =VALUE) */
                                if (var) {
@@ -7649,6 +8468,8 @@ static int FAST_FUNC builtin_trap(char **argv)
  process_sig_list:
                ret = EXIT_SUCCESS;
                while (*argv) {
+                       sighandler_t handler;
+
                        sig = get_signum(*argv++);
                        if (sig < 0 || sig >= NSIG) {
                                ret = EXIT_FAILURE;
@@ -7667,18 +8488,13 @@ static int FAST_FUNC builtin_trap(char **argv)
                        if (sig == 0)
                                continue;
 
-                       if (new_cmd) {
-                               sigaddset(&G.blocked_set, sig);
-                       } else {
-                               /* There was a trap handler, we are removing it
-                                * (if sig has non-DFL handling,
-                                * we don't need to do anything) */
-                               if (sig < 32 && (G.non_DFL_mask & (1 << sig)))
-                                       continue;
-                               sigdelset(&G.blocked_set, sig);
-                       }
+                       if (new_cmd)
+                               handler = (new_cmd[0] ? record_pending_signo : SIG_IGN);
+                       else
+                               /* We are removing trap handler */
+                               handler = pick_sighandler(sig);
+                       install_sighandler(sig, handler);
                }
-               sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
                return ret;
        }
 
@@ -7783,7 +8599,6 @@ static int FAST_FUNC builtin_fg_bg(char **argv)
        debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_cmds, pi->pgrp);
        for (i = 0; i < pi->num_cmds; i++) {
                debug_printf_jobs("reviving pid %d\n", pi->cmds[i].pid);
-               pi->cmds[i].is_stopped = 0;
        }
        pi->stopped_cmds = 0;
 
@@ -7821,6 +8636,14 @@ static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
 }
 #endif
 
+#if MAX_HISTORY && ENABLE_FEATURE_EDITING
+static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM)
+{
+       show_history(G.line_input_state);
+       return EXIT_SUCCESS;
+}
+#endif
+
 #if ENABLE_HUSH_JOB
 static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
 {
@@ -7879,6 +8702,27 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
        return EXIT_SUCCESS;
 }
 
+/* Interruptibility of read builtin in bash
+ * (tested on bash-4.2.8 by sending signals (not by ^C)):
+ *
+ * Empty trap makes read ignore corresponding signal, for any signal.
+ *
+ * SIGINT:
+ * - terminates non-interactive shell;
+ * - interrupts read in interactive shell;
+ * if it has non-empty trap:
+ * - executes trap and returns to command prompt in interactive shell;
+ * - executes trap and returns to read in non-interactive shell;
+ * SIGTERM:
+ * - is ignored (does not interrupt) read in interactive shell;
+ * - terminates non-interactive shell;
+ * if it has non-empty trap:
+ * - executes trap and returns to read;
+ * SIGHUP:
+ * - terminates shell (regardless of interactivity);
+ * if it has non-empty trap:
+ * - executes trap and returns to read;
+ */
 static int FAST_FUNC builtin_read(char **argv)
 {
        const char *r;
@@ -7886,6 +8730,7 @@ static int FAST_FUNC builtin_read(char **argv)
        char *opt_p = NULL;
        char *opt_t = NULL;
        char *opt_u = NULL;
+       const char *ifs;
        int read_flags;
 
        /* "!": do not abort on errors.
@@ -7895,10 +8740,12 @@ static int FAST_FUNC builtin_read(char **argv)
        if (read_flags == (uint32_t)-1)
                return EXIT_FAILURE;
        argv += optind;
+       ifs = get_local_var_value("IFS"); /* can be NULL */
 
+ again:
        r = shell_builtin_read(set_local_var_from_halves,
                argv,
-               get_local_var_value("IFS"), /* can be NULL */
+               ifs,
                read_flags,
                opt_n,
                opt_p,
@@ -7906,6 +8753,12 @@ static int FAST_FUNC builtin_read(char **argv)
                opt_u
        );
 
+       if ((uintptr_t)r == 1 && errno == EINTR) {
+               unsigned sig = check_and_run_traps();
+               if (sig && sig != SIGINT)
+                       goto again;
+       }
+
        if ((uintptr_t)r > 1) {
                bb_error_msg("%s", r);
                r = (char*)(uintptr_t)1;
@@ -7949,15 +8802,18 @@ static int FAST_FUNC builtin_set(char **argv)
        }
 
        do {
-               if (!strcmp(arg, "--")) {
+               if (strcmp(arg, "--") == 0) {
                        ++argv;
                        goto set_argv;
                }
                if (arg[0] != '+' && arg[0] != '-')
                        break;
-               for (n = 1; arg[n]; ++n)
-                       if (set_mode(arg[0], arg[n]))
+               for (n = 1; arg[n]; ++n) {
+                       if (set_mode((arg[0] == '-'), arg[n], argv[1]))
                                goto error;
+                       if (arg[n] == 'o' && argv[1])
+                               argv++;
+               }
        } while ((arg = *++argv) != NULL);
        /* Now argv[0] is 1st argument */
 
@@ -8040,6 +8896,9 @@ static int FAST_FUNC builtin_source(char **argv)
        free(arg_path);
        if (!input) {
                /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
+               /* POSIX: non-interactive shell should abort here,
+                * not merely fail. So far no one complained :)
+                */
                return EXIT_FAILURE;
        }
        close_on_exec_on(fileno(input));
@@ -8049,12 +8908,14 @@ static int FAST_FUNC builtin_source(char **argv)
        /* "we are inside sourced file, ok to use return" */
        G.flag_return_in_progress = -1;
 #endif
-       save_and_replace_G_args(&sv, argv);
+       if (argv[1])
+               save_and_replace_G_args(&sv, argv);
 
        parse_and_run_file(input);
        fclose(input);
 
-       restore_G_args(&sv, argv);
+       if (argv[1])
+               restore_G_args(&sv, argv);
 #if ENABLE_HUSH_FUNCTIONS
        G.flag_return_in_progress = sv_flg;
 #endif
@@ -8135,7 +8996,7 @@ static int FAST_FUNC builtin_unset(char **argv)
 static int FAST_FUNC builtin_wait(char **argv)
 {
        int ret = EXIT_SUCCESS;
-       int status, sig;
+       int status;
 
        argv = skip_dash_dash(argv);
        if (argv[0] == NULL) {
@@ -8155,25 +9016,53 @@ static int FAST_FUNC builtin_wait(char **argv)
                 * ^C <-- after ~4 sec from keyboard
                 * $
                 */
-               sigaddset(&G.blocked_set, SIGCHLD);
-               sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
                while (1) {
-                       checkjobs(NULL);
-                       if (errno == ECHILD)
+                       int sig;
+                       sigset_t oldset, allsigs;
+
+                       /* waitpid is not interruptible by SA_RESTARTed
+                        * signals which we use. Thus, this ugly dance:
+                        */
+
+                       /* Make sure possible SIGCHLD is stored in kernel's
+                        * pending signal mask before we call waitpid.
+                        * Or else we may race with SIGCHLD, lose it,
+                        * and get stuck in sigwaitinfo...
+                        */
+                       sigfillset(&allsigs);
+                       sigprocmask(SIG_SETMASK, &allsigs, &oldset);
+
+                       if (!sigisemptyset(&G.pending_set)) {
+                               /* Crap! we raced with some signal! */
+                       //      sig = 0;
+                               goto restore;
+                       }
+
+                       checkjobs(NULL); /* waitpid(WNOHANG) inside */
+                       if (errno == ECHILD) {
+                               sigprocmask(SIG_SETMASK, &oldset, NULL);
+                               break;
+                       }
+
+                       /* Wait for SIGCHLD or any other signal */
+                       //sig = sigwaitinfo(&allsigs, NULL);
+                       /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */
+                       /* Note: sigsuspend invokes signal handler */
+                       sigsuspend(&oldset);
+ restore:
+                       sigprocmask(SIG_SETMASK, &oldset, NULL);
+
+                       /* So, did we get a signal? */
+                       //if (sig > 0)
+                       //      raise(sig); /* run handler */
+                       sig = check_and_run_traps();
+                       if (sig /*&& sig != SIGCHLD - always true */) {
+                               /* see note 2 */
+                               ret = 128 + sig;
                                break;
-                       /* Wait for SIGCHLD or any other signal of interest */
-                       /* sigtimedwait with infinite timeout: */
-                       sig = sigwaitinfo(&G.blocked_set, NULL);
-                       if (sig > 0) {
-                               sig = check_and_run_traps(sig);
-                               if (sig && sig != SIGCHLD) { /* see note 2 */
-                                       ret = 128 + sig;
-                                       break;
-                               }
                        }
+                       /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
                }
-               sigdelset(&G.blocked_set, SIGCHLD);
-               sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
                return ret;
        }
 
index 718c26a..8a201fb 100644 (file)
@@ -61,7 +61,7 @@ check that the unevaluated part of the ternary operator does not do evaluation o
 20 20
 30 30
 check precedence of assignment vs. conditional operator
-hush: error in arithmetic
+hush: arithmetic syntax error
 check precedence of assignment vs. conditional operator
 associativity of assignment-operator operator
 6 6
@@ -70,22 +70,22 @@ octal, hex
 263 263
 255 255
 40 40
-hush: error in arithmetic
-hush: divide by 0
+hush: arithmetic syntax error
+hush: divide by zero
 hush: can't execute 'let': No such file or directory
-hush: error in arithmetic
+hush: arithmetic syntax error
 hush: can't execute 'let': No such file or directory
 abc
 def
 ghi
-hush: error in arithmetic
+hush: arithmetic syntax error
 16 16
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
+hush: arithmetic syntax error
+hush: malformed ?: operator
+hush: arithmetic syntax error
 9 9
-hush: error in arithmetic
-hush: error in arithmetic
+hush: arithmetic syntax error
+hush: arithmetic syntax error
 9 9
 9 9
 9 9
@@ -106,18 +106,18 @@ hush: error in arithmetic
 3 3
 4 4
 4 4
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
 4 4
 7 7
 -7 -7
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
 6 6
 3 3
 7 7
@@ -128,19 +128,19 @@ hush: error in arithmetic
 2 2
 -2 -2
 1 1
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
-hush: error in arithmetic
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
 5 5
 1 1
 4 4
 0 0
-hush: error in arithmetic
-hush: error in arithmetic
+hush: arithmetic syntax error
+hush: arithmetic syntax error
 8 12
-hush: error in arithmetic
+hush: arithmetic syntax error
 42
 42
 42
old mode 100755 (executable)
new mode 100644 (file)
similarity index 80%
rename from shell/hush_test/hush-bugs/export_exp.tests
rename to shell/hush_test/hush-bugs/export_exp.tests.disabled
index 91f57aa..0913fd3
@@ -1,3 +1,6 @@
+# This test shows a very special handling of export and local
+# builtins by bash.
+
 v="a=aa0 b=bb0"
 # only 1st arg should be expanded in multiple words
 export $v c=$v
diff --git a/shell/hush_test/hush-glob/bash_brace1.right b/shell/hush_test/hush-glob/bash_brace1.right
new file mode 100644 (file)
index 0000000..63365c9
--- /dev/null
@@ -0,0 +1,4 @@
+bash_brace1.tests
+*{b,b}race1.t*
+bash_brace1.tests bash_brace1.tests
+Done: 0
diff --git a/shell/hush_test/hush-glob/bash_brace1.tests b/shell/hush_test/hush-glob/bash_brace1.tests
new file mode 100755 (executable)
index 0000000..eb2f0e9
--- /dev/null
@@ -0,0 +1,10 @@
+# unquoted $v should be globbed:
+v='*brace1.t*'; echo $v
+
+# ...but not brace expanded:
+v='*{b,b}race1.t*'; echo $v
+
+# whereas direct brces are expanded:
+echo *{b,b}race1.t*
+
+echo Done: $?
diff --git a/shell/hush_test/hush-glob/glob2.right b/shell/hush_test/hush-glob/glob2.right
new file mode 100644 (file)
index 0000000..7a70c22
--- /dev/null
@@ -0,0 +1,18 @@
+Expected Actual
+Z\*    : Z\*
+Z*     : Z*
+Z\f    : Z\f
+Z\*    : Z\*
+
+Z\z    : Z\z
+Zz     : Zz
+Z\z    : Z\z
+Z\z    : Z\z
+
+Z\     : Z\
+Z\     : Z\
+
+Z\f Zf : Z\f Zf
+Z\f Zf : Z\f Zf
+
+Done: 0
diff --git a/shell/hush_test/hush-glob/glob2.tests b/shell/hush_test/hush-glob/glob2.tests
new file mode 100755 (executable)
index 0000000..4dbc925
--- /dev/null
@@ -0,0 +1,27 @@
+# This test demonstrates that in unquoted $v, backslashes expand by this rule:
+# \z -> \\\z; \<eol> -> \\<eol> (for any z, special or not),
+# and subsequently globbing converts \\ to \ and treats \z as literal z
+# even if it is a special char.
+
+>'Zf'
+>'Z\f'
+       echo 'Expected' 'Actual'
+v='\*'; echo 'Z\*    :' Z$v
+        echo 'Z*     :' Z\*
+        echo 'Z\f    :' Z\\*
+        echo 'Z\*    :' Z\\\*  # NB! only this matches Z$v output
+echo
+v='\z'; echo 'Z\z    :' Z$v
+        echo 'Zz     :' Z\z
+        echo 'Z\z    :' Z\\z
+        echo 'Z\z    :' Z\\\z
+echo
+v='\';  echo 'Z\     :' Z$v
+        echo 'Z\     :' Z\\
+echo
+v='*';  echo 'Z\f Zf :' Z$v
+        echo 'Z\f Zf :' Z*
+echo
+
+rm 'Z\f' 'Zf'
+echo Done: $?
diff --git a/shell/hush_test/hush-misc/assignment3.right b/shell/hush_test/hush-misc/assignment3.right
new file mode 100644 (file)
index 0000000..0f02d7c
--- /dev/null
@@ -0,0 +1,2 @@
+Done:0
+abc=123
diff --git a/shell/hush_test/hush-misc/assignment3.tests b/shell/hush_test/hush-misc/assignment3.tests
new file mode 100755 (executable)
index 0000000..790129b
--- /dev/null
@@ -0,0 +1,5 @@
+# This must be interpreted as assignments
+a=1 b\
+=2 c=3
+echo Done:$?
+echo abc=$a$b$c
diff --git a/shell/hush_test/hush-misc/assignment4.right b/shell/hush_test/hush-misc/assignment4.right
new file mode 100644 (file)
index 0000000..31c896f
--- /dev/null
@@ -0,0 +1 @@
+Done:0
diff --git a/shell/hush_test/hush-misc/assignment4.tests b/shell/hush_test/hush-misc/assignment4.tests
new file mode 100755 (executable)
index 0000000..6f46d0a
--- /dev/null
@@ -0,0 +1,3 @@
+# There was a bug where we misinterpreted assignments after 'do':
+for i in 1; do eval b=; done
+echo Done:$?
index 912f149..3a6b060 100755 (executable)
@@ -1,3 +1,2 @@
 while true; do echo A; break; echo B; done
 echo OK:$?
-
diff --git a/shell/hush_test/hush-misc/echo_write_error.right b/shell/hush_test/hush-misc/echo_write_error.right
new file mode 100644 (file)
index 0000000..ddcad43
--- /dev/null
@@ -0,0 +1,2 @@
+hush: write error: Broken pipe
+Ok: 1
diff --git a/shell/hush_test/hush-misc/echo_write_error.tests b/shell/hush_test/hush-misc/echo_write_error.tests
new file mode 100755 (executable)
index 0000000..0a40c9f
--- /dev/null
@@ -0,0 +1,7 @@
+trap "" PIPE
+
+{
+sleep 1
+echo Cant write this - get EPIPE
+echo Ok: $? >&2
+} | { true; }
diff --git a/shell/hush_test/hush-misc/heredoc_backslash1.right b/shell/hush_test/hush-misc/heredoc_backslash1.right
new file mode 100644 (file)
index 0000000..6a61148
--- /dev/null
@@ -0,0 +1,43 @@
+Quoted heredoc:
+a\
+       b
+a\\
+       b
+ 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+       -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+ 123456 `echo  v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
+ 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
+c\
+
+Unquoted heredoc:
+a      b
+a\
+       b
+ 123456 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
+       -qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
+ 123456 v-$a-\t-\-\"-\x-`-\--\z-\*-\?-
+ 123456 v-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-
+cEOF2
+
+Quoted -heredoc:
+a\
+b
+a\\
+b
+ 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+ 123456 `echo  v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
+ 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
+c\
+
+Unquoted -heredoc:
+a      b
+a\
+b
+ 123456 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
+-qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
+ 123456 v-$a-\t-\-\"-\x-`-\--\z-\*-\?-
+ 123456 v-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-
+cEOF4
+
+Done: 0
diff --git a/shell/hush_test/hush-misc/heredoc_backslash1.tests b/shell/hush_test/hush-misc/heredoc_backslash1.tests
new file mode 100755 (executable)
index 0000000..501af54
--- /dev/null
@@ -0,0 +1,70 @@
+# Test for correct handling of backslashes.
+# Note that some lines in each heredoc start with a tab.
+
+a=qwerty
+
+echo Quoted heredoc:
+cat <<"EOF1"
+a\
+       b
+a\\
+       b
+ 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+       -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+ 123456 `echo  v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
+ 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
+c\
+EOF1
+echo
+
+echo Unquoted heredoc:
+cat <<EOF2
+a\
+       b
+a\\
+       b
+ 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+       -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+ 123456 `echo  v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'`
+ 123456 $(echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-')
+c\
+EOF2
+EOF2
+echo
+
+echo Quoted -heredoc:
+cat <<-"EOF3"
+a\
+       b
+a\\
+       b
+ 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+       -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+ 123456 `echo  v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
+ 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
+c\
+       EOF3
+# In -heredoc case the marker is detected even if it is indented.
+echo
+
+echo Unquoted -heredoc:
+cat <<-EOF4
+a\
+       b
+a\\
+       b
+ 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+       -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+ 123456 `echo  v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'`
+ 123456 $(echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-')
+c\
+EOF4
+       EOF4
+# The marker is not detected if preceding line ends in backslash.
+# TODO: marker should be detected even if it is split by line continuation:
+# EOF\
+# 4
+# but currently hush doesn't do it. (Tab before "4" is not allowed, though.)
+echo
+
+echo "Done: $?"
diff --git a/shell/hush_test/hush-misc/pipefail.right b/shell/hush_test/hush-misc/pipefail.right
new file mode 100644 (file)
index 0000000..5845d89
--- /dev/null
@@ -0,0 +1,40 @@
+Default:
+true | true:
+0
+1
+true | false:
+1
+0
+false | true:
+0
+1
+exit 2 | exit 3 | exit 4:
+4
+0
+Pipefail on:
+true | true:
+0
+1
+true | false:
+1
+0
+false | true:
+1
+0
+exit 2 | exit 3 | exit 4:
+4
+0
+Pipefail off:
+true | true:
+0
+1
+true | false:
+1
+0
+false | true:
+0
+1
+exit 2 | exit 3 | exit 4:
+4
+0
+Done
diff --git a/shell/hush_test/hush-misc/pipefail.tests b/shell/hush_test/hush-misc/pipefail.tests
new file mode 100755 (executable)
index 0000000..9df8418
--- /dev/null
@@ -0,0 +1,45 @@
+echo Default:
+echo "true | true:"
+  true | true; echo $?
+! true | true; echo $?
+echo "true | false:"
+  true | false; echo $?
+! true | false; echo $?
+echo "false | true:"
+  false | true; echo $?
+! false | true; echo $?
+echo "exit 2 | exit 3 | exit 4:"
+  exit 2 | exit 3 | exit 4; echo $?
+! exit 2 | exit 3 | exit 4; echo $?
+
+echo Pipefail on:
+set -o pipefail
+echo "true | true:"
+  true | true; echo $?
+! true | true; echo $?
+echo "true | false:"
+  true | false; echo $?
+! true | false; echo $?
+echo "false | true:"
+  false | true; echo $?
+! false | true; echo $?
+echo "exit 2 | exit 3 | exit 4:"
+  exit 2 | exit 3 | exit 4; echo $?
+! exit 2 | exit 3 | exit 4; echo $?
+
+echo Pipefail off:
+set +o pipefail
+echo "true | true:"
+  true | true; echo $?
+! true | true; echo $?
+echo "true | false:"
+  true | false; echo $?
+! true | false; echo $?
+echo "false | true:"
+  false | true; echo $?
+! false | true; echo $?
+echo "exit 2 | exit 3 | exit 4:"
+  exit 2 | exit 3 | exit 4; echo $?
+! exit 2 | exit 3 | exit 4; echo $?
+
+echo Done
diff --git a/shell/hush_test/hush-misc/return1.right b/shell/hush_test/hush-misc/return1.right
new file mode 100644 (file)
index 0000000..7b24a35
--- /dev/null
@@ -0,0 +1 @@
+Ok:0
diff --git a/shell/hush_test/hush-misc/return1.tests b/shell/hush_test/hush-misc/return1.tests
new file mode 100755 (executable)
index 0000000..eeb92ef
--- /dev/null
@@ -0,0 +1,4 @@
+echo "true && return; echo Should not be printed" >return_sourced
+. ./return_sourced
+rm return_sourced
+echo Ok:$?
diff --git a/shell/hush_test/hush-misc/sigint1.right b/shell/hush_test/hush-misc/sigint1.right
new file mode 100644 (file)
index 0000000..a9094b0
--- /dev/null
@@ -0,0 +1 @@
+Sending SIGINT to main shell PID
diff --git a/shell/hush_test/hush-misc/sigint1.tests b/shell/hush_test/hush-misc/sigint1.tests
new file mode 100755 (executable)
index 0000000..3d483d3
--- /dev/null
@@ -0,0 +1,41 @@
+# What should happen if non-interactive shell gets SIGINT?
+
+(sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) &
+
+# We create a child which exits with 0 even on SIGINT
+# (The complex command is necessary only if SIGINT is generated by ^C,
+# in this testcase even bare "sleep 2" would do because
+# in the testcase we don't send SIGINT *to the child*...)
+$THIS_SH -c 'trap "exit 0" SIGINT; sleep 2'
+
+# In one second, we (main shell) get SIGINT here.
+# The question is whether we should, or should not, exit.
+
+# bash will not stop here. It will execute next command(s).
+
+# The rationale for this is described here:
+# http://www.cons.org/cracauer/sigint.html
+#
+# Basically, bash will not exit on SIGINT immediately if it waits
+# for a child. It will wait for the child to exit.
+# If child exits NOT by dying on SIGINT, then bash will not exit.
+#
+# The idea is that the following script:
+# | emacs file.txt
+# | more cmds
+# User may use ^C to interrupt editor's ops like search. But then
+# emacs exits normally. User expects that script doesn't stop.
+#
+# This is a nice idea, but detecting "did process really exit
+# with SIGINT?" is racy. Consider:
+# | bash -c 'while true; do /bin/true; done'
+# When ^C is pressed while bash waits for /bin/true to exit,
+# it may happen that /bin/true exits with exitcode 0 before
+# ^C is delivered to it as SIGINT. bash will see SIGINT, then
+# it will see that child exited with 0, and bash will NOT EXIT.
+
+# Therefore we do not implement bash behavior.
+# I'd say that emacs need to put itself into a separate pgrp
+# to isolate shell from getting stray SIGINTs from ^C.
+
+echo Next command after SIGINT was executed
diff --git a/shell/hush_test/hush-misc/source1.right b/shell/hush_test/hush-misc/source1.right
new file mode 100644 (file)
index 0000000..d425603
--- /dev/null
@@ -0,0 +1,5 @@
+hush: syntax error: unterminated ${name}
+line2
+Ok1:0
+hush: syntax error: unterminated '
+Ok2:1
diff --git a/shell/hush_test/hush-misc/source1.tests b/shell/hush_test/hush-misc/source1.tests
new file mode 100755 (executable)
index 0000000..c138883
--- /dev/null
@@ -0,0 +1,10 @@
+echo 'echo ${^}
+echo line2' >sourced1
+. ./sourced1
+echo Ok1:$?
+
+echo "echo '" >sourced1
+. ./sourced1
+echo Ok2:$?
+
+rm sourced1
diff --git a/shell/hush_test/hush-misc/source2.right b/shell/hush_test/hush-misc/source2.right
new file mode 100644 (file)
index 0000000..0587bad
--- /dev/null
@@ -0,0 +1,4 @@
+0:arg0 1:arg1 2:arg2
+Ok1:0
+0:arg0 1:q 2:w
+Ok2:0
diff --git a/shell/hush_test/hush-misc/source2.tests b/shell/hush_test/hush-misc/source2.tests
new file mode 100755 (executable)
index 0000000..40b6b83
--- /dev/null
@@ -0,0 +1,8 @@
+echo 'echo "0:$0 1:$1 2:$2"' >sourced1
+set -- 1 2 3
+"$THIS_SH" -c '. ./sourced1' arg0 arg1 arg2
+echo Ok1:$?
+"$THIS_SH" -c '. ./sourced1 q w e' arg0 arg1 arg2
+echo Ok2:$?
+
+rm sourced1
diff --git a/shell/hush_test/hush-misc/while3.right b/shell/hush_test/hush-misc/while3.right
new file mode 100644 (file)
index 0000000..7c4d7be
--- /dev/null
@@ -0,0 +1 @@
+OK:0
diff --git a/shell/hush_test/hush-misc/while3.tests b/shell/hush_test/hush-misc/while3.tests
new file mode 100755 (executable)
index 0000000..9132b5f
--- /dev/null
@@ -0,0 +1,4 @@
+while false; do
+    # bash will require at least ":" here...
+done
+echo OK:$?
diff --git a/shell/hush_test/hush-misc/while4.right b/shell/hush_test/hush-misc/while4.right
new file mode 100644 (file)
index 0000000..7b24a35
--- /dev/null
@@ -0,0 +1 @@
+Ok:0
diff --git a/shell/hush_test/hush-misc/while4.tests b/shell/hush_test/hush-misc/while4.tests
new file mode 100755 (executable)
index 0000000..ba80e60
--- /dev/null
@@ -0,0 +1,6 @@
+false
+while false && echo Not reached; do
+    echo BUG
+    break
+done
+echo Ok:$?
diff --git a/shell/hush_test/hush-parsing/comment1.right b/shell/hush_test/hush-parsing/comment1.right
new file mode 100644 (file)
index 0000000..a102b1d
--- /dev/null
@@ -0,0 +1,2 @@
+Nothing:
+String: #should-be-echoed
diff --git a/shell/hush_test/hush-parsing/comment1.tests b/shell/hush_test/hush-parsing/comment1.tests
new file mode 100755 (executable)
index 0000000..d268860
--- /dev/null
@@ -0,0 +1,2 @@
+echo Nothing: #should-not-be-echoed
+echo String: ""#should-be-echoed
diff --git a/shell/hush_test/hush-parsing/eol1.right b/shell/hush_test/hush-parsing/eol1.right
new file mode 100644 (file)
index 0000000..31c896f
--- /dev/null
@@ -0,0 +1 @@
+Done:0
diff --git a/shell/hush_test/hush-parsing/eol1.tests b/shell/hush_test/hush-parsing/eol1.tests
new file mode 100755 (executable)
index 0000000..f1b55e8
--- /dev/null
@@ -0,0 +1,18 @@
+# bug was that we treated <newline> as ';' in this line:
+true || echo foo |
+echo BAD1 | cat
+
+# variation on the same theme
+true || echo foo |
+# comment
+echo BAD2 | cat
+
+# variation on the same theme
+true || echo foo |
+
+echo BAD3 | cat
+
+# this should error out, but currently works in hush:
+#true || echo foo |;
+
+echo Done:$?
index e1562ed..1bff408 100644 (file)
@@ -1,4 +1,7 @@
 Should be printed
+Would not be printed by bash
+Would not be printed by bash
+Would not be printed by bash
 Should be printed
 Empty:
 Empty:
index f305c4c..7c5ff45 100755 (executable)
@@ -8,9 +8,9 @@ for a in "$@"; do echo Should not be printed; done
 # Yes, believe it or not, bash is mesmerized by "$@" and stops
 # treating "" as "this word cannot be expanded to nothing,
 # but must be at least null string". Now it can be expanded to nothing.
-for a in "$@"""; do echo Should not be printed; done
-for a in """$@"; do echo Should not be printed; done
-for a in """$@"''"$@"''; do echo Should not be printed; done
+for a in "$@"""; do echo Would not be printed by bash; done
+for a in """$@"; do echo Would not be printed by bash; done
+for a in """$@"''"$@"''; do echo Would not be printed by bash; done
 for a in ""; do echo Should be printed; done
 
 # Bug 207: "$@" expands to nothing, and we erroneously glob "%s\n" twice:
index dc84e92..00f267a 100644 (file)
@@ -2,5 +2,5 @@
 $TEST
 Q
 a\bc
-a"c
+11-$a-\t-\-\"-`-\--\z-\*-\?-22 33-$a-\t-\-"-`-\--\z-\*-\?-44
 done:0
index 469c43c..3aeb241 100755 (executable)
@@ -7,6 +7,8 @@ echo `echo '\'TEST\`echo ZZ\`BEST`
 echo `echo \\$TEST`
 echo `echo \$TEST`
 echo a`echo \\\\b`c
-# \" etc are NOT special (passed verbatim WITH \)!
-echo a`echo \"`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:$?
index b4932fb..3d00725 100644 (file)
@@ -1,2 +1,12 @@
 cow
 moo
+Traps1:
+trap -- 'exitfunc' EXIT
+Traps2:
+trap -- 'echo Should not run' EXIT
+Check1: 42
+Traps1:
+trap -- 'exitfunc' EXIT
+Traps2:
+trap -- 'echo Should not run' EXIT
+Check2: 42
index 092543c..2061105 100755 (executable)
@@ -1,3 +1,34 @@
 "$THIS_SH" -c 'trap "echo cow" 0'
 "$THIS_SH" -c 'trap "echo moo" EXIT'
 "$THIS_SH" -c 'trap "echo no" 0; trap 0'
+
+(
+exitfunc() {
+        echo "Traps1:"
+        trap
+        # EXIT trap is disabled after it is triggered,
+        # it can not be "re-armed" like this:
+        trap "echo Should not run" EXIT
+        echo "Traps2:"
+        trap
+}
+trap 'exitfunc' EXIT
+exit 42
+)
+echo Check1: $?
+
+(
+exitfunc() {
+        echo "Traps1:"
+        trap
+        # EXIT trap is disabled after it is triggered,
+        # it can not be "re-armed" like this:
+        trap "echo Should not run" EXIT
+        echo "Traps2:"
+        trap
+        exit 42
+}
+trap 'exitfunc' EXIT
+exit 66
+)
+echo Check2: $?
diff --git a/shell/hush_test/hush-trap/signal7.right b/shell/hush_test/hush-trap/signal7.right
new file mode 100644 (file)
index 0000000..ba7453e
--- /dev/null
@@ -0,0 +1 @@
+Bug detected: 0
diff --git a/shell/hush_test/hush-trap/signal7.tests b/shell/hush_test/hush-trap/signal7.tests
new file mode 100755 (executable)
index 0000000..c2b1381
--- /dev/null
@@ -0,0 +1,18 @@
+bug() {
+       trap : exit
+       # Bug was causing sh to be run in subshell,
+       # as if this line is replaced with (sh -c ...; exit $?) &
+       # here:
+       sh -c 'echo REAL_CHILD=$$' &
+       echo PARENTS_IDEA_OF_CHILD=$!
+       wait  # make sure bkgd shell completes
+}
+
+bug | {
+while read varval; do
+       eval $varval
+done
+test x"$REAL_CHILD" != x"" \
+&& test x"$REAL_CHILD" = x"$PARENTS_IDEA_OF_CHILD"
+echo "Bug detected: $?"
+}
diff --git a/shell/hush_test/hush-trap/signal_read1.right b/shell/hush_test/hush-trap/signal_read1.right
new file mode 100644 (file)
index 0000000..2870a8e
--- /dev/null
@@ -0,0 +1 @@
+Got HUP:0
diff --git a/shell/hush_test/hush-trap/signal_read1.tests b/shell/hush_test/hush-trap/signal_read1.tests
new file mode 100755 (executable)
index 0000000..1105479
--- /dev/null
@@ -0,0 +1,5 @@
+(sleep 1; kill -HUP $$) &
+trap 'echo "Got HUP:$?"; exit' HUP
+while true; do
+       read ignored
+done
diff --git a/shell/hush_test/hush-trap/signal_read2.right b/shell/hush_test/hush-trap/signal_read2.right
new file mode 100644 (file)
index 0000000..71a6bc1
--- /dev/null
@@ -0,0 +1,2 @@
+HUP
+Done:129
diff --git a/shell/hush_test/hush-trap/signal_read2.tests b/shell/hush_test/hush-trap/signal_read2.tests
new file mode 100755 (executable)
index 0000000..eab5b9b
--- /dev/null
@@ -0,0 +1,7 @@
+$THIS_SH -c '
+(sleep 1; kill -HUP $$) &
+while true; do
+       read ignored
+done
+'
+echo "Done:$?"
index 045294b..d877f2b 100755 (executable)
@@ -11,10 +11,9 @@ trap 'bad: caught WINCH' WINCH
 # With TERM we'll check whether it is reset
 trap 'bad: caught TERM'  TERM
 
-# using bash, because we don't have $PPID (yet)
-(trap; bash -c 'kill -HUP   $PPID'; echo Ok)
-(trap; bash -c 'kill -QUIT  $PPID'; echo Ok)
-(trap; bash -c 'kill -SYS   $PPID'; echo Ok)
-(trap; bash -c 'kill -WINCH $PPID'; echo Ok)
-(trap; bash -c 'kill -TERM  $PPID'; echo Bad: TERM is not reset)
+(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
diff --git a/shell/hush_test/hush-vars/var_bash1.right b/shell/hush_test/hush-vars/var_bash1.right
new file mode 100644 (file)
index 0000000..c0a0769
--- /dev/null
@@ -0,0 +1,14 @@
+
+
+f
+bcdef
+abcdef
+abcdef
+bcde
+abcd
+abcd
+abcdef
+bcdef
+abcdef
+abcdef
+abcdef
diff --git a/shell/hush_test/hush-vars/var_bash1.tests b/shell/hush_test/hush-vars/var_bash1.tests
new file mode 100755 (executable)
index 0000000..24d3c9a
--- /dev/null
@@ -0,0 +1,18 @@
+var=abcdef
+
+echo ${var:7}
+echo ${var:6}
+echo ${var:5}
+echo ${var:1}
+echo ${var:0}
+echo ${var:-1}
+
+echo ${var:1:4}
+echo ${var:0:4}
+echo ${var::4}
+echo ${var:-1:4}
+
+echo ${var:1:7}
+echo ${var:0:7}
+echo ${var::7}
+echo ${var:-1:7}
diff --git a/shell/hush_test/hush-vars/var_bash2.right b/shell/hush_test/hush-vars/var_bash2.right
new file mode 100644 (file)
index 0000000..acba5c6
--- /dev/null
@@ -0,0 +1,10 @@
+abc123xcba123
+abx123dcba123
+abx123dxba123
+abcx23dcba123
+abcxxxdcbaxxx
+abx
+xba123
+abx23
+abc23dcba123
+abcdcba
diff --git a/shell/hush_test/hush-vars/var_bash2.tests b/shell/hush_test/hush-vars/var_bash2.tests
new file mode 100755 (executable)
index 0000000..29c526c
--- /dev/null
@@ -0,0 +1,24 @@
+var=abc123dcba123
+
+echo ${var/d/x}
+echo ${var/c/x}
+echo ${var//c/x}
+echo ${var/[123]/x}
+echo ${var//[123]/x}
+echo ${var/c*/x}
+echo ${var/*c/x}
+
+# must match longest match: result is "abx23"
+echo ${var/c*1/x}
+
+# empty replacement - 2nd slash can be omitted
+echo ${var/[123]}
+echo ${var//[123]}
+
+### ash doesn't support
+### # match only at the beginning:
+### echo ${var/#a/x}
+### echo ${var/#b/x} # should not match
+### echo ${var//#b/x} # should not match
+### # match only at the end:
+### echo ${var/%3/x}
diff --git a/shell/hush_test/hush-vars/var_bash3.right b/shell/hush_test/hush-vars/var_bash3.right
new file mode 100644 (file)
index 0000000..a97c850
--- /dev/null
@@ -0,0 +1,20 @@
+1 a041#c
+2 a041#c
+3 a\041#c
+4 a\041#c
+5 a\041#c
+6 a\041#c
+7 a\041#c
+8 a\041#c
+9 a\041#c
+10 a\c
+11 a\c
+12 a\c
+13 a\\c
+14 a\\c
+15 a\\c
+16 a\tc
+17 a\tc
+18 a\tc
+19 atc
+20 a\tc
diff --git a/shell/hush_test/hush-vars/var_bash3.tests b/shell/hush_test/hush-vars/var_bash3.tests
new file mode 100755 (executable)
index 0000000..146dbb6
--- /dev/null
@@ -0,0 +1,41 @@
+a='abc'
+r=${a//b/\041#}
+echo 1 $r
+echo 2 ${a//b/\041#}
+echo 3 "${a//b/\041#}"
+
+a='abc'
+r=${a//b/\\041#}
+echo 4 $r
+echo 5 ${a//b/\\041#}
+echo 6 "${a//b/\\041#}"
+
+a='abc'
+b='\041#'
+r=${a//b/$b}
+echo 7 $r
+echo 8 ${a//b/$b}
+echo 9 "${a//b/$b}"
+
+a='abc'
+b='\'
+r="${a//b/$b}"
+echo 10 $r
+echo 11 ${a//b/$b}
+echo 12 "${a//b/$b}"
+
+a='abc'
+b='\\'
+r="${a//b/$b}"
+echo 13 $r
+echo 14 ${a//b/$b}
+echo 15 "${a//b/$b}"
+
+a='abc'
+b='\t'
+r="${a//b/$b}"
+echo 16 $r
+echo 17 ${a//b/$b}
+echo 18 "${a//b/$b}"
+echo 19 ${a//b/\t}
+echo 20 "${a//b/\t}"
diff --git a/shell/hush_test/hush-vars/var_bash4.right b/shell/hush_test/hush-vars/var_bash4.right
new file mode 100644 (file)
index 0000000..0ef1bf6
--- /dev/null
@@ -0,0 +1,40 @@
+Source:        a*b\*c
+Replace str:   _\\_\z_
+Pattern:       single backslash and star: "replace literal star"
+Unquoted:      a_\_z_b\*c
+Unquoted =:    a_\_z_b\*c
+Quoted:        a_\_\z_b\*c
+Quoted =:      a_\_\z_b\*c
+Pattern:       double backslash and star: "replace backslash and everything after it"
+Unquoted:      a*b_\_z_
+Unquoted =:    a*b_\_z_
+Quoted:        a*b_\_\z_
+Quoted =:      a*b_\_\z_
+
+Source:        a\bc
+Replace str:   _\\_\z_
+Pattern:       single backslash and b: "replace b"
+Unquoted:      a\_\_z_c
+Unquoted =:    a\_\_z_c
+Quoted:        a\_\_\z_c
+Quoted =:      a\_\_\z_c
+Pattern:       double backslash and b: "replace backslash and b"
+Unquoted:      a_\_z_c
+Unquoted =:    a_\_z_c
+Quoted:        a_\_\z_c
+Quoted =:      a_\_\z_c
+
+Source:        a\bc
+Replace str:   _\\_\z_ (as variable $s)
+Pattern:       single backslash and b: "replace b"
+Unquoted:      a\_\\_\z_c
+Unquoted =:    a\_\\_\z_c
+Quoted:        a\_\\_\z_c
+Quoted =:      a\_\\_\z_c
+Pattern:       double backslash and b: "replace backslash and b"
+Unquoted:      a_\\_\z_c
+Unquoted =:    a_\\_\z_c
+Quoted:        a_\\_\z_c
+Quoted =:      a_\\_\z_c
+
+Done: 0
diff --git a/shell/hush_test/hush-vars/var_bash4.tests b/shell/hush_test/hush-vars/var_bash4.tests
new file mode 100755 (executable)
index 0000000..32aa2b3
--- /dev/null
@@ -0,0 +1,81 @@
+# This testcase demonstrates that backslashes are treated differently
+# in 1st and 2nd parts of ${var/search/repl}:
+# if quoted ("${var/search/repl}"), and repl contains \a (a non-special char),
+# the backslash in repl stays; if unquoted, backslash is removed.
+# But search part does not act like that: \a is always converted to just a,
+# even in quotes.
+#
+# bash4 (and probably bash3 too): "Quoted:" results are different from
+# unquoted expansions - they have a backslash before z.
+#
+# The difference only exists if repl is a literal. If it is a variable:
+# ${v/.../$s}, then all backslashes are preserved in both cases.
+
+v='a*b\*c'
+echo 'Source:       ' "$v"
+echo 'Replace str:  ' '_\\_\z_'
+
+echo 'Pattern:      ' 'single backslash and star: "replace literal star"'
+echo 'Unquoted:     ' ${v/\*/_\\_\z_}
+r=${v/\*/_\\_\z_}
+echo 'Unquoted =:   ' "$r"
+echo 'Quoted:       ' "${v/\*/_\\_\z_}"
+r="${v/\*/_\\_\z_}"
+echo 'Quoted =:     ' "$r"
+
+echo 'Pattern:      ' 'double backslash and star: "replace backslash and everything after it"'
+echo 'Unquoted:     '  ${v/\\*/_\\_\z_}
+r=${v/\\*/_\\_\z_}
+echo 'Unquoted =:   ' "$r"
+echo 'Quoted:       ' "${v/\\*/_\\_\z_}"
+r="${v/\\*/_\\_\z_}"
+echo 'Quoted =:     ' "$r"
+
+echo
+
+v='a\bc'
+echo 'Source:       ' "$v"
+echo 'Replace str:  ' '_\\_\z_'
+
+echo 'Pattern:      ' 'single backslash and b: "replace b"'
+echo 'Unquoted:     '  ${v/\b/_\\_\z_}
+r=${v/\b/_\\_\z_}
+echo 'Unquoted =:   ' "$r"
+echo 'Quoted:       ' "${v/\b/_\\_\z_}"
+r="${v/\b/_\\_\z_}"
+echo 'Quoted =:     ' "$r"
+
+echo 'Pattern:      ' 'double backslash and b: "replace backslash and b"'
+echo 'Unquoted:     '  ${v/\\b/_\\_\z_}
+r=${v/\\b/_\\_\z_}
+echo 'Unquoted =:   ' "$r"
+echo 'Quoted:       ' "${v/\\b/_\\_\z_}"
+r="${v/\\b/_\\_\z_}"
+echo 'Quoted =:     ' "$r"
+
+echo
+
+v='a\bc'
+s='_\\_\z_'
+echo 'Source:       ' "$v"
+echo 'Replace str:  ' "$s" '(as variable $s)'
+
+echo 'Pattern:      ' 'single backslash and b: "replace b"'
+echo 'Unquoted:     '  ${v/\b/$s}
+r=${v/\b/$s}
+echo 'Unquoted =:   ' "$r"
+echo 'Quoted:       ' "${v/\b/$s}"
+r="${v/\b/$s}"
+echo 'Quoted =:     ' "$r"
+
+echo 'Pattern:      ' 'double backslash and b: "replace backslash and b"'
+echo 'Unquoted:     '  ${v/\\b/$s}
+r=${v/\\b/$s}
+echo 'Unquoted =:   ' "$r"
+echo 'Quoted:       ' "${v/\\b/$s}"
+r="${v/\\b/$s}"
+echo 'Quoted =:     ' "$r"
+
+echo
+
+echo Done: $?
diff --git a/shell/hush_test/hush-vars/var_bash5.right b/shell/hush_test/hush-vars/var_bash5.right
new file mode 100644 (file)
index 0000000..1990902
--- /dev/null
@@ -0,0 +1,11 @@
+1 a/
+2 a/d
+3 a/e/f
+4 a\
+5 a\d
+6 a\e\f
+7 a\\
+8 a\\d
+9 a\\e\\f
+a ab
+Done: 0
diff --git a/shell/hush_test/hush-vars/var_bash5.tests b/shell/hush_test/hush-vars/var_bash5.tests
new file mode 100755 (executable)
index 0000000..5748b4a
--- /dev/null
@@ -0,0 +1,29 @@
+# This testcase checks whether slashes in ${v/a/b} are parsed before
+# or after expansions
+
+v='a/b/c'
+s='b/c'
+r='e/f'
+echo "1 ${v/$s}"
+echo "2 ${v/$s/d}"
+echo "3 ${v/$s/$r}"
+
+v='a\b\c'
+s='b\\c'
+r='e\f'
+echo "4 ${v/$s}"
+echo "5 ${v/$s/d}"
+echo "6 ${v/$s/$r}"
+
+v='a\\b\\c'
+s='b\\\\c'
+r='e\\f'
+echo "7 ${v/$s}"
+echo "8 ${v/$s/d}"
+echo "9 ${v/$s/$r}"
+
+v='a-$a-\t-\\-\"-\`-\--\z-\*-\?-b'
+s='-$a-\\t-\\\\-\\"-\\`-\\--\\z-\\\*-\\\?-'
+echo "a ${v/$s}"
+
+echo Done: $?
diff --git a/shell/hush_test/hush-vars/var_bash6.right b/shell/hush_test/hush-vars/var_bash6.right
new file mode 100644 (file)
index 0000000..63fc23d
--- /dev/null
@@ -0,0 +1,5 @@
+Expected Actual
+a*z    : a*z
+\z     : \z
+a1z a2z: a1z a2z
+z      : z
diff --git a/shell/hush_test/hush-vars/var_bash6.tests b/shell/hush_test/hush-vars/var_bash6.tests
new file mode 100755 (executable)
index 0000000..cf2e4f0
--- /dev/null
@@ -0,0 +1,9 @@
+# This testcase checks globbing correctness in ${v/a/b}
+
+>a1z; >a2z;
+          echo 'Expected' 'Actual'
+v='a bz'; echo 'a*z    :' "${v/a*z/a*z}"
+v='a bz'; echo '\z     :' "${v/a*z/\z}"
+v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z}
+v='a bz'; echo 'z      :' ${v/a*z/\z}
+rm a1z a2z
diff --git a/shell/hush_test/hush-vars/var_expand_on_ifs.right b/shell/hush_test/hush-vars/var_expand_on_ifs.right
new file mode 100644 (file)
index 0000000..2ed2069
--- /dev/null
@@ -0,0 +1,9 @@
+1 a b c
+2 a + b c
+3 a b c
+4 a  b c
+5 a  b c
+6 a b + c
+7 a b c
+8 a b  c
+9 a b  c
diff --git a/shell/hush_test/hush-vars/var_expand_on_ifs.tests b/shell/hush_test/hush-vars/var_expand_on_ifs.tests
new file mode 100755 (executable)
index 0000000..a12ff8e
--- /dev/null
@@ -0,0 +1,11 @@
+b=' b '
+e=''
+echo 1 a $b c
+echo 2 a +$b c
+echo 3 a $e$b c
+echo 4 a "$e"$b c
+echo 5 a ""$b c
+echo 6 a $b+ c
+echo 7 a $b$e c
+echo 8 a $b"$e" c
+echo 9 a $b"" c
diff --git a/shell/hush_test/hush-vars/var_serial.right b/shell/hush_test/hush-vars/var_serial.right
new file mode 100644 (file)
index 0000000..42aa330
--- /dev/null
@@ -0,0 +1,5 @@
+Assignments only: c=a
+Assignments and a command: c=a
+Assignments and a builtin: c=a
+Assignments and a function: c=a
+Done
diff --git a/shell/hush_test/hush-vars/var_serial.tests b/shell/hush_test/hush-vars/var_serial.tests
new file mode 100755 (executable)
index 0000000..6b4a4cd
--- /dev/null
@@ -0,0 +1,22 @@
+a=a
+
+b=b
+c=c
+# Second assignment depends on the first:
+b=$a c=$b
+echo Assignments only: c=$c
+
+b=b
+c=c
+b=$a c=$b "$THIS_SH" -c 'echo Assignments and a command: c=$c'
+
+b=b
+c=c
+b=$a c=$b eval 'echo Assignments and a builtin: c=$c'
+
+b=b
+c=c
+f() { echo Assignments and a function: c=$c; }
+b=$a c=$b f
+
+echo Done
diff --git a/shell/hush_test/hush-vars/var_unbackslash.right b/shell/hush_test/hush-vars/var_unbackslash.right
new file mode 100644 (file)
index 0000000..8bc8347
--- /dev/null
@@ -0,0 +1,11 @@
+b1=-qwerty-t-\-"-`---z-*-?-
+b1=-qwerty-t-\-"-`---z-*-?-
+b2=-qwerty-\t-\-"-`-\--\z-\*-\?-
+b2=-qwerty-\t-\-"-`-\--\z-\*-\?-
+b3=-$a-\t-\\-\"-\`-\--\z-\*-\?-
+b3=-$a-\t-\\-\"-\`-\--\z-\*-\?-
+c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
+c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
+c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
+c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
+Done: 0
diff --git a/shell/hush_test/hush-vars/var_unbackslash.tests b/shell/hush_test/hush-vars/var_unbackslash.tests
new file mode 100755 (executable)
index 0000000..bb52af3
--- /dev/null
@@ -0,0 +1,23 @@
+# Test for correct handling of backslashes
+a=qwerty
+
+b=-$a-\t-\\-\"-\`-\--\z-\*-\?-
+echo b1=$b
+echo "b1=$b"
+b="-$a-\t-\\-\"-\`-\--\z-\*-\?-"
+echo b2=$b
+echo "b2=$b"
+b='-$a-\t-\\-\"-\`-\--\z-\*-\?-'
+echo b3=$b
+echo "b3=$b"
+
+c=$b
+echo "c=$c"
+c=${b}
+echo "c=$c"
+c="$b"
+echo "c=$c"
+c="${b}"
+echo "c=$c"
+
+echo "Done: $?"
index 256f152..64a7abc 100755 (executable)
@@ -48,8 +48,9 @@ do_test()
                        *.orig|*~) ;;
                        #*) echo $x ; sh $x ;;
                        *)
+                       echo -n "$1/$x:"
                        sh "$x" >"../$1-$x.fail" 2>&1 && \
-                       { echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail";
+                       { { echo " ok"; rm "../$1-$x.fail"; } || echo " fail"; }
                        ;;
                esac
        done
@@ -60,6 +61,7 @@ do_test()
        name="${x%%.tests}"
        test -f "$name.right" || continue
 #      echo Running test: "$x"
+       echo -n "$1/$x:"
        (
                "$THIS_SH" "./$x" >"$name.xx" 2>&1
                # filter C library differences
@@ -70,9 +72,9 @@ do_test()
                diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail"
        )
        case $? in
-               0)  echo "$1/$x: ok";;
-               77) echo "$1/$x: skip (feature disabled)";;
-               *)  echo "$1/$x: fail"; tret=1;;
+               0)  echo " ok";;
+               77) echo " skip (feature disabled)";;
+               *)  echo " fail"; tret=1;;
        esac
        done
        exit ${tret}
index fb6a38e..fee3cf2 100644 (file)
@@ -4,7 +4,7 @@
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * Copyright (c) 1989, 1991, 1993, 1994
  *      The Regents of the University of California.  All rights reserved.
 # include <stdlib.h>
 # include <string.h>
 # include <unistd.h>
+# define FAST_FUNC /* nothing */
+# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* nothing */
+# define POP_SAVED_FUNCTION_VISIBILITY /* nothing */
 #else
 # include "libbb.h"
 #endif
 #include <fnmatch.h>
 #include "match.h"
 
-#define pmatch(a, b) !fnmatch((a), (b), 0)
-
-char *scanleft(char *string, char *pattern, bool match_at_left)
+char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags)
 {
-       char c;
-       char *loc = string;
-
-       do {
-               int match;
-               const char *s;
-
-               c = *loc;
-               if (match_at_left) {
-                       *loc = '\0';
-                       s = string;
-               } else
-                       s = loc;
-               match = pmatch(pattern, s);
-               *loc = c;
-
-               if (match)
-                       return loc;
-
-               loc++;
-       } while (c);
-
-       return NULL;
-}
-
-char *scanright(char *string, char *pattern, bool match_at_left)
-{
-       char c;
-       char *loc = string + strlen(string);
+       char *loc;
+       char *end;
+       unsigned len = strlen(string);
+       int early_exit;
+
+       /* We can stop the scan early only if the string part
+        * we are matching against is shrinking, and the pattern has
+        * an unquoted "star" at the corresponding end. There are two cases.
+        * Case 1:
+        * "qwerty" does not match against pattern "*zy",
+        * no point in trying to match "werty", "erty" etc:
+        */
+       early_exit = (flags == (SCAN_MOVE_FROM_LEFT + SCAN_MATCH_RIGHT_HALF) && pattern[0] == '*');
+
+       if (flags & SCAN_MOVE_FROM_LEFT) {
+               loc = string;
+               end = string + len + 1;
+       } else {
+               loc = string + len;
+               end = string - 1;
+               if (flags == (SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF)) {
+                       /* Case 2:
+                        * "qwerty" does not match against pattern "qz*",
+                        * no point in trying to match "qwert", "qwer" etc:
+                        */
+                       const char *p = pattern + strlen(pattern);
+                       if (--p >= pattern && *p == '*') {
+                               early_exit = 1;
+                               while (--p >= pattern && *p == '\\')
+                                       early_exit ^= 1;
+                       }
+               }
+       }
 
-       while (loc >= string) {
-               int match;
-               const char *s;
+       while (loc != end) {
+               char c;
+               int r;
 
                c = *loc;
-               if (match_at_left) {
+               if (flags & SCAN_MATCH_LEFT_HALF) {
                        *loc = '\0';
-                       s = string;
-               } else
-                       s = loc;
-               match = pmatch(pattern, s);
-               *loc = c;
-
-               if (match)
+                       r = fnmatch(pattern, string, 0);
+                       *loc = c;
+               } else {
+                       r = fnmatch(pattern, loc, 0);
+               }
+               if (r == 0) /* match found */
                        return loc;
+               if (early_exit) {
+#ifdef STANDALONE
+                       printf("(early exit) ");
+#endif
+                       break;
+               }
 
-               loc--;
+               if (flags & SCAN_MOVE_FROM_LEFT) {
+                       loc++;
+               } else {
+                       loc--;
+               }
        }
-
        return NULL;
 }
 
@@ -86,12 +99,11 @@ int main(int argc, char *argv[])
        char *string;
        char *op;
        char *pattern;
-       bool match_at_left;
        char *loc;
 
-       int i;
+       setvbuf(stdout, NULL, _IONBF, 0);
 
-       if (argc == 1) {
+       if (!argv[1]) {
                puts(
                        "Usage: match <test> [test...]\n\n"
                        "Where a <test> is the form: <string><op><match>\n"
@@ -101,36 +113,34 @@ int main(int argc, char *argv[])
                return 1;
        }
 
-       for (i = 1; i < argc; ++i) {
+       while (*++argv) {
                size_t off;
-               scan_t scan;
-
-               printf("'%s': ", argv[i]);
+               unsigned scan_flags;
 
-               string = strdup(argv[i]);
+               string = *argv;
                off = strcspn(string, "#%");
                if (!off) {
                        printf("invalid format\n");
-                       free(string);
                        continue;
                }
                op = string + off;
-               scan = pick_scan(op[0], op[1], &match_at_left);
+               scan_flags = pick_scan(op[0], op[1]);
+
+               printf("'%s': flags:%x, ", string, scan_flags);
                pattern = op + 1;
                if (op[0] == op[1])
-                       op[1] = '\0', ++pattern;
+                       pattern++;
                op[0] = '\0';
 
-               loc = scan(string, pattern, match_at_left);
+               loc = scan_and_match(string, pattern, scan_flags);
 
-               if (match_at_left) {
+               if (scan_flags & SCAN_MATCH_LEFT_HALF) {
                        printf("'%s'\n", loc);
                } else {
-                       *loc = '\0';
+                       if (loc)
+                               *loc = '\0';
                        printf("'%s'\n", string);
                }
-
-               free(string);
        }
 
        return 0;
index c022ceb..aa393ed 100644 (file)
@@ -7,25 +7,26 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 
 //TODO! Why ash.c still uses internal version?!
 
-typedef char *(*scan_t)(char *string, char *match, bool match_at_left);
+enum {
+       SCAN_MOVE_FROM_LEFT = (1 << 0),
+       SCAN_MOVE_FROM_RIGHT = (1 << 1),
+       SCAN_MATCH_LEFT_HALF = (1 << 2),
+       SCAN_MATCH_RIGHT_HALF = (1 << 3),
+};
 
-char *scanleft(char *string, char *match, bool match_at_left);
-char *scanright(char *string, char *match, bool match_at_left);
+char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags);
 
-static inline scan_t pick_scan(char op1, char op2, bool *match_at_left)
+static inline unsigned pick_scan(char op1, char op2)
 {
-       /* #  - scanleft
-        * ## - scanright
-        * %  - scanright
-        * %% - scanleft
-        */
+       unsigned scan_flags;
        if (op1 == '#') {
-               *match_at_left = true;
-               return op1 == op2 ? scanright : scanleft;
-       } else {
-               *match_at_left = false;
-               return op1 == op2 ? scanleft : scanright;
+               scan_flags = SCAN_MATCH_LEFT_HALF +
+                       (op1 == op2 ? SCAN_MOVE_FROM_RIGHT : SCAN_MOVE_FROM_LEFT);
+       } else { /* % */
+               scan_flags = SCAN_MATCH_RIGHT_HALF +
+                       (op1 == op2 ? SCAN_MOVE_FROM_LEFT : SCAN_MOVE_FROM_RIGHT);
        }
+       return scan_flags;
 }
 
 POP_SAVED_FUNCTION_VISIBILITY
index 91fb28f..3da1511 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * arithmetic code ripped out of ash shell for code sharing
+ * Arithmetic code ripped out of ash shell for code sharing.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
  * rewrote arith (see notes to this), added locale support,
  * rewrote dynamic variables.
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 /* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
-
  Permission is hereby granted, free of charge, to any person obtaining
  a copy of this software and associated documentation files (the
  "Software"), to deal in the Software without restriction, including
  without limitation the rights to use, copy, modify, merge, publish,
  distribute, sublicense, and/or sell copies of the Software, and to
  permit persons to whom the Software is furnished to do so, subject to
  the following conditions:
-
  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.
-
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
+ *
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
+ *
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
+ *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
 
 /* This is my infix parser/evaluator. It is optimized for size, intended
  * as a replacement for yacc-based parsers. However, it may well be faster
  * than a comparable parser written in yacc. The supported operators are
  * listed in #defines below. Parens, order of operations, and error handling
  * are supported. This code is thread safe. The exact expression format should
- * be that which POSIX specifies for shells. */
-
-/* The code uses a simple two-stack algorithm. See
+ * be that which POSIX specifies for shells.
+ *
+ * The code uses a simple two-stack algorithm. See
  * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
  * for a detailed explanation of the infix-to-postfix algorithm on which
  * this is based (this code differs in that it applies operators immediately
  * to the stack instead of adding them to a queue to end up with an
- * expression). */
-
-/* To use the routine, call it with an expression string and error return
- * pointer */
+ * expression).
+ */
 
 /*
  * Aug 24, 2001              Manuel Novoa III
  *  (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
  *
  * - allow access to variable,
- *   used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
- * - realize assign syntax (VAR=expr, +=, *= etc)
- * - realize exponentiation (** operator)
- * - realize comma separated - expr, expr
- * - realise ++expr --expr expr++ expr--
- * - realise expr ? expr : expr (but, second expr calculate always)
+ *   use recursive value indirection: c="2*2"; a="c"; echo $((a+=2)) produce 6
+ * - implement assign syntax (VAR=expr, +=, *= etc)
+ * - implement exponentiation (** operator)
+ * - implement comma separated - expr, expr
+ * - implement ++expr --expr expr++ expr--
+ * - implement expr ? expr : expr (but second expr is always calculated)
  * - allow hexadecimal and octal numbers
- * - was restored loses XOR operator
- * - remove one goto label, added three ;-)
- * - protect $((num num)) as true zero expr (Manuel`s error)
+ * - restore lost XOR operator
+ * - protect $((num num)) as true zero expr (Manuel's error)
  * - always use special isspace(), see comment from bash ;-)
  */
 #include "libbb.h"
 #include "math.h"
 
-#define a_e_h_t arith_eval_hooks_t
-#define lookupvar (math_hooks->lookupvar)
-#define setvar    (math_hooks->setvar   )
-#define endofname (math_hooks->endofname)
-
-#define arith_isspace(arithval) \
-       (arithval == ' ' || arithval == '\n' || arithval == '\t')
+#define lookupvar (math_state->lookupvar)
+#define setvar    (math_state->setvar   )
+//#define endofname (math_state->endofname)
 
 typedef unsigned char operator;
 
@@ -133,181 +126,199 @@ typedef unsigned char operator;
  * precedence, and 3 high bits are an ID unique across operators of that
  * precedence. The ID portion is so that multiple operators can have the
  * same precedence, ensuring that the leftmost one is evaluated first.
- * Consider * and /. */
-
-#define tok_decl(prec,id) (((id)<<5)|(prec))
-#define PREC(op) ((op) & 0x1F)
-
-#define TOK_LPAREN tok_decl(0,0)
+ * Consider * and /
+ */
+#define tok_decl(prec,id)       (((id)<<5) | (prec))
+#define PREC(op)                ((op) & 0x1F)
 
-#define TOK_COMMA tok_decl(1,0)
+#define TOK_LPAREN              tok_decl(0,0)
 
-#define TOK_ASSIGN tok_decl(2,0)
-#define TOK_AND_ASSIGN tok_decl(2,1)
-#define TOK_OR_ASSIGN tok_decl(2,2)
-#define TOK_XOR_ASSIGN tok_decl(2,3)
-#define TOK_PLUS_ASSIGN tok_decl(2,4)
-#define TOK_MINUS_ASSIGN tok_decl(2,5)
-#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
-#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
+#define TOK_COMMA               tok_decl(1,0)
 
-#define TOK_MUL_ASSIGN tok_decl(3,0)
-#define TOK_DIV_ASSIGN tok_decl(3,1)
-#define TOK_REM_ASSIGN tok_decl(3,2)
+/* All assignments are right associative and have the same precedence,
+ * but there are 11 of them, which doesn't fit into 3 bits for unique id.
+ * Abusing another precedence level:
+ */
+#define TOK_ASSIGN              tok_decl(2,0)
+#define TOK_AND_ASSIGN          tok_decl(2,1)
+#define TOK_OR_ASSIGN           tok_decl(2,2)
+#define TOK_XOR_ASSIGN          tok_decl(2,3)
+#define TOK_PLUS_ASSIGN         tok_decl(2,4)
+#define TOK_MINUS_ASSIGN        tok_decl(2,5)
+#define TOK_LSHIFT_ASSIGN       tok_decl(2,6)
+#define TOK_RSHIFT_ASSIGN       tok_decl(2,7)
 
-/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
-#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
+#define TOK_MUL_ASSIGN          tok_decl(3,0)
+#define TOK_DIV_ASSIGN          tok_decl(3,1)
+#define TOK_REM_ASSIGN          tok_decl(3,2)
 
-/* conditional is right associativity too */
-#define TOK_CONDITIONAL tok_decl(4,0)
-#define TOK_CONDITIONAL_SEP tok_decl(4,1)
+#define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0)
 
-#define TOK_OR tok_decl(5,0)
+/* Ternary conditional operator is right associative too */
+#define TOK_CONDITIONAL         tok_decl(4,0)
+#define TOK_CONDITIONAL_SEP     tok_decl(4,1)
 
-#define TOK_AND tok_decl(6,0)
+#define TOK_OR                  tok_decl(5,0)
 
-#define TOK_BOR tok_decl(7,0)
+#define TOK_AND                 tok_decl(6,0)
 
-#define TOK_BXOR tok_decl(8,0)
+#define TOK_BOR                 tok_decl(7,0)
 
-#define TOK_BAND tok_decl(9,0)
+#define TOK_BXOR                tok_decl(8,0)
 
-#define TOK_EQ tok_decl(10,0)
-#define TOK_NE tok_decl(10,1)
+#define TOK_BAND                tok_decl(9,0)
 
-#define TOK_LT tok_decl(11,0)
-#define TOK_GT tok_decl(11,1)
-#define TOK_GE tok_decl(11,2)
-#define TOK_LE tok_decl(11,3)
+#define TOK_EQ                  tok_decl(10,0)
+#define TOK_NE                  tok_decl(10,1)
 
-#define TOK_LSHIFT tok_decl(12,0)
-#define TOK_RSHIFT tok_decl(12,1)
+#define TOK_LT                  tok_decl(11,0)
+#define TOK_GT                  tok_decl(11,1)
+#define TOK_GE                  tok_decl(11,2)
+#define TOK_LE                  tok_decl(11,3)
 
-#define TOK_ADD tok_decl(13,0)
-#define TOK_SUB tok_decl(13,1)
+#define TOK_LSHIFT              tok_decl(12,0)
+#define TOK_RSHIFT              tok_decl(12,1)
 
-#define TOK_MUL tok_decl(14,0)
-#define TOK_DIV tok_decl(14,1)
-#define TOK_REM tok_decl(14,2)
+#define TOK_ADD                 tok_decl(13,0)
+#define TOK_SUB                 tok_decl(13,1)
 
-/* exponent is right associativity */
-#define TOK_EXPONENT tok_decl(15,1)
+#define TOK_MUL                 tok_decl(14,0)
+#define TOK_DIV                 tok_decl(14,1)
+#define TOK_REM                 tok_decl(14,2)
 
-/* For now unary operators. */
-#define UNARYPREC 16
-#define TOK_BNOT tok_decl(UNARYPREC,0)
-#define TOK_NOT tok_decl(UNARYPREC,1)
+/* Exponent is right associative */
+#define TOK_EXPONENT            tok_decl(15,1)
 
-#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
-#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
+/* Unary operators */
+#define UNARYPREC               16
+#define TOK_BNOT                tok_decl(UNARYPREC,0)
+#define TOK_NOT                 tok_decl(UNARYPREC,1)
 
-#define PREC_PRE (UNARYPREC+2)
+#define TOK_UMINUS              tok_decl(UNARYPREC+1,0)
+#define TOK_UPLUS               tok_decl(UNARYPREC+1,1)
 
-#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
-#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
+#define PREC_PRE                (UNARYPREC+2)
 
-#define PREC_POST (UNARYPREC+3)
+#define TOK_PRE_INC             tok_decl(PREC_PRE, 0)
+#define TOK_PRE_DEC             tok_decl(PREC_PRE, 1)
 
-#define TOK_POST_INC tok_decl(PREC_POST, 0)
-#define TOK_POST_DEC tok_decl(PREC_POST, 1)
+#define PREC_POST               (UNARYPREC+3)
 
-#define SPEC_PREC (UNARYPREC+4)
+#define TOK_POST_INC            tok_decl(PREC_POST, 0)
+#define TOK_POST_DEC            tok_decl(PREC_POST, 1)
 
-#define TOK_NUM tok_decl(SPEC_PREC, 0)
-#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
+#define SPEC_PREC               (UNARYPREC+4)
 
-#define NUMPTR (*numstackptr)
+#define TOK_NUM                 tok_decl(SPEC_PREC, 0)
+#define TOK_RPAREN              tok_decl(SPEC_PREC, 1)
 
 static int
-tok_have_assign(operator op)
+is_assign_op(operator op)
 {
        operator prec = PREC(op);
-
-       convert_prec_is_assing(prec);
-       return (prec == PREC(TOK_ASSIGN) ||
-                       prec == PREC_PRE || prec == PREC_POST);
+       fix_assignment_prec(prec);
+       return prec == PREC(TOK_ASSIGN)
+       || prec == PREC_PRE
+       || prec == PREC_POST;
 }
 
 static int
-is_right_associativity(operator prec)
+is_right_associative(operator prec)
 {
-       return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
-               || prec == PREC(TOK_CONDITIONAL));
+       return prec == PREC(TOK_ASSIGN)
+       || prec == PREC(TOK_EXPONENT)
+       || prec == PREC(TOK_CONDITIONAL);
 }
 
+
 typedef struct {
        arith_t val;
-       arith_t contidional_second_val;
-       char contidional_second_val_initialized;
-       char *var;      /* if NULL then is regular number,
-                          else is variable name */
-} v_n_t;
-
-typedef struct chk_var_recursive_looped_t {
+       /* We acquire second_val only when "expr1 : expr2" part
+        * of ternary ?: op is evaluated.
+        * We treat ?: as two binary ops: (expr ? (expr1 : expr2)).
+        * ':' produces a new value which has two parts, val and second_val;
+        * then '?' selects one of them based on its left side.
+        */
+       arith_t second_val;
+       char second_val_present;
+       /* If NULL then it's just a number, else it's a named variable */
+       char *var;
+} var_or_num_t;
+
+typedef struct remembered_name {
+       struct remembered_name *next;
        const char *var;
-       struct chk_var_recursive_looped_t *next;
-} chk_var_recursive_looped_t;
+} remembered_name;
 
-static chk_var_recursive_looped_t *prev_chk_var_recursive;
 
-static int
-arith_lookup_val(v_n_t *t, a_e_h_t *math_hooks)
+static arith_t FAST_FUNC
+evaluate_string(arith_state_t *math_state, const char *expr);
+
+static const char*
+arith_lookup_val(arith_state_t *math_state, var_or_num_t *t)
 {
        if (t->var) {
                const char *p = lookupvar(t->var);
-
                if (p) {
-                       int errcode;
-
-                       /* recursive try as expression */
-                       chk_var_recursive_looped_t *cur;
-                       chk_var_recursive_looped_t cur_save;
+                       remembered_name *cur;
+                       remembered_name cur_save;
 
-                       for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
+                       /* did we already see this name?
+                        * testcase: a=b; b=a; echo $((a))
+                        */
+                       for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) {
                                if (strcmp(cur->var, t->var) == 0) {
-                                       /* expression recursion loop detected */
-                                       return -5;
+                                       /* Yes */
+                                       return "expression recursion loop detected";
                                }
                        }
-                       /* save current lookuped var name */
-                       cur = prev_chk_var_recursive;
+
+                       /* push current var name */
+                       cur = math_state->list_of_recursed_names;
                        cur_save.var = t->var;
                        cur_save.next = cur;
-                       prev_chk_var_recursive = &cur_save;
+                       math_state->list_of_recursed_names = &cur_save;
+
+                       /* recursively evaluate p as expression */
+                       t->val = evaluate_string(math_state, p);
 
-                       t->val = arith (p, &errcode, math_hooks);
-                       /* restore previous ptr after recursiving */
-                       prev_chk_var_recursive = cur;
-                       return errcode;
+                       /* pop current var name */
+                       math_state->list_of_recursed_names = cur;
+
+                       return math_state->errmsg;
                }
-               /* allow undefined var as 0 */
+               /* treat undefined var as 0 */
                t->val = 0;
        }
        return 0;
 }
 
-/* "applying" a token means performing it on the top elements on the integer
- * stack. For a unary operator it will only change the top element, but a
- * binary operator will pop two arguments and push a result */
-static NOINLINE int
-arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hooks)
+/* "Applying" a token means performing it on the top elements on the integer
+ * stack. For an unary operator it will only change the top element, but a
+ * binary operator will pop two arguments and push the result */
+static NOINLINE const char*
+arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_or_num_t **numstackptr)
 {
-       v_n_t *numptr_m1;
-       arith_t numptr_val, rez;
-       int ret_arith_lookup_val;
+#define NUMPTR (*numstackptr)
+
+       var_or_num_t *top_of_stack;
+       arith_t rez;
+       const char *err;
 
        /* There is no operator that can work without arguments */
-       if (NUMPTR == numstack) goto err;
-       numptr_m1 = NUMPTR - 1;
+       if (NUMPTR == numstack)
+               goto err;
+
+       top_of_stack = NUMPTR - 1;
 
-       /* check operand is var with noninteger value */
-       ret_arith_lookup_val = arith_lookup_val(numptr_m1, math_hooks);
-       if (ret_arith_lookup_val)
-               return ret_arith_lookup_val;
+       /* Resolve name to value, if needed */
+       err = arith_lookup_val(math_state, top_of_stack);
+       if (err)
+               return err;
 
-       rez = numptr_m1->val;
+       rez = top_of_stack->val;
        if (op == TOK_UMINUS)
-               rez *= -1;
+               rez = -rez;
        else if (op == TOK_NOT)
                rez = !rez;
        else if (op == TOK_BNOT)
@@ -318,118 +329,123 @@ arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hoo
                rez--;
        else if (op != TOK_UPLUS) {
                /* Binary operators */
+               arith_t right_side_val;
+               char bad_second_val;
 
-               /* check and binary operators need two arguments */
-               if (numptr_m1 == numstack) goto err;
-
-               /* ... and they pop one */
-               --NUMPTR;
-               numptr_val = rez;
-               if (op == TOK_CONDITIONAL) {
-                       if (!numptr_m1->contidional_second_val_initialized) {
-                               /* protect $((expr1 ? expr2)) without ": expr" */
-                               goto err;
-                       }
-                       rez = numptr_m1->contidional_second_val;
-               } else if (numptr_m1->contidional_second_val_initialized) {
-                       /* protect $((expr1 : expr2)) without "expr ? " */
+               /* Binary operators need two arguments */
+               if (top_of_stack == numstack)
                        goto err;
+               /* ...and they pop one */
+               NUMPTR = top_of_stack; /* this decrements NUMPTR */
+
+               bad_second_val = top_of_stack->second_val_present;
+               if (op == TOK_CONDITIONAL) { /* ? operation */
+                       /* Make next if (...) protect against
+                        * $((expr1 ? expr2)) - that is, missing ": expr" */
+                       bad_second_val = !bad_second_val;
+               }
+               if (bad_second_val) {
+                       /* Protect against $((expr <not_?_op> expr1 : expr2)) */
+                       return "malformed ?: operator";
                }
-               numptr_m1 = NUMPTR - 1;
+
+               top_of_stack--; /* now points to left side */
+
                if (op != TOK_ASSIGN) {
-                       /* check operand is var with noninteger value for not '=' */
-                       ret_arith_lookup_val = arith_lookup_val(numptr_m1, math_hooks);
-                       if (ret_arith_lookup_val)
-                               return ret_arith_lookup_val;
+                       /* Resolve left side value (unless the op is '=') */
+                       err = arith_lookup_val(math_state, top_of_stack);
+                       if (err)
+                               return err;
                }
-               if (op == TOK_CONDITIONAL) {
-                       numptr_m1->contidional_second_val = rez;
+
+               right_side_val = rez;
+               rez = top_of_stack->val;
+               if (op == TOK_CONDITIONAL) /* ? operation */
+                       rez = (rez ? right_side_val : top_of_stack[1].second_val);
+               else if (op == TOK_CONDITIONAL_SEP) { /* : operation */
+                       if (top_of_stack == numstack) {
+                               /* Protect against $((expr : expr)) */
+                               return "malformed ?: operator";
+                       }
+                       top_of_stack->second_val_present = op;
+                       top_of_stack->second_val = right_side_val;
                }
-               rez = numptr_m1->val;
-               if (op == TOK_BOR || op == TOK_OR_ASSIGN)
-                       rez |= numptr_val;
+               else if (op == TOK_BOR || op == TOK_OR_ASSIGN)
+                       rez |= right_side_val;
                else if (op == TOK_OR)
-                       rez = numptr_val || rez;
+                       rez = right_side_val || rez;
                else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
-                       rez &= numptr_val;
+                       rez &= right_side_val;
                else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
-                       rez ^= numptr_val;
+                       rez ^= right_side_val;
                else if (op == TOK_AND)
-                       rez = rez && numptr_val;
+                       rez = rez && right_side_val;
                else if (op == TOK_EQ)
-                       rez = (rez == numptr_val);
+                       rez = (rez == right_side_val);
                else if (op == TOK_NE)
-                       rez = (rez != numptr_val);
+                       rez = (rez != right_side_val);
                else if (op == TOK_GE)
-                       rez = (rez >= numptr_val);
+                       rez = (rez >= right_side_val);
                else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
-                       rez >>= numptr_val;
+                       rez >>= right_side_val;
                else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
-                       rez <<= numptr_val;
+                       rez <<= right_side_val;
                else if (op == TOK_GT)
-                       rez = (rez > numptr_val);
+                       rez = (rez > right_side_val);
                else if (op == TOK_LT)
-                       rez = (rez < numptr_val);
+                       rez = (rez < right_side_val);
                else if (op == TOK_LE)
-                       rez = (rez <= numptr_val);
+                       rez = (rez <= right_side_val);
                else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
-                       rez *= numptr_val;
+                       rez *= right_side_val;
                else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
-                       rez += numptr_val;
+                       rez += right_side_val;
                else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
-                       rez -= numptr_val;
+                       rez -= right_side_val;
                else if (op == TOK_ASSIGN || op == TOK_COMMA)
-                       rez = numptr_val;
-               else if (op == TOK_CONDITIONAL_SEP) {
-                       if (numptr_m1 == numstack) {
-                               /* protect $((expr : expr)) without "expr ? " */
-                               goto err;
-                       }
-                       numptr_m1->contidional_second_val_initialized = op;
-                       numptr_m1->contidional_second_val = numptr_val;
-               } else if (op == TOK_CONDITIONAL) {
-                       rez = rez ?
-                               numptr_val : numptr_m1->contidional_second_val;
-               } else if (op == TOK_EXPONENT) {
-                       if (numptr_val < 0)
-                               return -3;      /* exponent less than 0 */
-                       else {
-                               arith_t c = 1;
-
-                               if (numptr_val)
-                                       while (numptr_val--)
-                                               c *= rez;
-                               rez = c;
-                       }
-               } else if (numptr_val==0)          /* zero divisor check */
-                       return -2;
+                       rez = right_side_val;
+               else if (op == TOK_EXPONENT) {
+                       arith_t c;
+                       if (right_side_val < 0)
+                               return "exponent less than 0";
+                       c = 1;
+                       while (--right_side_val >= 0)
+                               c *= rez;
+                       rez = c;
+               }
+               else if (right_side_val == 0)
+                       return "divide by zero";
                else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
-                       rez /= numptr_val;
+                       rez /= right_side_val;
                else if (op == TOK_REM || op == TOK_REM_ASSIGN)
-                       rez %= numptr_val;
+                       rez %= right_side_val;
        }
-       if (tok_have_assign(op)) {
+
+       if (is_assign_op(op)) {
                char buf[sizeof(arith_t)*3 + 2];
 
-               if (numptr_m1->var == NULL) {
+               if (top_of_stack->var == NULL) {
                        /* Hmm, 1=2 ? */
+//TODO: actually, bash allows ++7 but for some reason it evals to 7, not 8
                        goto err;
                }
-               /* save to shell variable */
-               sprintf(buf, arith_t_fmt, rez);
-               setvar(numptr_m1->var, buf);
-               /* after saving, make previous value for v++ or v-- */
+               /* Save to shell variable */
+               sprintf(buf, ARITH_FMT, rez);
+               setvar(top_of_stack->var, buf);
+               /* After saving, make previous value for v++ or v-- */
                if (op == TOK_POST_INC)
                        rez--;
                else if (op == TOK_POST_DEC)
                        rez++;
        }
-       numptr_m1->val = rez;
-       /* protect geting var value, is number now */
-       numptr_m1->var = NULL;
-       return 0;
+
+       top_of_stack->val = rez;
+       /* Erase var name, it is just a number now */
+       top_of_stack->var = NULL;
+       return NULL;
  err:
-       return -1;
+       return "arithmetic syntax error";
+#undef NUMPTR
 }
 
 /* longest must be first */
@@ -476,38 +492,42 @@ static const char op_tokens[] ALIGN1 = {
        '(',        0, TOK_LPAREN,
        0
 };
-/* ptr to ")" */
-#define endexpression (&op_tokens[sizeof(op_tokens)-7])
+#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7])
 
-arith_t
-arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
+static arith_t FAST_FUNC
+evaluate_string(arith_state_t *math_state, const char *expr)
 {
-       char arithval; /* Current character under analysis */
-       operator lasttok, op;
-       operator prec;
-       operator *stack, *stackptr;
-       const char *p = endexpression;
-       int errcode;
-       v_n_t *numstack, *numstackptr;
-       unsigned datasizes = strlen(expr) + 2;
-
+       operator lasttok;
+       const char *errmsg;
+       const char *start_expr = expr = skip_whitespace(expr);
+       unsigned expr_len = strlen(expr) + 2;
        /* Stack of integers */
-       /* The proof that there can be no more than strlen(startbuf)/2+1 integers
-        * in any given correct or incorrect expression is left as an exercise to
-        * the reader. */
-       numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
+       /* The proof that there can be no more than strlen(startbuf)/2+1
+        * integers in any given correct or incorrect expression
+        * is left as an exercise to the reader. */
+       var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0]));
+       var_or_num_t *numstackptr = numstack;
        /* Stack of operator tokens */
-       stackptr = stack = alloca(datasizes * sizeof(stack[0]));
+       operator *const stack = alloca(expr_len * sizeof(stack[0]));
+       operator *stackptr = stack;
 
-       *stackptr++ = lasttok = TOK_LPAREN;     /* start off with a left paren */
-       *perrcode = errcode = 0;
+       /* Start with a left paren */
+       *stackptr++ = lasttok = TOK_LPAREN;
+       errmsg = NULL;
 
        while (1) {
+               const char *p;
+               operator op;
+               operator prec;
+               char arithval;
+
+               expr = skip_whitespace(expr);
                arithval = *expr;
-               if (arithval == 0) {
-                       if (p == endexpression) {
-                               /* Null expression. */
-                               return 0;
+               if (arithval == '\0') {
+                       if (expr == start_expr) {
+                               /* Null expression */
+                               numstack->val = 0;
+                               goto ret;
                        }
 
                        /* This is only reached after all tokens have been extracted from the
@@ -515,77 +535,80 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
                         * are to be applied in order. At the end, there should be a final
                         * result on the integer stack */
 
-                       if (expr != endexpression + 1) {
-                               /* If we haven't done so already, */
-                               /* append a closing right paren */
-                               expr = endexpression;
-                               /* and let the loop process it. */
+                       if (expr != ptr_to_rparen + 1) {
+                               /* If we haven't done so already,
+                                * append a closing right paren
+                                * and let the loop process it */
+                               expr = ptr_to_rparen;
                                continue;
                        }
-                       /* At this point, we're done with the expression. */
-                       if (numstackptr != numstack+1) {
-                               /* ... but if there isn't, it's bad */
- err:
-                               *perrcode = -1;
-                               return *perrcode;
+                       /* At this point, we're done with the expression */
+                       if (numstackptr != numstack + 1) {
+                               /* ...but if there isn't, it's bad */
+                               goto err;
                        }
                        if (numstack->var) {
                                /* expression is $((var)) only, lookup now */
-                               errcode = arith_lookup_val(numstack, math_hooks);
+                               errmsg = arith_lookup_val(math_state, numstack);
                        }
- ret:
-                       *perrcode = errcode;
-                       return numstack->val;
+                       goto ret;
                }
 
-               /* Continue processing the expression. */
-               if (arith_isspace(arithval)) {
-                       /* Skip whitespace */
-                       goto prologue;
-               }
                p = endofname(expr);
                if (p != expr) {
-                       size_t var_name_size = (p-expr) + 1;  /* trailing zero */
-
+                       /* Name */
+                       size_t var_name_size = (p-expr) + 1;  /* +1 for NUL */
                        numstackptr->var = alloca(var_name_size);
                        safe_strncpy(numstackptr->var, expr, var_name_size);
                        expr = p;
  num:
-                       numstackptr->contidional_second_val_initialized = 0;
+                       numstackptr->second_val_present = 0;
                        numstackptr++;
                        lasttok = TOK_NUM;
                        continue;
                }
+
                if (isdigit(arithval)) {
+                       /* Number */
                        numstackptr->var = NULL;
                        errno = 0;
-                       /* call strtoul[l]: */
-                       numstackptr->val = strto_arith_t(expr, (char **) &expr, 0);
+                       numstackptr->val = strto_arith_t(expr, (char**) &expr, 0);
                        if (errno)
                                numstackptr->val = 0; /* bash compat */
                        goto num;
                }
-               for (p = op_tokens; ; p++) {
-                       const char *o;
 
-                       if (*p == 0) {
-                               /* strange operator not found */
-                               goto err;
-                       }
-                       for (o = expr; *p && *o == *p; p++)
-                               o++;
-                       if (!*p) {
-                               /* found */
-                               expr = o - 1;
-                               break;
+               /* Should be an operator */
+               p = op_tokens;
+               while (1) {
+// TODO: bash allows 7+++v, treats it as 7 + ++v
+// we treat it as 7++ + v and reject
+                       /* Compare expr to current op_tokens[] element */
+                       const char *e = expr;
+                       while (1) {
+                               if (*p == '\0') {
+                                       /* Match: operator is found */
+                                       expr = e;
+                                       goto tok_found;
+                               }
+                               if (*p != *e)
+                                       break;
+                               p++;
+                               e++;
                        }
-                       /* skip tail uncompared token */
+                       /* No match, go to next element of op_tokens[] */
                        while (*p)
                                p++;
-                       /* skip zero delim */
-                       p++;
+                       p += 2; /* skip NUL and TOK_foo bytes */
+                       if (*p == '\0') {
+                               /* No next element, operator not found */
+                               //math_state->syntax_error_at = expr;
+                               goto err;
+                       }
                }
-               op = p[1];
+ tok_found:
+               op = p[1]; /* fetch TOK_foo value */
+               /* NB: expr now points past the operator */
 
                /* post grammar: a++ reduce to num */
                if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
@@ -614,13 +637,13 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
                /* We don't want an unary operator to cause recursive descent on the
                 * stack, because there can be many in a row and it could cause an
                 * operator to be evaluated before its argument is pushed onto the
-                * integer stack. */
-               /* But for binary operators, "apply" everything on the operator
+                * integer stack.
+                * But for binary operators, "apply" everything on the operator
                 * stack until we find an operator with a lesser priority than the
-                * one we have just extracted. */
-               /* Left paren is given the lowest priority so it will never be
+                * one we have just extracted. If op is right-associative,
+                * then stop "applying" on the equal priority too.
+                * Left paren is given the lowest priority so it will never be
                 * "applied" in this way.
-                * if associativity is right and priority eq, applied also skip
                 */
                prec = PREC(op);
                if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
@@ -630,41 +653,56 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
                                goto err;
                        }
                        while (stackptr != stack) {
+                               operator prev_op = *--stackptr;
                                if (op == TOK_RPAREN) {
                                        /* The algorithm employed here is simple: while we don't
                                         * hit an open paren nor the bottom of the stack, pop
                                         * tokens and apply them */
-                                       if (stackptr[-1] == TOK_LPAREN) {
-                                               --stackptr;
-                                               /* Any operator directly after a */
+                                       if (prev_op == TOK_LPAREN) {
+                                               /* Any operator directly after a
+                                                * close paren should consider itself binary */
                                                lasttok = TOK_NUM;
-                                               /* close paren should consider itself binary */
-                                               goto prologue;
+                                               goto next;
                                        }
                                } else {
-                                       operator prev_prec = PREC(stackptr[-1]);
-
-                                       convert_prec_is_assing(prec);
-                                       convert_prec_is_assing(prev_prec);
-                                       if (prev_prec < prec)
-                                               break;
-                                       /* check right assoc */
-                                       if (prev_prec == prec && is_right_associativity(prec))
+                                       operator prev_prec = PREC(prev_op);
+                                       fix_assignment_prec(prec);
+                                       fix_assignment_prec(prev_prec);
+                                       if (prev_prec < prec
+                                        || (prev_prec == prec && is_right_associative(prec))
+                                       ) {
+                                               stackptr++;
                                                break;
+                                       }
                                }
-                               errcode = arith_apply(*--stackptr, numstack, &numstackptr, math_hooks);
-                               if (errcode) goto ret;
+                               errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr);
+                               if (errmsg)
+                                       goto err_with_custom_msg;
                        }
-                       if (op == TOK_RPAREN) {
+                       if (op == TOK_RPAREN)
                                goto err;
-                       }
                }
 
-               /* Push this operator to the stack and remember it. */
+               /* Push this operator to the stack and remember it */
                *stackptr++ = lasttok = op;
- prologue:
-               ++expr;
-       } /* while */
+ next: ;
+       } /* while (1) */
+
+ err:
+       errmsg = "arithmetic syntax error";
+ err_with_custom_msg:
+       numstack->val = -1;
+ ret:
+       math_state->errmsg = errmsg;
+       return numstack->val;
+}
+
+arith_t FAST_FUNC
+arith(arith_state_t *math_state, const char *expr)
+{
+       math_state->errmsg = NULL;
+       math_state->list_of_recursed_names = NULL;
+       return evaluate_string(math_state, expr);
 }
 
 /*
index 2b0b2b8..864bee6 100644 (file)
@@ -9,67 +9,53 @@
 
 /* The math library has just one function:
  *
- *     arith_t arith(const char *expr, int *perrcode, arith_eval_hooks_t *hooks);
+ * arith_t arith(arith_state_t *state, const char *expr);
  *
- *     The first argument is the math string to parse.  All normal expansions must
- *     be done already.  i.e. no dollar symbols should be present.
+ * The expr argument is the math string to parse.  All normal expansions must
+ * be done already.  i.e. no dollar symbols should be present.
  *
- *     The second argument is a semi-detailed error description in case something
- *     goes wrong in the parsing steps.  Currently, those values are (for
- *     compatibility, you should assume all negative values are errors):
- *              0 - no errors (yay!)
- *             -1 - unspecified problem
- *             -2 - divide by zero
- *             -3 - exponent less than 0
- *             -5 - expression recursion loop detected
+ * The state argument is a pointer to a struct of hooks for your shell (see below),
+ * and an error message string (NULL if no error).
  *
- *     The third argument is a struct pointer of hooks for your shell (see below).
- *
- *     The function returns the answer to the expression.  So if you called it
- *     with the expression:
- *             "1 + 2 + 3"
- *     You would obviously get back 6.
+ * The function returns the answer to the expression.  So if you called it
+ * with the expression:
+ * "1 + 2 + 3"
+ * you would obviously get back 6.
  */
 
 /* To add support to a shell, you need to implement three functions:
  *
- *     lookupvar() - look up and return the value of a variable
+ * lookupvar() - look up and return the value of a variable
  *
- *             If the shell does:
- *                     foo=123
- *             Then the code:
- *                     const char *val = lookupvar("foo");
- *             Will result in val pointing to "123"
+ *     If the shell does:
+ *             foo=123
+ *     Then the code:
+ *             const char *val = lookupvar("foo");
+ *     will result in val pointing to "123"
  *
- *     setvar() - set a variable to some value
+ * setvar() - set a variable to some value
  *
- *             If the arithmetic expansion does something like:
- *                     $(( i = 1))
- *             Then the math code will make a call like so:
- *                     setvar("i", "1", 0);
- *             The storage for the first two parameters are not allocated, so your
- *             shell implementation will most likely need to strdup() them to save.
+ *     If the arithmetic expansion does something like:
+ *             $(( i = 1))
+ *     then the math code will make a call like so:
+ *             setvar("i", "1", 0);
+ *     The storage for the first two parameters are not allocated, so your
+ *     shell implementation will most likely need to strdup() them to save.
  *
- *     endofname() - return the end of a variable name from input
+ * endofname() - return the end of a variable name from input
  *
- *             The arithmetic code does not know about variable naming conventions.
- *             So when it is given an experession, it knows something is not numeric,
- *             but it is up to the shell to dictate what is a valid identifiers.
- *             So when it encounters something like:
- *                     $(( some_var + 123 ))
- *             It will make a call like so:
- *                     end = endofname("some_var + 123");
- *             So the shell needs to scan the input string and return a pointer to the
- *             first non-identifier string.  In this case, it should return the input
- *             pointer with an offset pointing to the first space.  The typical
- *             implementation will return the offset of first char that does not match
- *             the regex (in C locale): ^[a-zA-Z_][a-zA-Z_0-9]*
- */
-
-/* To make your life easier when dealing with optional 64bit math support,
- * rather than assume that the type is "signed long" and you can always
- * use "%ld" to scan/print the value, use the arith_t helper defines.  See
- * below for the exact things that are available.
+ *     The arithmetic code does not know about variable naming conventions.
+ *     So when it is given an experession, it knows something is not numeric,
+ *     but it is up to the shell to dictate what is a valid identifiers.
+ *     So when it encounters something like:
+ *             $(( some_var + 123 ))
+ *     It will make a call like so:
+ *             end = endofname("some_var + 123");
+ *     So the shell needs to scan the input string and return a pointer to the
+ *     first non-identifier string.  In this case, it should return the input
+ *     pointer with an offset pointing to the first space.  The typical
+ *     implementation will return the offset of first char that does not match
+ *     the regex (in C locale): ^[a-zA-Z_][a-zA-Z_0-9]*
  */
 
 #ifndef SHELL_MATH_H
@@ -79,25 +65,27 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 
 #if ENABLE_SH_MATH_SUPPORT_64
 typedef long long arith_t;
-#define arith_t_fmt "%lld"
+#define ARITH_FMT "%lld"
 #define strto_arith_t strtoull
 #else
 typedef long arith_t;
-#define arith_t_fmt "%ld"
+#define ARITH_FMT "%ld"
 #define strto_arith_t strtoul
 #endif
 
 typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name);
 typedef void        FAST_FUNC (*arith_var_set_t)(const char *name, const char *val);
-typedef char*       FAST_FUNC (*arith_var_endofname_t)(const char *name);
+//typedef const char* FAST_FUNC (*arith_var_endofname_t)(const char *name);
 
-typedef struct arith_eval_hooks {
+typedef struct arith_state_t {
+       const char           *errmsg;
        arith_var_lookup_t    lookupvar;
        arith_var_set_t       setvar;
-       arith_var_endofname_t endofname;
-} arith_eval_hooks_t;
+//     arith_var_endofname_t endofname;
+       void                 *list_of_recursed_names;
+} arith_state_t;
 
-arith_t arith(const char *expr, int *perrcode, arith_eval_hooks_t*);
+arith_t FAST_FUNC arith(arith_state_t *state, const char *expr);
 
 POP_SAVED_FUNCTION_VISIBILITY
 
index 7f5821c..853ab08 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2009 Denys Vlasenko
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 #include "random.h"
index 0856340..180c48a 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2009 Denys Vlasenko
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 #ifndef SHELL_RANDOM_H
 #define SHELL_RANDOM_H 1
index 3114ff3..5729715 100644 (file)
  * Copyright (c) 2010 Denys Vlasenko
  * Split from ash.c
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
 #include "shell_common.h"
+#include <sys/resource.h> /* getrlimit */
 
 const char defifsvar[] ALIGN1 = "IFS= \t\n";
 
@@ -36,6 +37,10 @@ int FAST_FUNC is_well_formed_var_name(const char *s, char terminator)
 
 /* read builtin */
 
+/* Needs to be interruptible: shell must handle traps and shell-special signals
+ * while inside read. To implement this, be sure to not loop on EINTR
+ * and return errno == EINTR reliably.
+ */
 //TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL"
 //string. hush naturally has it, and ash has setvareq().
 //Here we can simply store "VAR=" at buffer start and store read data directly
@@ -51,6 +56,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
        const char *opt_u
 )
 {
+       unsigned err;
        unsigned end_ms; /* -t TIMEOUT */
        int fd; /* -u FD */
        int nchars; /* -n NUM */
@@ -62,6 +68,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
        int startword;
        smallint backslash;
 
+       errno = err = 0;
+
        pp = argv;
        while (*pp) {
                if (!is_well_formed_var_name(*pp, '\0')) {
@@ -131,7 +139,13 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
                old_tty = tty;
                if (nchars) {
                        tty.c_lflag &= ~ICANON;
-                       tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
+                       // Setting it to more than 1 breaks poll():
+                       // it blocks even if there's data. !??
+                       //tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
+                       /* reads would block only if < 1 char is available */
+                       tty.c_cc[VMIN] = 1;
+                       /* no timeout (reads block forever) */
+                       tty.c_cc[VTIME] = 0;
                }
                if (read_flags & BUILTIN_READ_SILENT) {
                        tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
@@ -152,28 +166,40 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
        bufpos = 0;
        do {
                char c;
+               struct pollfd pfd[1];
+               int timeout;
 
-               if (end_ms) {
-                       int timeout;
-                       struct pollfd pfd[1];
+               if ((bufpos & 0xff) == 0)
+                       buffer = xrealloc(buffer, bufpos + 0x101);
 
-                       pfd[0].fd = fd;
-                       pfd[0].events = POLLIN;
+               timeout = -1;
+               if (end_ms) {
                        timeout = end_ms - (unsigned)monotonic_ms();
-                       if (timeout <= 0 /* already late? */
-                        || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
-                       ) { /* timed out! */
+                       if (timeout <= 0) { /* already late? */
                                retval = (const char *)(uintptr_t)1;
                                goto ret;
                        }
                }
 
-               if ((bufpos & 0xff) == 0)
-                       buffer = xrealloc(buffer, bufpos + 0x100);
-               if (nonblock_safe_read(fd, &buffer[bufpos], 1) != 1) {
+               /* We must poll even if timeout is -1:
+                * we want to be interrupted if signal arrives,
+                * regardless of SA_RESTART-ness of that signal!
+                */
+               errno = 0;
+               pfd[0].fd = fd;
+               pfd[0].events = POLLIN;
+               if (poll(pfd, 1, timeout) != 1) {
+                       /* timed out, or EINTR */
+                       err = errno;
+                       retval = (const char *)(uintptr_t)1;
+                       goto ret;
+               }
+               if (read(fd, &buffer[bufpos], 1) != 1) {
+                       err = errno;
                        retval = (const char *)(uintptr_t)1;
                        break;
                }
+
                c = buffer[bufpos];
                if (c == '\0')
                        continue;
@@ -240,6 +266,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
        free(buffer);
        if (read_flags & BUILTIN_READ_SILENT)
                tcsetattr(fd, TCSANOW, &old_tty);
+
+       errno = err;
        return retval;
 }
 
@@ -286,6 +314,12 @@ static const struct limits limits_tbl[] = {
 #ifdef RLIMIT_LOCKS
        { RLIMIT_LOCKS,         0,      'w',    "locks" },
 #endif
+#ifdef RLIMIT_NICE
+       { RLIMIT_NICE,          0,      'e',    "scheduling priority" },
+#endif
+#ifdef RLIMIT_RTPRIO
+       { RLIMIT_RTPRIO,        0,      'r',    "real-time priority" },
+#endif
 };
 
 enum {
@@ -328,6 +362,12 @@ static const char ulimit_opt_string[] = "-HSa"
 #ifdef RLIMIT_LOCKS
                        "w::"
 #endif
+#ifdef RLIMIT_NICE
+                       "e::"
+#endif
+#ifdef RLIMIT_RTPRIO
+                       "r::"
+#endif
                        ;
 
 static void printlim(unsigned opts, const struct rlimit *limit,
@@ -368,9 +408,9 @@ shell_builtin_ulimit(char **argv)
 #endif
        /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */
 
-        argc = 1;
-        while (argv[argc])
-                argc++;
+       argc = 1;
+       while (argv[argc])
+               argc++;
 
        opts = 0;
        while (1) {
@@ -422,15 +462,20 @@ shell_builtin_ulimit(char **argv)
                                                else
                                                        val = bb_strtoull(val_str, NULL, 10);
                                                if (errno) {
-                                                       bb_error_msg("bad number");
+                                                       bb_error_msg("invalid number '%s'", val_str);
                                                        return EXIT_FAILURE;
                                                }
                                                val <<= l->factor_shift;
                                        }
 //bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val);
+                                       /* from man bash: "If neither -H nor -S
+                                        * is specified, both the soft and hard
+                                        * limits are set. */
+                                       if (!opts)
+                                               opts = OPT_hard + OPT_soft;
                                        if (opts & OPT_hard)
                                                limit.rlim_max = val;
-                                       if ((opts & OPT_soft) || opts == 0)
+                                       if (opts & OPT_soft)
                                                limit.rlim_cur = val;
 //bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max);
                                        if (setrlimit(l->cmd, &limit) < 0) {
index 1e9f6a6..993ed59 100644 (file)
  * Copyright (c) 2010 Denys Vlasenko
  * Split from ash.c
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #ifndef SHELL_COMMON_H
 #define SHELL_COMMON_H 1
 
 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 
-extern const char defifsvar[]; /* "IFS= \t\n" */
+extern const char defifsvar[] ALIGN1; /* "IFS= \t\n" */
 #define defifs (defifsvar + 4)
 
 int FAST_FUNC is_well_formed_var_name(const char *s, char terminator);
index 6d574ab..fcf9930 100644 (file)
@@ -52,6 +52,13 @@ config FEATURE_SYSLOGD_DUP
          Option -D instructs syslogd to drop consecutive messages
          which are totally the same.
 
+config FEATURE_SYSLOGD_CFG
+       bool "Support syslog.conf"
+       default y
+       depends on SYSLOGD
+       help
+         Supports restricted syslogd config. See docs/syslog.conf.txt
+
 config FEATURE_SYSLOGD_READ_BUFFER_SIZE
        int "Read buffer size in bytes"
        default 256
@@ -106,6 +113,19 @@ config FEATURE_LOGREAD_REDUCED_LOCKING
          from circular buffer, minimizing semaphore
          contention at some minor memory expense.
 
+config FEATURE_KMSG_SYSLOG
+       bool "Linux kernel printk buffer support"
+       default y
+       depends on SYSLOGD
+       select PLATFORM_LINUX
+       help
+         When you enable this feature, the syslogd utility will
+         write system log message to the Linux kernel's printk buffer.
+         This can be used as a smaller alternative to the syslogd IPC
+         support, as klogd and logread aren't needed.
+
+         NOTICE: Syslog facilities in log entries needs kernel 3.5+.
+
 config KLOGD
        bool "klogd"
        default y
@@ -116,6 +136,26 @@ config KLOGD
          you wish to record the messages produced by the kernel,
          you should enable this option.
 
+comment "klogd should not be used together with syslog to kernel printk buffer"
+       depends on KLOGD && FEATURE_KMSG_SYSLOG
+
+config FEATURE_KLOGD_KLOGCTL
+       bool "Use the klogctl() interface"
+       default y
+       depends on KLOGD
+       select PLATFORM_LINUX
+       help
+         The klogd applet supports two interfaces for reading
+         kernel messages. Linux provides the klogctl() interface
+         which allows reading messages from the kernel ring buffer
+         independently from the file system.
+
+         If you answer 'N' here, klogd will use the more portable
+         approach of reading them from /proc or a device node.
+         However, this method requires the file to be available.
+
+         If in doubt, say 'Y'.
+
 config LOGGER
        bool "logger"
        default y
@@ -127,4 +167,3 @@ config LOGGER
            problems that occur within programs and scripts.
 
 endmenu
-
index faaac11..d386cc2 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
index 3eac4ad..432ded1 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>.
  * Changes: Made this a standalone busybox module which uses standalone
- *                                     syslog() client interface.
+ * syslog() client interface.
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
  *
  * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define klogd_trivial_usage
+//usage:       "[-c N] [-n]"
+//usage:#define klogd_full_usage "\n\n"
+//usage:       "Kernel logger\n"
+//usage:     "\n       -c N    Print to console messages more urgent than prio N (1-8)"
+//usage:     "\n       -n      Run in foreground"
+
 #include "libbb.h"
 #include <syslog.h>
-#include <sys/klog.h>
 
-static void klogd_signal(int sig)
+
+/* The Linux-specific klogctl(3) interface does not rely on the filesystem and
+ * allows us to change the console loglevel. Alternatively, we read the
+ * messages from _PATH_KLOG. */
+
+#if ENABLE_FEATURE_KLOGD_KLOGCTL
+
+# include <sys/klog.h>
+
+static void klogd_open(void)
+{
+       /* "Open the log. Currently a NOP" */
+       klogctl(1, NULL, 0);
+}
+
+static void klogd_setloglevel(int lvl)
+{
+       /* "printk() prints a message on the console only if it has a loglevel
+        * less than console_loglevel". Here we set console_loglevel = lvl. */
+       klogctl(8, NULL, lvl);
+}
+
+static int klogd_read(char *bufp, int len)
+{
+       return klogctl(2, bufp, len);
+}
+# define READ_ERROR "klogctl(2) error"
+
+static void klogd_close(void)
 {
        /* FYI: cmd 7 is equivalent to setting console_loglevel to 7
         * via klogctl(8, NULL, 7). */
        klogctl(7, NULL, 0); /* "7 -- Enable printk's to console" */
        klogctl(0, NULL, 0); /* "0 -- Close the log. Currently a NOP" */
-       syslog(LOG_NOTICE, "klogd: exiting");
-       kill_myself_with_sig(sig);
 }
 
+#else
+
+# include <paths.h>
+# ifndef _PATH_KLOG
+#  ifdef __GNU__
+#   define _PATH_KLOG "/dev/klog"
+#  else
+#   error "your system's _PATH_KLOG is unknown"
+#  endif
+# endif
+# define PATH_PRINTK "/proc/sys/kernel/printk"
+
+enum { klogfd = 3 };
+
+static void klogd_open(void)
+{
+       int fd = xopen(_PATH_KLOG, O_RDONLY);
+       xmove_fd(fd, klogfd);
+}
+
+static void klogd_setloglevel(int lvl)
+{
+       FILE *fp = fopen_or_warn(PATH_PRINTK, "w");
+       if (fp) {
+               /* This changes only first value:
+                * "messages with a higher priority than this
+                * [that is, with numerically lower value]
+                * will be printed to the console".
+                * The other three values in this pseudo-file aren't changed.
+                */
+               fprintf(fp, "%u\n", lvl);
+               fclose(fp);
+       }
+}
+
+static int klogd_read(char *bufp, int len)
+{
+       return read(klogfd, bufp, len);
+}
+# define READ_ERROR "read error"
+
+static void klogd_close(void)
+{
+       klogd_setloglevel(7);
+       if (ENABLE_FEATURE_CLEAN_UP)
+               close(klogfd);
+}
+
+#endif
+
 #define log_buffer bb_common_bufsiz1
 enum {
        KLOGD_LOGBUF_SIZE = sizeof(log_buffer),
@@ -38,13 +120,26 @@ enum {
        OPT_FOREGROUND = (1 << 1),
 };
 
+/* TODO: glibc openlog(LOG_KERN) reverts to LOG_USER instead,
+ * because that's how they interpret word "default"
+ * in the openlog() manpage:
+ *      LOG_USER (default)
+ *              generic user-level messages
+ * and the fact that LOG_KERN is a constant 0.
+ * glibc interprets it as "0 in openlog() call means 'use default'".
+ * I think it means "if openlog wasn't called before syslog() is called,
+ * use default".
+ * Convincing glibc maintainers otherwise is, as usual, nearly impossible.
+ * Should we open-code syslog() here to use correct facility?
+ */
+
 int klogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int klogd_main(int argc UNUSED_PARAM, char **argv)
 {
        int i = 0;
        char *opt_c;
        int opt;
-       int used = 0;
+       int used;
 
        opt = getopt32(argv, "c:n", &opt_c);
        if (opt & OPT_LEVEL) {
@@ -55,67 +150,98 @@ int klogd_main(int argc UNUSED_PARAM, char **argv)
                bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
        }
 
-       openlog("kernel", 0, LOG_KERN);
+       logmode = LOGMODE_SYSLOG;
 
-       bb_signals(BB_FATAL_SIGS, klogd_signal);
-       signal(SIGHUP, SIG_IGN);
-
-       /* "Open the log. Currently a NOP" */
-       klogctl(1, NULL, 0);
+       /* klogd_open() before openlog(), since it might use fixed fd 3,
+        * and openlog() also may use the same fd 3 if we swap them:
+        */
+       klogd_open();
+       openlog("kernel", 0, LOG_KERN);
+       /*
+        * glibc problem: for some reason, glibc changes LOG_KERN to LOG_USER
+        * above. The logic behind this is that standard
+        * http://pubs.opengroup.org/onlinepubs/9699919799/functions/syslog.html
+        * says the following about openlog and syslog:
+        * "LOG_USER
+        *  Messages generated by arbitrary processes.
+        *  This is the default facility identifier if none is specified."
+        *
+        * I believe glibc misinterpreted this text as "if openlog's
+        * third parameter is 0 (=LOG_KERN), treat it as LOG_USER".
+        * Whereas it was meant to say "if *syslog* is called with facility
+        * 0 in its 1st parameter without prior call to openlog, then perform
+        * implicit openlog(LOG_USER)".
+        *
+        * As a result of this, eh, feature, standard klogd was forced
+        * to open-code its own openlog and syslog implementation (!).
+        *
+        * Note that prohibiting openlog(LOG_KERN) on libc level does not
+        * add any security: any process can open a socket to "/dev/log"
+        * and write a string "<0>Voila, a LOG_KERN + LOG_EMERG message"
+        *
+        * Google code search tells me there is no widespread use of
+        * openlog("foo", 0, 0), thus fixing glibc won't break userspace.
+        *
+        * The bug against glibc was filed:
+        * bugzilla.redhat.com/show_bug.cgi?id=547000
+        */
 
-       /* "printk() prints a message on the console only if it has a loglevel
-        * less than console_loglevel". Here we set console_loglevel = i. */
        if (i)
-               klogctl(8, NULL, i);
+               klogd_setloglevel(i);
+
+       signal(SIGHUP, SIG_IGN);
+       /* We want klogd_read to not be restarted, thus _norestart: */
+       bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo);
 
        syslog(LOG_NOTICE, "klogd started: %s", bb_banner);
 
-       while (1) {
+       write_pidfile(CONFIG_PID_FILE_PATH "/klogd.pid");
+
+       used = 0;
+       while (!bb_got_signal) {
                int n;
                int priority;
                char *start;
 
                /* "2 -- Read from the log." */
                start = log_buffer + used;
-               n = klogctl(2, start, KLOGD_LOGBUF_SIZE-1 - used);
+               n = klogd_read(start, KLOGD_LOGBUF_SIZE-1 - used);
                if (n < 0) {
                        if (errno == EINTR)
                                continue;
-                       syslog(LOG_ERR, "klogd: error %d in klogctl(2): %m",
-                                       errno);
+                       bb_perror_msg(READ_ERROR);
                        break;
                }
                start[n] = '\0';
 
-               /* klogctl buffer parsing modelled after code in dmesg.c */
                /* Process each newline-terminated line in the buffer */
                start = log_buffer;
                while (1) {
                        char *newline = strchrnul(start, '\n');
 
                        if (*newline == '\0') {
-                               /* This line is incomplete... */
-                               if (start != log_buffer) {
-                                       /* move it to the front of the buffer */
-                                       overlapping_strcpy(log_buffer, start);
-                                       used = newline - start;
-                                       /* don't log it yet */
+                               /* This line is incomplete */
+
+                               /* move it to the front of the buffer */
+                               overlapping_strcpy(log_buffer, start);
+                               used = newline - start;
+                               if (used < KLOGD_LOGBUF_SIZE-1) {
+                                       /* buffer isn't full */
                                        break;
                                }
-                               /* ...but if buffer is full, log it anyway */
+                               /* buffer is full, log it anyway */
                                used = 0;
                                newline = NULL;
                        } else {
                                *newline++ = '\0';
                        }
 
-                       /* Extract the facility and priority */
+                       /* Extract the priority */
                        priority = LOG_INFO;
                        if (*start == '<') {
                                start++;
-
-                               priority = strtoul(start, &start, 10);
-
+                               if (*start)
+                                       priority = strtoul(start, &start, 10);
                                if (*start == '>')
                                        start++;
                        }
@@ -129,5 +255,10 @@ int klogd_main(int argc UNUSED_PARAM, char **argv)
                }
        }
 
+       klogd_close();
+       syslog(LOG_NOTICE, "klogd: exiting");
+       remove_pidfile(CONFIG_PID_FILE_PATH "/klogd.pid");
+       if (bb_got_signal)
+               kill_myself_with_sig(bb_got_signal);
        return EXIT_FAILURE;
 }
index def8330..5a70277 100644 (file)
@@ -4,9 +4,20 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define logger_trivial_usage
+//usage:       "[OPTIONS] [MESSAGE]"
+//usage:#define logger_full_usage "\n\n"
+//usage:       "Write MESSAGE (or stdin) to syslog\n"
+//usage:     "\n       -s      Log to stderr as well as the system log"
+//usage:     "\n       -t TAG  Log using the specified tag (defaults to user name)"
+//usage:     "\n       -p PRIO Priority (numeric or facility.level pair)"
+//usage:
+//usage:#define logger_example_usage
+//usage:       "$ logger \"hello\"\n"
+
 /*
  * Done in syslogd_and_logger.c:
 #include "libbb.h"
@@ -121,7 +132,7 @@ int logger_main(int argc UNUSED_PARAM, char **argv)
 
 /*-
  * Copyright (c) 1983, 1993
- *     The Regents of the University of California.  All rights reserved.
+ * The Regents of the University of California.  All rights reserved.
  *
  * This is the original license statement for the decode and pencode functions.
  *
@@ -134,8 +145,8 @@ int logger_main(int argc UNUSED_PARAM, char **argv)
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
- * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
- *             ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ * 3. BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
  *
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
index 1e8d6bc..bea73d9 100644 (file)
@@ -6,9 +6,15 @@
  *
  * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define logread_trivial_usage
+//usage:       "[-f]"
+//usage:#define logread_full_usage "\n\n"
+//usage:       "Show messages in syslogd's circular buffer\n"
+//usage:     "\n       -f      Output data as log grows"
+
 #include "libbb.h"
 #include <sys/ipc.h>
 #include <sys/sem.h>
@@ -43,13 +49,18 @@ struct globals {
        memcpy(SMrup, init_sem, sizeof(init_sem)); \
 } while (0)
 
+#if 0
 static void error_exit(const char *str) NORETURN;
 static void error_exit(const char *str)
 {
-       //release all acquired resources
+       /* Release all acquired resources */
        shmdt(shbuf);
        bb_perror_msg_and_die(str);
 }
+#else
+/* On Linux, shmdt is not mandatory on exit */
+# define error_exit(str) bb_perror_msg_and_die(str)
+#endif
 
 /*
  * sem_up - up()'s a semaphore.
@@ -60,11 +71,10 @@ static void sem_up(int semid)
                error_exit("semop[SMrup]");
 }
 
-static void interrupted(int sig UNUSED_PARAM)
+static void interrupted(int sig)
 {
-       signal(SIGINT, SIG_IGN);
-       shmdt(shbuf);
-       exit(EXIT_SUCCESS);
+       /* shmdt(shbuf); - on Linux, shmdt is not mandatory on exit */
+       kill_myself_with_sig(sig);
 }
 
 int logread_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -79,18 +89,18 @@ int logread_main(int argc UNUSED_PARAM, char **argv)
 
        log_shmid = shmget(KEY_ID, 0, 0);
        if (log_shmid == -1)
-               bb_perror_msg_and_die("can't find syslogd buffer");
+               bb_perror_msg_and_die("can't %s syslogd buffer", "find");
 
        /* Attach shared memory to our char* */
        shbuf = shmat(log_shmid, NULL, SHM_RDONLY);
        if (shbuf == NULL)
-               bb_perror_msg_and_die("can't access syslogd buffer");
+               bb_perror_msg_and_die("can't %s syslogd buffer", "access");
 
        log_semid = semget(KEY_ID, 0, 0);
        if (log_semid == -1)
                error_exit("can't get access to semaphores for syslogd buffer");
 
-       signal(SIGINT, interrupted);
+       bb_signals(BB_FATAL_SIGS, interrupted);
 
        /* Suppose atomic memory read */
        /* Max possible value for tail is shbuf->size - 1 */
@@ -116,7 +126,7 @@ int logread_main(int argc UNUSED_PARAM, char **argv)
                shbuf_data = shbuf->data; /* pointer! */
 
                if (DEBUG)
-                       printf("cur:%d tail:%i size:%i\n",
+                       printf("cur:%u tail:%u size:%u\n",
                                        cur, shbuf_tail, shbuf_size);
 
                if (!follow) {
@@ -177,9 +187,10 @@ int logread_main(int argc UNUSED_PARAM, char **argv)
                }
                free(copy);
 #endif
+               fflush_all();
        } while (follow);
 
-       shmdt(shbuf);
+       /* shmdt(shbuf); - on Linux, shmdt is not mandatory on exit */
 
        fflush_stdout_and_exit(EXIT_SUCCESS);
 }
index 20dd170..a6a4ff2 100644 (file)
  *
  * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define syslogd_trivial_usage
+//usage:       "[OPTIONS]"
+//usage:#define syslogd_full_usage "\n\n"
+//usage:       "System logging utility\n"
+//usage:       IF_NOT_FEATURE_SYSLOGD_CFG(
+//usage:       "(this version of syslogd ignores /etc/syslog.conf)\n"
+//usage:       )
+//usage:     "\n       -n              Run in foreground"
+//usage:     "\n       -O FILE         Log to FILE (default:/var/log/messages)"
+//usage:     "\n       -l N            Log only messages more urgent than prio N (1-8)"
+//usage:     "\n       -S              Smaller output"
+//usage:       IF_FEATURE_ROTATE_LOGFILE(
+//usage:     "\n       -s SIZE         Max size (KB) before rotation (default:200KB, 0=off)"
+//usage:     "\n       -b N            N rotated logs to keep (default:1, max=99, 0=purge)"
+//usage:       )
+//usage:       IF_FEATURE_REMOTE_LOG(
+//usage:     "\n       -R HOST[:PORT]  Log to HOST:PORT (default PORT:514)"
+//usage:     "\n       -L              Log locally and via network (default is network only if -R)"
+//usage:       )
+//usage:       IF_FEATURE_SYSLOGD_DUP(
+//usage:     "\n       -D              Drop duplicates"
+//usage:       )
+//usage:       IF_FEATURE_IPC_SYSLOG(
+/* NB: -Csize shouldn't have space (because size is optional) */
+//usage:     "\n       -C[size_kb]     Log to shared mem buffer (use logread to read it)"
+//usage:       )
+//usage:       IF_FEATURE_SYSLOGD_CFG(
+//usage:     "\n       -f FILE         Use FILE as config (default:/etc/syslog.conf)"
+//usage:       )
+/* //usage:  "\n       -m MIN          Minutes between MARK lines (default:20, 0=off)" */
+//usage:       IF_FEATURE_KMSG_SYSLOG(
+//usage:     "\n       -K              Log to kernel printk buffer (use dmesg to read it)"
+//usage:       )
+//usage:
+//usage:#define syslogd_example_usage
+//usage:       "$ syslogd -R masterlog:514\n"
+//usage:       "$ syslogd -R 192.168.1.1:601\n"
+
 /*
  * Done in syslogd_and_logger.c:
 #include "libbb.h"
@@ -20,6 +58,9 @@
 #define SYSLOG_NAMES_CONST
 #include <syslog.h>
 */
+#ifndef _PATH_LOG
+#define _PATH_LOG      "/dev/log"
+#endif
 
 #include <sys/un.h>
 #include <sys/uio.h>
@@ -34,7 +75,6 @@
 #include <sys/shm.h>
 #endif
 
-#include "sd-daemon.h"
 
 #define DEBUG 0
 
@@ -67,10 +107,26 @@ typedef struct {
 } remoteHost_t;
 #endif
 
+typedef struct logFile_t {
+       const char *path;
+       int fd;
+#if ENABLE_FEATURE_ROTATE_LOGFILE
+       unsigned size;
+       uint8_t isRegular;
+#endif
+} logFile_t;
+
+#if ENABLE_FEATURE_SYSLOGD_CFG
+typedef struct logRule_t {
+       uint8_t enabled_facility_priomap[LOG_NFACILITIES];
+       struct logFile_t *file;
+       struct logRule_t *next;
+} logRule_t;
+#endif
+
 /* Allows us to have smaller initializer. Ugly. */
 #define GLOBALS \
-       const char *logFilePath;                \
-       int logFD;                              \
+       logFile_t logFile;                      \
        /* interval between marks in seconds */ \
        /*int markInterval;*/                   \
        /* level of messages to be logged */    \
@@ -80,8 +136,6 @@ IF_FEATURE_ROTATE_LOGFILE( \
        unsigned logFileSize;                   \
        /* number of rotated message files */   \
        unsigned logFileRotate;                 \
-       unsigned curFileSize;                   \
-       smallint isRegular;                     \
 ) \
 IF_FEATURE_IPC_SYSLOG( \
        int shmid; /* ipc shared memory id */   \
@@ -89,6 +143,13 @@ IF_FEATURE_IPC_SYSLOG( \
        int shm_size;                           \
        struct sembuf SMwup[1];                 \
        struct sembuf SMwdn[3];                 \
+) \
+IF_FEATURE_SYSLOGD_CFG( \
+       logRule_t *log_rules; \
+) \
+IF_FEATURE_KMSG_SYSLOG( \
+       int kmsgfd; \
+       int primask; \
 )
 
 struct init_globals {
@@ -120,20 +181,22 @@ struct globals {
 };
 
 static const struct init_globals init_data = {
-       .logFilePath = "/var/log/messages",
-       .logFD = -1,
+       .logFile = {
+               .path = "/var/log/messages",
+               .fd = -1,
+       },
 #ifdef SYSLOGD_MARK
        .markInterval = 20 * 60,
 #endif
        .logLevel = 8,
 #if ENABLE_FEATURE_ROTATE_LOGFILE
-       .logFileSize = 2 * 1024 * 1024,
+       .logFileSize = 200 * 1024,
        .logFileRotate = 1,
 #endif
 #if ENABLE_FEATURE_IPC_SYSLOG
        .shmid = -1,
        .s_semid = -1,
-       .shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024), // default shm size
+       .shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024), /* default shm size */
        .SMwup = { {1, -1, IPC_NOWAIT} },
        .SMwdn = { {0, 0}, {1, 0}, {1, +1} },
 #endif
@@ -158,6 +221,8 @@ enum {
        IF_FEATURE_REMOTE_LOG(    OPTBIT_locallog   ,)  // -L
        IF_FEATURE_IPC_SYSLOG(    OPTBIT_circularlog,)  // -C
        IF_FEATURE_SYSLOGD_DUP(   OPTBIT_dup        ,)  // -D
+       IF_FEATURE_SYSLOGD_CFG(   OPTBIT_cfg        ,)  // -f
+       IF_FEATURE_KMSG_SYSLOG(   OPTBIT_kmsg       ,)  // -K
 
        OPT_mark        = 1 << OPTBIT_mark    ,
        OPT_nofork      = 1 << OPTBIT_nofork  ,
@@ -170,6 +235,9 @@ enum {
        OPT_locallog    = IF_FEATURE_REMOTE_LOG(    (1 << OPTBIT_locallog   )) + 0,
        OPT_circularlog = IF_FEATURE_IPC_SYSLOG(    (1 << OPTBIT_circularlog)) + 0,
        OPT_dup         = IF_FEATURE_SYSLOGD_DUP(   (1 << OPTBIT_dup        )) + 0,
+       OPT_cfg         = IF_FEATURE_SYSLOGD_CFG(   (1 << OPTBIT_cfg        )) + 0,
+       OPT_kmsg        = IF_FEATURE_KMSG_SYSLOG(   (1 << OPTBIT_kmsg       )) + 0,
+
 };
 #define OPTION_STR "m:nO:l:S" \
        IF_FEATURE_ROTATE_LOGFILE("s:" ) \
@@ -177,18 +245,199 @@ enum {
        IF_FEATURE_REMOTE_LOG(    "R:" ) \
        IF_FEATURE_REMOTE_LOG(    "L"  ) \
        IF_FEATURE_IPC_SYSLOG(    "C::") \
-       IF_FEATURE_SYSLOGD_DUP(   "D"  )
+       IF_FEATURE_SYSLOGD_DUP(   "D"  ) \
+       IF_FEATURE_SYSLOGD_CFG(   "f:" ) \
+       IF_FEATURE_KMSG_SYSLOG(   "K"  )
 #define OPTION_DECL *opt_m, *opt_l \
        IF_FEATURE_ROTATE_LOGFILE(,*opt_s) \
        IF_FEATURE_ROTATE_LOGFILE(,*opt_b) \
-       IF_FEATURE_IPC_SYSLOG(    ,*opt_C = NULL)
-#define OPTION_PARAM &opt_m, &G.logFilePath, &opt_l \
+       IF_FEATURE_IPC_SYSLOG(    ,*opt_C = NULL) \
+       IF_FEATURE_SYSLOGD_CFG(   ,*opt_f = NULL)
+#define OPTION_PARAM &opt_m, &(G.logFile.path), &opt_l \
        IF_FEATURE_ROTATE_LOGFILE(,&opt_s) \
        IF_FEATURE_ROTATE_LOGFILE(,&opt_b) \
-       IF_FEATURE_REMOTE_LOG(    ,&remoteAddrList) \
-       IF_FEATURE_IPC_SYSLOG(    ,&opt_C)
+       IF_FEATURE_REMOTE_LOG(    ,&remoteAddrList) \
+       IF_FEATURE_IPC_SYSLOG(    ,&opt_C) \
+       IF_FEATURE_SYSLOGD_CFG(   ,&opt_f)
 
 
+#if ENABLE_FEATURE_SYSLOGD_CFG
+static const CODE* find_by_name(char *name, const CODE* c_set)
+{
+       for (; c_set->c_name; c_set++) {
+               if (strcmp(name, c_set->c_name) == 0)
+                       return c_set;
+       }
+       return NULL;
+}
+#endif
+static const CODE* find_by_val(int val, const CODE* c_set)
+{
+       for (; c_set->c_name; c_set++) {
+               if (c_set->c_val == val)
+                       return c_set;
+       }
+       return NULL;
+}
+
+#if ENABLE_FEATURE_SYSLOGD_CFG
+static void parse_syslogdcfg(const char *file)
+{
+       char *t;
+       logRule_t **pp_rule;
+       /* tok[0] set of selectors */
+       /* tok[1] file name */
+       /* tok[2] has to be NULL */
+       char *tok[3];
+       parser_t *parser;
+
+       parser = config_open2(file ? file : "/etc/syslog.conf",
+                               file ? xfopen_for_read : fopen_for_read);
+       if (!parser)
+               /* didn't find default /etc/syslog.conf */
+               /* proceed as if we built busybox without config support */
+               return;
+
+       /* use ptr to ptr to avoid checking whether head was initialized */
+       pp_rule = &G.log_rules;
+       /* iterate through lines of config, skipping comments */
+       while (config_read(parser, tok, 3, 2, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) {
+               char *cur_selector;
+               logRule_t *cur_rule;
+
+               /* unexpected trailing token? */
+               if (tok[2])
+                       goto cfgerr;
+
+               cur_rule = *pp_rule = xzalloc(sizeof(*cur_rule));
+
+               cur_selector = tok[0];
+               /* iterate through selectors: "kern.info;kern.!err;..." */
+               do {
+                       const CODE *code;
+                       char *next_selector;
+                       uint8_t negated_prio; /* "kern.!err" */
+                       uint8_t single_prio;  /* "kern.=err" */
+                       uint32_t facmap; /* bitmap of enabled facilities */
+                       uint8_t primap;  /* bitmap of enabled priorities */
+                       unsigned i;
+
+                       next_selector = strchr(cur_selector, ';');
+                       if (next_selector)
+                               *next_selector++ = '\0';
+
+                       t = strchr(cur_selector, '.');
+                       if (!t)
+                               goto cfgerr;
+                       *t++ = '\0'; /* separate facility from priority */
+
+                       negated_prio = 0;
+                       single_prio = 0;
+                       if (*t == '!') {
+                               negated_prio = 1;
+                               ++t;
+                       }
+                       if (*t == '=') {
+                               single_prio = 1;
+                               ++t;
+                       }
+
+                       /* parse priority */
+                       if (*t == '*')
+                               primap = 0xff; /* all 8 log levels enabled */
+                       else {
+                               uint8_t priority;
+                               code = find_by_name(t, prioritynames);
+                               if (!code)
+                                       goto cfgerr;
+                               primap = 0;
+                               priority = code->c_val;
+                               if (priority == INTERNAL_NOPRI) {
+                                       /* ensure we take "enabled_facility_priomap[fac] &= 0" branch below */
+                                       negated_prio = 1;
+                               } else {
+                                       priority = 1 << priority;
+                                       do {
+                                               primap |= priority;
+                                               if (single_prio)
+                                                       break;
+                                               priority >>= 1;
+                                       } while (priority);
+                                       if (negated_prio)
+                                               primap = ~primap;
+                               }
+                       }
+
+                       /* parse facility */
+                       if (*cur_selector == '*')
+                               facmap = (1<<LOG_NFACILITIES) - 1;
+                       else {
+                               char *next_facility;
+                               facmap = 0;
+                               t = cur_selector;
+                               /* iterate through facilities: "kern,daemon.<priospec>" */
+                               do {
+                                       next_facility = strchr(t, ',');
+                                       if (next_facility)
+                                               *next_facility++ = '\0';
+                                       code = find_by_name(t, facilitynames);
+                                       if (!code)
+                                               goto cfgerr;
+                                       /* "mark" is not a real facility, skip it */
+                                       if (code->c_val != INTERNAL_MARK)
+                                               facmap |= 1<<(LOG_FAC(code->c_val));
+                                       t = next_facility;
+                               } while (t);
+                       }
+
+                       /* merge result with previous selectors */
+                       for (i = 0; i < LOG_NFACILITIES; ++i) {
+                               if (!(facmap & (1<<i)))
+                                       continue;
+                               if (negated_prio)
+                                       cur_rule->enabled_facility_priomap[i] &= primap;
+                               else
+                                       cur_rule->enabled_facility_priomap[i] |= primap;
+                       }
+
+                       cur_selector = next_selector;
+               } while (cur_selector);
+
+               /* check whether current file name was mentioned in previous rules or
+                * as global logfile (G.logFile).
+                */
+               if (strcmp(G.logFile.path, tok[1]) == 0) {
+                       cur_rule->file = &G.logFile;
+                       goto found;
+               }
+               /* temporarily use cur_rule as iterator, but *pp_rule still points
+                * to currently processing rule entry.
+                * NOTE: *pp_rule points to the current (and last in the list) rule.
+                */
+               for (cur_rule = G.log_rules; cur_rule != *pp_rule; cur_rule = cur_rule->next) {
+                       if (strcmp(cur_rule->file->path, tok[1]) == 0) {
+                               /* found - reuse the same file structure */
+                               (*pp_rule)->file = cur_rule->file;
+                               cur_rule = *pp_rule;
+                               goto found;
+                       }
+               }
+               cur_rule->file = xzalloc(sizeof(*cur_rule->file));
+               cur_rule->file->fd = -1;
+               cur_rule->file->path = xstrdup(tok[1]);
+ found:
+               pp_rule = &cur_rule->next;
+       }
+       config_close(parser);
+       return;
+
+ cfgerr:
+       bb_error_msg_and_die("error in '%s' at line %d",
+                       file ? file : "/etc/syslog.conf",
+                       parser->lineno);
+}
+#endif
+
 /* circular buffer variables/structures */
 #if ENABLE_FEATURE_IPC_SYSLOG
 
@@ -232,7 +481,7 @@ static void ipcsyslog_init(void)
        G.shbuf->size = G.shm_size - offsetof(struct shbuf_ds, data) - 1;
        /*G.shbuf->tail = 0;*/
 
-       // we'll trust the OS to set initial semval to 0 (let's hope)
+       /* we'll trust the OS to set initial semval to 0 (let's hope) */
        G.s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023);
        if (G.s_semid == -1) {
                if (errno == EEXIST) {
@@ -245,9 +494,10 @@ static void ipcsyslog_init(void)
 }
 
 /* Write message to shared mem buffer */
-static void log_to_shmem(const char *msg, int len)
+static void log_to_shmem(const char *msg)
 {
        int old_tail, new_tail;
+       int len;
 
        if (semop(G.s_semid, G.SMwdn, 3) == -1) {
                bb_perror_msg_and_die("SMwdn");
@@ -259,7 +509,7 @@ static void log_to_shmem(const char *msg, int len)
         * tail's max value is (shbuf->size - 1)
         * Last byte of buffer is never used and remains NUL.
         */
-       len++; /* length with NUL included */
+       len = strlen(msg) + 1; /* length with NUL included */
  again:
        old_tail = G.shbuf->tail;
        new_tail = old_tail + len;
@@ -284,27 +534,58 @@ static void log_to_shmem(const char *msg, int len)
                printf("tail:%d\n", G.shbuf->tail);
 }
 #else
-void ipcsyslog_cleanup(void);
-void ipcsyslog_init(void);
+static void ipcsyslog_cleanup(void) {}
+static void ipcsyslog_init(void) {}
 void log_to_shmem(const char *msg);
 #endif /* FEATURE_IPC_SYSLOG */
 
+#if ENABLE_FEATURE_KMSG_SYSLOG
+static void kmsg_init(void)
+{
+       G.kmsgfd = xopen("/dev/kmsg", O_WRONLY);
+
+       /*
+        * kernel < 3.5 expects single char printk KERN_* priority prefix,
+        * from 3.5 onwards the full syslog facility/priority format is supported
+        */
+       if (get_linux_version_code() < KERNEL_VERSION(3,5,0))
+               G.primask = LOG_PRIMASK;
+       else
+               G.primask = -1;
+}
+
+static void kmsg_cleanup(void)
+{
+       if (ENABLE_FEATURE_CLEAN_UP)
+               close(G.kmsgfd);
+}
+
+/* Write message to /dev/kmsg */
+static void log_to_kmsg(int pri, const char *msg)
+{
+       /*
+        * kernel < 3.5 expects single char printk KERN_* priority prefix,
+        * from 3.5 onwards the full syslog facility/priority format is supported
+        */
+       pri &= G.primask;
+
+       write(G.kmsgfd, G.printbuf, sprintf(G.printbuf, "<%d>%s\n", pri, msg));
+}
+#else
+static void kmsg_init(void) {}
+static void kmsg_cleanup(void) {}
+static void log_to_kmsg(int pri UNUSED_PARAM, const char *msg UNUSED_PARAM) {}
+#endif /* FEATURE_KMSG_SYSLOG */
 
 /* Print a message to the log file. */
-static void log_locally(time_t now, char *msg)
+static void log_locally(time_t now, char *msg, logFile_t *log_file)
 {
 #ifdef SYSLOGD_WRLOCK
        struct flock fl;
 #endif
        int len = strlen(msg);
 
-#if ENABLE_FEATURE_IPC_SYSLOG
-       if ((option_mask32 & OPT_circularlog) && G.shbuf) {
-               log_to_shmem(msg, len);
-               return;
-       }
-#endif
-       if (G.logFD >= 0) {
+       if (log_file->fd >= 0) {
                /* Reopen log file every second. This allows admin
                 * to delete the file and not worry about restarting us.
                 * This costs almost nothing since it happens
@@ -314,15 +595,15 @@ static void log_locally(time_t now, char *msg)
                        now = time(NULL);
                if (G.last_log_time != now) {
                        G.last_log_time = now;
-                       close(G.logFD);
+                       close(log_file->fd);
                        goto reopen;
                }
        } else {
  reopen:
-               G.logFD = open(G.logFilePath, O_WRONLY | O_CREAT
+               log_file->fd = open(log_file->path, O_WRONLY | O_CREAT
                                        | O_NOCTTY | O_APPEND | O_NONBLOCK,
                                        0666);
-               if (G.logFD < 0) {
+               if (log_file->fd < 0) {
                        /* cannot open logfile? - print to /dev/console then */
                        int fd = device_open(DEV_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK);
                        if (fd < 0)
@@ -335,9 +616,9 @@ static void log_locally(time_t now, char *msg)
 #if ENABLE_FEATURE_ROTATE_LOGFILE
                {
                        struct stat statf;
-                       G.isRegular = (fstat(G.logFD, &statf) == 0 && S_ISREG(statf.st_mode));
+                       log_file->isRegular = (fstat(log_file->fd, &statf) == 0 && S_ISREG(statf.st_mode));
                        /* bug (mostly harmless): can wrap around if file > 4gb */
-                       G.curFileSize = statf.st_size;
+                       log_file->size = statf.st_size;
                }
 #endif
        }
@@ -347,41 +628,49 @@ static void log_locally(time_t now, char *msg)
        fl.l_start = 0;
        fl.l_len = 1;
        fl.l_type = F_WRLCK;
-       fcntl(G.logFD, F_SETLKW, &fl);
+       fcntl(log_file->fd, F_SETLKW, &fl);
 #endif
 
 #if ENABLE_FEATURE_ROTATE_LOGFILE
-       if (G.logFileSize && G.isRegular && G.curFileSize > G.logFileSize) {
+       if (G.logFileSize && log_file->isRegular && log_file->size > G.logFileSize) {
                if (G.logFileRotate) { /* always 0..99 */
-                       int i = strlen(G.logFilePath) + 3 + 1;
+                       int i = strlen(log_file->path) + 3 + 1;
                        char oldFile[i];
                        char newFile[i];
                        i = G.logFileRotate - 1;
                        /* rename: f.8 -> f.9; f.7 -> f.8; ... */
                        while (1) {
-                               sprintf(newFile, "%s.%d", G.logFilePath, i);
+                               sprintf(newFile, "%s.%d", log_file->path, i);
                                if (i == 0) break;
-                               sprintf(oldFile, "%s.%d", G.logFilePath, --i);
+                               sprintf(oldFile, "%s.%d", log_file->path, --i);
                                /* ignore errors - file might be missing */
                                rename(oldFile, newFile);
                        }
                        /* newFile == "f.0" now */
-                       rename(G.logFilePath, newFile);
+                       rename(log_file->path, newFile);
+                       /* Incredibly, if F and F.0 are hardlinks, POSIX
+                        * _demands_ that rename returns 0 but does not
+                        * remove F!!!
+                        * (hardlinked F/F.0 pair was observed after
+                        * power failure during rename()).
+                        * Ensure old file is gone:
+                        */
+                       unlink(log_file->path);
 #ifdef SYSLOGD_WRLOCK
                        fl.l_type = F_UNLCK;
-                       fcntl(G.logFD, F_SETLKW, &fl);
+                       fcntl(log_file->fd, F_SETLKW, &fl);
 #endif
-                       close(G.logFD);
+                       close(log_file->fd);
                        goto reopen;
                }
-               ftruncate(G.logFD, 0);
+               ftruncate(log_file->fd, 0);
        }
-       G.curFileSize +=
+       log_file->size +=
 #endif
-                       full_write(G.logFD, msg, len);
+                       full_write(log_file->fd, msg, len);
 #ifdef SYSLOGD_WRLOCK
        fl.l_type = F_UNLCK;
-       fcntl(G.logFD, F_SETLKW, &fl);
+       fcntl(log_file->fd, F_SETLKW, &fl);
 #endif
 }
 
@@ -389,29 +678,15 @@ static void parse_fac_prio_20(int pri, char *res20)
 {
        const CODE *c_pri, *c_fac;
 
-       if (pri != 0) {
-               c_fac = facilitynames;
-               while (c_fac->c_name) {
-                       if (c_fac->c_val != (LOG_FAC(pri) << 3)) {
-                               c_fac++;
-                               continue;
-                       }
-                       /* facility is found, look for prio */
-                       c_pri = prioritynames;
-                       while (c_pri->c_name) {
-                               if (c_pri->c_val != LOG_PRI(pri)) {
-                                       c_pri++;
-                                       continue;
-                               }
-                               snprintf(res20, 20, "%s.%s",
-                                               c_fac->c_name, c_pri->c_name);
-                               return;
-                       }
-                       /* prio not found, bail out */
-                       break;
+       c_fac = find_by_val(LOG_FAC(pri) << 3, facilitynames);
+       if (c_fac) {
+               c_pri = find_by_val(LOG_PRI(pri), prioritynames);
+               if (c_pri) {
+                       snprintf(res20, 20, "%s.%s", c_fac->c_name, c_pri->c_name);
+                       return;
                }
-               snprintf(res20, 20, "<%d>", pri);
        }
+       snprintf(res20, 20, "<%d>", pri);
 }
 
 /* len parameter is used only for "is there a timestamp?" check.
@@ -436,6 +711,11 @@ static void timestamp_and_log(int pri, char *msg, int len)
        }
        timestamp[15] = '\0';
 
+       if (option_mask32 & OPT_kmsg) {
+               log_to_kmsg(pri, msg);
+               return;
+       }
+
        if (option_mask32 & OPT_small)
                sprintf(G.printbuf, "%s %s\n", timestamp, msg);
        else {
@@ -445,7 +725,32 @@ static void timestamp_and_log(int pri, char *msg, int len)
        }
 
        /* Log message locally (to file or shared mem) */
-       log_locally(now, G.printbuf);
+#if ENABLE_FEATURE_SYSLOGD_CFG
+       {
+               bool match = 0;
+               logRule_t *rule;
+               uint8_t facility = LOG_FAC(pri);
+               uint8_t prio_bit = 1 << LOG_PRI(pri);
+
+               for (rule = G.log_rules; rule; rule = rule->next) {
+                       if (rule->enabled_facility_priomap[facility] & prio_bit) {
+                               log_locally(now, G.printbuf, rule->file);
+                               match = 1;
+                       }
+               }
+               if (match)
+                       return;
+       }
+#endif
+       if (LOG_PRI(pri) < G.logLevel) {
+#if ENABLE_FEATURE_IPC_SYSLOG
+               if ((option_mask32 & OPT_circularlog) && G.shbuf) {
+                       log_to_shmem(G.printbuf);
+                       return;
+               }
+#endif
+               log_locally(now, G.printbuf, &G.logFile);
+       }
 }
 
 static void timestamp_and_log_internal(const char *msg)
@@ -490,8 +795,7 @@ static void split_escape_and_log(char *tmpbuf, int len)
                *q = '\0';
 
                /* Now log it */
-               if (LOG_PRI(pri) < G.logLevel)
-                       timestamp_and_log(pri, G.parsebuf, q - G.parsebuf);
+               timestamp_and_log(pri, G.parsebuf, q - G.parsebuf);
        }
 }
 
@@ -513,16 +817,18 @@ static NOINLINE int create_socket(void)
        int sock_fd;
        char *dev_log_name;
 
-       if (sd_listen_fds(1) == 1 && sd_is_socket_unix(SD_LISTEN_FDS_START, SOCK_DGRAM, -1, "/dev/log", 0) > 0)
+#if ENABLE_FEATURE_SYSTEMD
+       if (sd_listen_fds() == 1)
                return SD_LISTEN_FDS_START;
+#endif
 
        memset(&sunx, 0, sizeof(sunx));
        sunx.sun_family = AF_UNIX;
 
        /* Unlink old /dev/log or object it points to. */
        /* (if it exists, bind will fail) */
-       strcpy(sunx.sun_path, "/dev/log");
-       dev_log_name = xmalloc_follow_symlinks("/dev/log");
+       strcpy(sunx.sun_path, _PATH_LOG);
+       dev_log_name = xmalloc_follow_symlinks(_PATH_LOG);
        if (dev_log_name) {
                safe_strncpy(sunx.sun_path, dev_log_name, sizeof(sunx.sun_path));
                free(dev_log_name);
@@ -531,7 +837,7 @@ static NOINLINE int create_socket(void)
 
        sock_fd = xsocket(AF_UNIX, SOCK_DGRAM, 0);
        xbind(sock_fd, (struct sockaddr *) &sunx, sizeof(sunx));
-       chmod("/dev/log", 0666);
+       chmod(_PATH_LOG, 0666);
 
        return sock_fd;
 }
@@ -550,7 +856,7 @@ static int try_to_resolve_remote(remoteHost_t *rh)
                if (!rh->remoteAddr)
                        return -1;
        }
-       return socket(rh->remoteAddr->u.sa.sa_family, SOCK_DGRAM, 0);
+       return xsocket(rh->remoteAddr->u.sa.sa_family, SOCK_DGRAM, 0);
 }
 #endif
 
@@ -580,9 +886,11 @@ static void do_syslogd(void)
 #endif
        sock_fd = create_socket();
 
-       if (ENABLE_FEATURE_IPC_SYSLOG && (option_mask32 & OPT_circularlog)) {
+       if (option_mask32 & OPT_circularlog)
                ipcsyslog_init();
-       }
+
+       if (option_mask32 & OPT_kmsg)
+               kmsg_init();
 
        timestamp_and_log_internal("syslogd started: BusyBox v" BB_VER);
 
@@ -600,7 +908,7 @@ static void do_syslogd(void)
                sz = read(sock_fd, recvbuf, MAX_READ - 1);
                if (sz < 0) {
                        if (!bb_got_signal)
-                               bb_perror_msg("read from /dev/log");
+                               bb_perror_msg("read from %s", _PATH_LOG);
                        break;
                }
 
@@ -640,11 +948,25 @@ static void do_syslogd(void)
                                if (rh->remoteFD == -1)
                                        continue;
                        }
-                       /* Send message to remote logger, ignore possible error */
-                       /* TODO: on some errors, close and set G.remoteFD to -1
-                        * so that DNS resolution and connect is retried? */
-                       sendto(rh->remoteFD, recvbuf, sz+1, MSG_DONTWAIT,
-                               &(rh->remoteAddr->u.sa), rh->remoteAddr->len);
+
+                       /* Send message to remote logger.
+                        * On some errors, close and set remoteFD to -1
+                        * so that DNS resolution is retried.
+                        */
+                       if (sendto(rh->remoteFD, recvbuf, sz+1,
+                                       MSG_DONTWAIT | MSG_NOSIGNAL,
+                                       &(rh->remoteAddr->u.sa), rh->remoteAddr->len) == -1
+                       ) {
+                               switch (errno) {
+                               case ECONNRESET:
+                               case ENOTCONN: /* paranoia */
+                               case EPIPE:
+                                       close(rh->remoteFD);
+                                       rh->remoteFD = -1;
+                                       free(rh->remoteAddr);
+                                       rh->remoteAddr = NULL;
+                               }
+                       }
                }
 #endif
                if (!ENABLE_FEATURE_REMOTE_LOG || (option_mask32 & OPT_locallog)) {
@@ -655,8 +977,10 @@ static void do_syslogd(void)
 
        timestamp_and_log_internal("syslogd exiting");
        puts("syslogd exiting");
-       if (ENABLE_FEATURE_IPC_SYSLOG)
-               ipcsyslog_cleanup();
+       remove_pidfile(CONFIG_PID_FILE_PATH "/syslogd.pid");
+       ipcsyslog_cleanup();
+       if (option_mask32 & OPT_kmsg)
+               kmsg_cleanup();
        kill_myself_with_sig(bb_got_signal);
 #undef recvbuf
 }
@@ -704,10 +1028,12 @@ int syslogd_main(int argc UNUSED_PARAM, char **argv)
        if (opt_C) // -Cn
                G.shm_size = xatoul_range(opt_C, 4, INT_MAX/1024) * 1024;
 #endif
-
        /* If they have not specified remote logging, then log locally */
        if (ENABLE_FEATURE_REMOTE_LOG && !(opts & OPT_remotelog)) // -R
                option_mask32 |= OPT_locallog;
+#if ENABLE_FEATURE_SYSLOGD_CFG
+       parse_syslogdcfg(opt_f);
+#endif
 
        /* Store away localhost's name before the fork */
        G.hostname = safe_gethostname();
@@ -716,8 +1042,10 @@ int syslogd_main(int argc UNUSED_PARAM, char **argv)
        if (!(opts & OPT_nofork)) {
                bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
        }
+
        //umask(0); - why??
-       write_pidfile("/var/run/syslogd.pid");
+       write_pidfile(CONFIG_PID_FILE_PATH "/syslogd.pid");
+
        do_syslogd();
        /* return EXIT_SUCCESS; */
 }
index 51573bd..0964f23 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2008 by Denys Vlasenko <vda.linux@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #include "libbb.h"
index 071399c..7dcbbe8 100755 (executable)
@@ -2,7 +2,7 @@
 
 # Tests for the sourcecode base itself.
 # Copyright 2006 by Mike Frysinger <vapier@gentoo.org>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 [ -n "$srcdir" ] || srcdir=$(pwd)
 . ./testing.sh
index 4630d08..0a8eb9b 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 # Copyright 2010 Nokia Corporation
 # Written by Alexander Shishkin
-# Licensed under GPL v2 or later, see file LICENSE for details
+# Licensed under GPLv2 or later, see file LICENSE in this source tree.
 
 . ./testing.sh
 
index 2eeb746..2a99245 100755 (executable)
@@ -3,7 +3,7 @@
 # These are not ash tests, we use ash as a way to test lineedit!
 #
 # Copyright 2010 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 test -f "$bindir/.config" && . "$bindir/.config"
index 0691a79..132afc6 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 # Copyright 2007 by Denys Vlasenko <vda.linux@googlemail.com>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
@@ -16,20 +16,85 @@ testing "awk -F case 5" "awk -F '[#]' '{ print NF }'" "4\n" "" "#abc##zz\n"
 testing "awk -F case 6" "awk -F '[#]' '{ print NF }'" "4\n" "" "z#abc##zz\n"
 testing "awk -F case 7" "awk -F '[#]' '{ print NF }'" "5\n" "" "z##abc##zz\n"
 
+# conditions and operators
+testing "awk if operator == "  "awk 'BEGIN{if(23==23) print \"foo\"}'" "foo\n" "" ""
+testing "awk if operator != "  "awk 'BEGIN{if(23!=23) print \"bar\"}'" ""      "" ""
+testing "awk if operator >= "  "awk 'BEGIN{if(23>=23) print \"foo\"}'" "foo\n" "" ""
+testing "awk if operator < "   "awk 'BEGIN{if(2 < 13) print \"foo\"}'" "foo\n" "" ""
+testing "awk if string == "    "awk 'BEGIN{if(\"a\"==\"ab\") print \"bar\"}'" "" "" ""
+
 # 4294967295 = 0xffffffff
-testing "awk bitwise op"  "awk '{ print or(4294967295,1) }'" "4.29497e+09\n" "" "\n"
+testing "awk bitwise op"  "awk '{ print or(4294967295,1) }'" "4294967295\n" "" "\n"
+
+# we were testing for a non-empty body when deciding if a function was
+# defined or not. The testcase below caused:
+# awk: cmd. line:8: Call to undefined function
+prg='
+function empty_fun(count) {
+  # empty
+}
+END {
+  i=1
+  print "L" i "\n"
+  empty_fun(i + i + ++i)
+  print "L" i "\n"
+}'
+testing "awk handles empty function f(arg){}" \
+       "awk '$prg'" \
+       "L1\n\nL2\n\n" \
+       "" ""
+
+prg='
+function outer_fun() {
+  return 1
+}
+END {
+  i=1
+  print "L" i "\n"
+  i += outer_fun()
+  print "L" i "\n"
+}'
+testing "awk properly handles function from other scope" \
+       "awk '$prg'" \
+       "L1\n\nL2\n\n" \
+       "" ""
+
+prg='
+END {
+  i=1
+  print "L" i "\n"
+  i + trigger_error_fun()
+  print "L" i "\n"
+}'
+testing "awk properly handles undefined function" \
+       "awk '$prg' 2>&1" \
+       "L1\n\nawk: cmd. line:5: Call to undefined function\n" \
+       "" ""
+
+
 optional DESKTOP
-testing "awk hex const 1" "awk '{ print or(0xffffffff,1) }'" "4.29497e+09\n" "" "\n"
-testing "awk hex const 2" "awk '{ print or(0x80000000,1) }'" "2.14748e+09\n" "" "\n"
+testing "awk hex const 1" "awk '{ print or(0xffffffff,1) }'" "4294967295\n" "" "\n"
+testing "awk hex const 2" "awk '{ print or(0x80000000,1) }'" "2147483649\n" "" "\n"
 testing "awk oct const"   "awk '{ print or(01234,1) }'"      "669\n"         "" "\n"
 SKIP=
 
+# check that "hex/oct integer" heuristic doesn't kick in on 00NN.NNN
+testing "awk floating const with leading zeroes" \
+       "awk '{ printf \"%f %f\n\", \"000.123\", \"009.123\" }'" \
+       "0.123000 9.123000\n" \
+       "" "\n"
+
 # long field seps requiring regex
 testing "awk long field sep" "awk -F-- '{ print NF, length(\$NF), \$NF }'" \
        "2 0 \n3 0 \n4 0 \n5 0 \n" \
        "" \
        "a--\na--b--\na--b--c--\na--b--c--d--"
 
+testing "awk -F handles escapes" "awk -F'\\x21' '{print \$1}'" \
+       "a\n" \
+       "" \
+       "a!b\n"
+
 # '@(samp|code|file)\{' is an invalid extended regex (unmatched '{'),
 # but gawk 3.1.5 does not bail out on it.
 testing "awk gsub falls back to non-extended-regex" \
@@ -71,6 +136,12 @@ testing "awk string cast (bug 725)" \
 testing "awk handles whitespace before array subscript" \
        "awk 'BEGIN { arr [3] = 1; print arr [3] }'" "1\n" "" ""
 
+# GNU awk 3.1.5's "print ERRNO" prints "No such file or directory" instead of "2",
+# do we need to emulate that as well?
+testing "awk handles non-existing file correctly" \
+       "awk 'BEGIN { getline line <\"doesnt_exist\"; print ERRNO; ERRNO=0; close(\"doesnt_exist\"); print ERRNO; print \"Ok\" }'" \
+       "2\n0\nOk\n" "" ""
+
 prg='
 BEGIN {
   u["a"]=1
@@ -190,4 +261,40 @@ end d
 " \
        "" ""
 
+testing "awk handles empty ()" \
+       "awk 'BEGIN {print()}' 2>&1" "awk: cmd. line:1: Empty sequence\n" "" ""
+
+testing "awk FS assignment" "awk '{FS=\":\"; print \$1}'" \
+       "a:b\ne\n" \
+       "" \
+       "a:b c:d\ne:f g:h"
+
+optional FEATURE_AWK_LIBM
+testing "awk large integer" \
+       "awk 'BEGIN{n=(2^31)-1; print n, int(n), n%1, ++n, int(n), n%1}'" \
+       "2147483647 2147483647 0 2147483648 2147483648 0\n" \
+       "" ""
+SKIP=
+
+testing "awk length(array)" \
+       "awk 'BEGIN{ A[1]=2; A[\"qwe\"]=\"asd\"; print length(A)}'" \
+       "2\n" \
+       "" ""
+
+testing "awk -f and ARGC" \
+       "awk -f - input" \
+       "re\n2\n" \
+       "do re mi\n" \
+       '{print $2; print ARGC;}' \
+
+optional FEATURE_AWK_GNU_EXTENSIONS
+testing "awk -e and ARGC" \
+       "awk -e '{print \$2; print ARGC;}' input" \
+       "re\n2\n" \
+       "do re mi\n" \
+       ""
+SKIP=
+
+# testing "description" "command" "result" "infile" "stdin"
+
 exit $FAILCOUNT
index 38907d4..7140e99 100644 (file)
@@ -1,2 +1 @@
 test x$(basename $(pwd)) = x$(busybox basename $(pwd))
-
index 734acbb..fcfce1a 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 # Used by both gunzip and bunzip2 tests
 
+FAILCOUNT=0
+
 if test "${0##*/}" = "gunzip.tests"; then
     unpack=gunzip
     ext=gz
@@ -461,6 +463,24 @@ $ECHO -ne "\x40\xa0\x00\x8b\x12\xe8\xfb\xb7\x27\xaa\xd3\x36\x0c\xfc\xe1\x40"
 $ECHO -ne "\x01\xff\x8b\xb9\x22\x9c\x28\x48\x5f\xa5\xca\xf3\x80"
 }
 
+pbzip_4m_zeros() {
+$ECHO -ne "\x42\x5a\x68\x31\x31\x41\x59\x26\x53\x59\x63\xe3\xec\xa2\x00\x06"
+$ECHO -ne "\xe4\xc1\x00\xc0\x00\x02\x00\x00\x08\x20\x00\x30\xcc\x09\xaa\x69"
+$ECHO -ne "\x94\xa1\x36\xa9\x28\x4f\x17\x72\x45\x38\x50\x90\x63\xe3\xec\xa2"
+$ECHO -ne "\x42\x5a\x68\x31\x31\x41\x59\x26\x53\x59\x63\xe3\xec\xa2\x00\x06"
+$ECHO -ne "\xe4\xc1\x00\xc0\x00\x02\x00\x00\x08\x20\x00\x30\xcc\x09\xaa\x69"
+$ECHO -ne "\x94\xa1\x36\xa9\x28\x4f\x17\x72\x45\x38\x50\x90\x63\xe3\xec\xa2"
+$ECHO -ne "\x42\x5a\x68\x31\x31\x41\x59\x26\x53\x59\x63\xe3\xec\xa2\x00\x06"
+$ECHO -ne "\xe4\xc1\x00\xc0\x00\x02\x00\x00\x08\x20\x00\x30\xcc\x09\xaa\x69"
+$ECHO -ne "\x94\xa1\x36\xa9\x28\x4f\x17\x72\x45\x38\x50\x90\x63\xe3\xec\xa2"
+$ECHO -ne "\x42\x5a\x68\x31\x31\x41\x59\x26\x53\x59\x63\xe3\xec\xa2\x00\x06"
+$ECHO -ne "\xe4\xc1\x00\xc0\x00\x02\x00\x00\x08\x20\x00\x30\xcc\x09\xaa\x69"
+$ECHO -ne "\x94\xa1\x36\xa9\x28\x4f\x17\x72\x45\x38\x50\x90\x63\xe3\xec\xa2"
+$ECHO -ne "\x42\x5a\x68\x31\x31\x41\x59\x26\x53\x59\xc9\xb5\x21\xef\x00\x04"
+$ECHO -ne "\x8d\x40\x20\xc0\x00\x01\x00\x00\x08\x20\x00\x30\xcc\x05\x29\xa6"
+$ECHO -ne "\x4a\x11\xb1\x4a\x11\xe2\xee\x48\xa7\x0a\x12\x19\x36\xa4\x3d\xe0"
+}
+
 prep() {
     rm -f t*
     hello_$ext >t1.$ext
@@ -473,6 +493,7 @@ check() {
        echo "PASS: $1"
     else
        echo "FAIL: $1"
+       FAILCOUNT=$((FAILCOUNT + 1))
     fi
 }
 
@@ -517,8 +538,20 @@ if test "${0##*/}" = "bunzip2.tests"; then
     if test1_bz2 | ${bb}bunzip2 >/dev/null \
        && test "`test1_bz2 | ${bb}bunzip2 | md5sum`" = "61bbeee4be9c6f110a71447f584fda7b  -"
     then
-       echo "PASS: $unpack: test bz2 file"
+       echo "PASS: $unpack: test_bz2 file"
+    else
+       echo "FAIL: $unpack: test_bz2 file"
+       FAILCOUNT=$((FAILCOUNT + 1))
+    fi
+
+    if pbzip_4m_zeros | ${bb}bunzip2 >/dev/null \
+       && test "`pbzip_4m_zeros | ${bb}bunzip2 | md5sum`" = "b5cfa9d6c8febd618f91ac2843d50a1c  -"
+    then
+       echo "PASS: $unpack: pbzip_4m_zeros file"
     else
-       echo "FAIL: $unpack: test bz2 file"
+       echo "FAIL: $unpack: pbzip_4m_zeros file"
+       FAILCOUNT=$((FAILCOUNT + 1))
     fi
 fi
+
+exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255))
index 0c74053..04fea3e 100755 (executable)
@@ -2,7 +2,7 @@
 
 # Tests for busybox applet itself.
 # Copyright 2005 by Rob Landley <rob@landley.net>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
index 78d74f2..1c1fd65 100755 (executable)
@@ -1,5 +1,7 @@
 #!/bin/sh
 
+FAILCOUNT=0
+
 ext=bz2
 
 bb="busybox "
@@ -35,6 +37,7 @@ check() {
        echo "PASS: $1"
     else
        echo "FAIL: $1"
+       FAILCOUNT=$((FAILCOUNT + 1))
     fi
 }
 
@@ -47,3 +50,35 @@ prep; check "bzcat: dont delete src" "${bb}bzcat t2.bz2; test -f t2.bz2 && echo
 
 )
 rm -rf testdir
+
+
+
+# Copyright 2011 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "test name" "command" "expected result" "file input" "stdin"
+
+# "input" file is bzipped file with "a\n" data
+testing "bzcat can print many files" \
+"$ECHO -ne '$hexdump' | bzcat input input; echo \$?" \
+"\
+a
+a
+0
+" "\
+\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x63\x3e\xd6\xe2\x00\x00\
+\x00\xc1\x00\x00\x10\x20\x00\x20\x00\x21\x00\x82\xb1\x77\x24\x53\
+\x85\x09\x06\x33\xed\x6e\x20\
+" ""
+
+# "input" file is bzipped zero byte file
+testing "bzcat can handle compressed zero-length bzip2 files" \
+"$ECHO -ne '$hexdump' | bzcat input input; echo \$?" \
+"0\n" \
+"\x42\x5a\x68\x39\x17\x72\x45\x38\x50\x90\x00\x00\x00\x00" ""
+
+
+
+exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255))
index db693ee..5509cf0 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright 2010 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 test -f "$bindir/.config" && . "$bindir/.config"
index 753db21..2f48168 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 # Copyright 2008 by Denys Vlasenko <vda.linux@googlemail.com>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
index e81939d..33cd876 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright 2010 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
index 7aee774..4cd441a 100755 (executable)
@@ -1,9 +1,11 @@
 #!/bin/sh
 # Copyright 2008 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
+umask 022
+
 # ls -ln shows date. Need to remove that, it's variable.
 # sed: coalesce spaces
 # cut: remove date
@@ -114,6 +116,16 @@ link
 " "" ""
 SKIP=
 
+# avoid 'not created: newer or same age file exists' message for directories
+rm -rf cpio.testdir cpio.testdir2 2>/dev/null
+mkdir cpio.testdir
+testing "cpio extracts in existing directory" \
+"$ECHO -ne '$hexdump' | bzcat | cpio -id 2>&1; echo \$?" \
+"\
+1 blocks
+0
+" "" ""
+SKIP=
 
 # Clean up
 rm -rf cpio.testdir cpio.testdir2 2>/dev/null
index 0fd79f2..1103402 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 # Copyright 2007 by Denys Vlasenko <vda.linux@googlemail.com>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
diff --git a/testsuite/date/date-@-works b/testsuite/date/date-@-works
new file mode 100644 (file)
index 0000000..03b4c7f
--- /dev/null
@@ -0,0 +1,13 @@
+# Tests for time_t value (unix time format)
+
+# Just before DST switched off
+test x"Sun Oct 31 03:59:59 EEST 2010" = x"`TZ=EET-2EEST,M3.5.0/3,M10.5.0/4 busybox date -d @1288486799`"
+
+# Just after DST switched off
+test x"Sun Oct 31 03:00:01 EET 2010" = x"`TZ=EET-2EEST,M3.5.0/3,M10.5.0/4 busybox date -d @1288486801`"
+
+# Just before DST switched on
+test x"Sun Mar 28 02:59:59 EET 2010" = x"`TZ=EET-2EEST,M3.5.0/3,M10.5.0/4 busybox date -d @1269737999`"
+
+# Just after DST switched on
+test x"Sun Mar 28 04:00:01 EEST 2010" = x"`TZ=EET-2EEST,M3.5.0/3,M10.5.0/4 busybox date -d @1269738001`"
index d056344..81378cc 100644 (file)
@@ -1,8 +1,13 @@
-dt1="`date -R`"
-# Wait for the start of next second
-dt="$dt1"
-while test x"$dt" = x"$dt1"; do
-    dt="`date -R`"
-done
+# When different date's use time() and clock_gettime(),
+# seconds transition may not happen at _exactly_ the same moment.
+# Therefore we try it several times.
 
-test x"$dt" = x"`busybox date -R`"
+test x"`date -R`" = x"`busybox date -R`" && exit 0 || true
+test x"`date -R`" = x"`busybox date -R`" && exit 0 || true
+test x"`date -R`" = x"`busybox date -R`" && exit 0 || true
+test x"`date -R`" = x"`busybox date -R`" && exit 0 || true
+test x"`date -R`" = x"`busybox date -R`" && exit 0 || true
+test x"`date -R`" = x"`busybox date -R`" && exit 0 || true
+test x"`date -R`" = x"`busybox date -R`" && exit 0 || true
+test x"`date -R`" = x"`busybox date -R`" && exit 0 || true
+false
index 901c485..35c2468 100644 (file)
@@ -31,9 +31,11 @@ dt=`busybox date -d '1999-1-2 3:4:5'`
 dt=`echo "$dt" | cut -b1-19`
 test x"$dt" = x"Sat Jan  2 03:04:05"
 
-dt=`busybox date -d 01231133`
-dt=`echo "$dt" | cut -b5-19`
-test x"$dt" = x"Jan 23 11:33:00"
+# date (GNU coreutils) 8.17 doesn't accept 01231133 either:
+# date: invalid date '01231133'
+#dt=`busybox date -d 01231133`
+#dt=`echo "$dt" | cut -b5-19`
+#test x"$dt" = x"Jan 23 11:33:00"
 
 dt=`busybox date -d 200001231133`
 dt=`echo "$dt" | cut -b1-19`
index e745d38..4f53939 100644 (file)
@@ -1,4 +1,7 @@
 unset LANG
+unset LANGUAGE
+unset LC_TIME
+unset LC_ALL
 
 dt=`busybox date -d 1:2 +%T`
 test x"$dt" = x"01:02:00"
@@ -38,9 +41,9 @@ test x"$hdt" = x"$dt"
 # Avoiding using week day in this evaluation, as it's mostly different every year
 # date (GNU coreutils) 6.10 reports:
 #      date: invalid date '01231133'
-dt=`busybox date -d 01231133 +%c`
-dt=`echo "$dt" | cut -b5-19`
-test x"$dt" = x"Jan 23 11:33:00"
+#dt=`busybox date -d 01231133 +%c`
+#dt=`echo "$dt" | cut -b5-19`
+#test x"$dt" = x"Jan 23 11:33:00"
 
 # date (GNU coreutils) 6.10 reports:
 #      date: invalid date '012311332000'
index 27a4b33..6de4648 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright 2008 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
index f339c8f..d673dd9 100644 (file)
@@ -1,2 +1 @@
 test x$(dirname $(pwd)) = x$(busybox dirname $(pwd))
-
index c18433c..1c77b65 100644 (file)
@@ -1,4 +1,4 @@
 # FEATURE: CONFIG_FEATURE_HUMAN_READABLE
 
 dd if=/dev/zero of=file bs=1M count=1 2>/dev/null
-test x"`busybox du -h .`" = x"1.0M     ."
+test x"`busybox du -h file`" = x"1.0M  file"
index a522649..213e9ba 100644 (file)
@@ -1,4 +1,13 @@
+mkdir du.testdir
+cd du.testdir
 dd if=/dev/zero of=file1 bs=1k count=64 2>/dev/null
 dd if=/dev/zero of=file2 bs=1k count=16 2>/dev/null
+# ext4 on images <512M gives 81kb
+# ext3 on images <512M gives 83kb
+# a bsd system reportedly gives 82kb
 test x"`busybox du -k .`" = x"80       ." \
-  -o x"`busybox du -k .`" = x"88       ." \
+  -o x"`busybox du -k .`" = x"81       ." \
+  -o x"`busybox du -k .`" = x"82       ." \
+  -o x"`busybox du -k .`" = x"83       ." \
+  -o x"`busybox du -k .`" = x"84       ." \
+  -o x"`busybox du -k .`" = x"88       ."
index 6b150e0..af87345 100644 (file)
@@ -1,8 +1,12 @@
 # FEATURE: CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
 
+mkdir du.testdir
+cd du.testdir
 dd if=/dev/zero of=file1 bs=1k count=64 2>/dev/null
 ln file1 file1.1
 dd if=/dev/zero of=file2 bs=1k count=16 2>/dev/null
 test x"`busybox du -l .`" = x"144      ." \
+  -o x"`busybox du -l .`" = x"146      ." \
   -o x"`busybox du -l .`" = x"148      ." \
   -o x"`busybox du -l .`" = x"152      ." \
+  -o x"`busybox du -l .`" = x"156      ."
diff --git a/testsuite/echo/echo-prints-dash b/testsuite/echo/echo-prints-dash
new file mode 100644 (file)
index 0000000..ddcdbad
--- /dev/null
@@ -0,0 +1 @@
+test "`busybox echo - | od -t x1 | head -n 1`" = "0000000 2d 0a"
diff --git a/testsuite/echo/echo-prints-non-opts b/testsuite/echo/echo-prints-non-opts
new file mode 100644 (file)
index 0000000..c7d1e20
--- /dev/null
@@ -0,0 +1 @@
+test "`busybox echo -neEZ | od -t x1 | head -n 1`" = "0000000 2d 6e 65 45 5a 0a"
diff --git a/testsuite/echo/echo-prints-slash_00041 b/testsuite/echo/echo-prints-slash_00041
new file mode 100644 (file)
index 0000000..9cffabd
--- /dev/null
@@ -0,0 +1,3 @@
+# FEATURE: CONFIG_FEATURE_FANCY_ECHO
+
+test "`busybox echo -ne '\00041z' | od -t x1 | head -n 1`" = "0000000 04 31 7a"
diff --git a/testsuite/echo/echo-prints-slash_0041 b/testsuite/echo/echo-prints-slash_0041
new file mode 100644 (file)
index 0000000..b07429d
--- /dev/null
@@ -0,0 +1,3 @@
+# FEATURE: CONFIG_FEATURE_FANCY_ECHO
+
+test "`busybox echo -ne '\0041z' | od -t x1 | head -n 1`" = "0000000 21 7a"
diff --git a/testsuite/echo/echo-prints-slash_041 b/testsuite/echo/echo-prints-slash_041
new file mode 100644 (file)
index 0000000..1d70cec
--- /dev/null
@@ -0,0 +1,3 @@
+# FEATURE: CONFIG_FEATURE_FANCY_ECHO
+
+test "`busybox echo -ne '\041z' | od -t x1 | head -n 1`" = "0000000 21 7a"
diff --git a/testsuite/echo/echo-prints-slash_41 b/testsuite/echo/echo-prints-slash_41
new file mode 100644 (file)
index 0000000..6d8999b
--- /dev/null
@@ -0,0 +1,3 @@
+# FEATURE: CONFIG_FEATURE_FANCY_ECHO
+
+test "`busybox echo -ne '\41z' | od -t x1 | head -n 1`" = "0000000 21 7a"
index 631ab4d..0682c29 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright 2008 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 test -f "$bindir/.config" && . "$bindir/.config"
index af49ac4..5a0fffb 100644 (file)
@@ -56,4 +56,3 @@ busybox expr 0 \>= 1
 if [ $? != 1 ] ; then
        exit 1;
 fi;
-
index e5700cc..ecf8b9c 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright 2009 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 test -f "$bindir/.config" && . "$bindir/.config"
index d4bf80d..412efff 100755 (executable)
@@ -1,13 +1,13 @@
 #!/bin/sh
 
 # Copyright 2005 by Rob Landley <rob@landley.net>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 # AUDIT:
 
 . ./testing.sh
 
-# testing "test name" "options" "expected result" "file input" "stdin"
+# testing "test name" "commands" "expected result" "file input" "stdin"
 #   file input will be file called "input"
 #   test can create a file "actual" instead of writing to stdout
 
@@ -82,6 +82,20 @@ testing "grep -F handles -i" "grep -F -i foo input ; echo \$?" \
 testing "grep can read regexps from stdin" "grep -f - input ; echo \$?" \
        "two\nthree\n0\n" "tw\ntwo\nthree\n" "tw.\nthr\n"
 
+# -x (whole line match)
+testing "grep -x (full match)" "grep -x foo input ; echo \$?" \
+       "foo\n0\n" "foo\n" ""
+testing "grep -x (partial match 1)" "grep -x foo input ; echo \$?" \
+       "1\n" "foo bar\n" ""
+testing "grep -x (partial match 2)" "grep -x foo input ; echo \$?" \
+       "1\n" "bar foo\n" ""
+testing "grep -x -F (full match)" "grep -x -F foo input ; echo \$?" \
+       "foo\n0\n" "foo\n" ""
+testing "grep -x -F (partial match 1)" "grep -x -F foo input ; echo \$?" \
+       "1\n" "foo bar\n" ""
+testing "grep -x -F (partial match 2)" "grep -x -F foo input ; echo \$?" \
+       "1\n" "bar foo\n" ""
+
 optional FEATURE_GREP_EGREP_ALIAS
 testing "grep -E supports extended regexps" "grep -E fo+" "foo\n" "" \
        "b\ar\nfoo\nbaz"
@@ -98,5 +112,55 @@ testing "grep -o does not loop forever" \
        'grep -o "[^/]*$"' \
        "test\n" \
        "" "/var/test\n"
+testing "grep -o does not loop forever on zero-length match" \
+       'grep -o "" | head -n1' \
+       "" \
+       "" "test\n"
+
+testing "grep -f EMPTY_FILE" \
+       "grep -f input" \
+       "" \
+       "" \
+       "test\n"
+
+testing "grep -v -f EMPTY_FILE" \
+       "grep -v -f input" \
+       "test\n" \
+       "" \
+       "test\n"
+
+testing "grep -Fw matches only words" \
+       "grep -Fw foo input" \
+       "" \
+       "foop\n" \
+       ""
+
+testing "grep -Fw doesn't stop on 1st mismatch" \
+       "grep -Fw foo input" \
+       "foop foo\n" \
+       "foop foo\n" \
+       ""
+
+testing "grep -w doesn't stop on 1st mismatch" \
+       "grep -w foo input" \
+       "foop foo\n" \
+       "foop foo\n" \
+       ""
+
+testing "grep -w ^str doesn't match str not at the beginning" \
+       "grep -w ^str input" \
+       "" \
+       "strstr\n" \
+       ""
+
+testing "grep -w ^ doesn't hang" \
+       "grep -w ^ input" \
+       "" \
+       "anything\n" \
+       ""
+
+# testing "test name" "commands" "expected result" "file input" "stdin"
+#   file input will be file called "input"
+#   test can create a file "actual" instead of writing to stdout
 
 exit $FAILCOUNT
index d781004..68c0755 100755 (executable)
@@ -1,3 +1,3 @@
 #!/bin/sh
 
-. bunzip2.tests
+. ./bunzip2.tests
index bcfd717..8c20bdf 100644 (file)
@@ -1,8 +1,6 @@
 h=x$(busybox hostid)
 # Is $h a sequence of hex numbers?
-x="${h//[0123456789abcdef]/x}"
-x="${x//xxx/x}"
-x="${x//xxx/x}"
-x="${x//xxx/x}"
-x="${x//xx/x}"
-test x"$x" = x"x"
+case "$h" in
+ x*[!0-9a-f]*) false;;
+ *) true;;
+esac
index e062242..54c0aac 100644 (file)
@@ -1,3 +1,3 @@
-f=$(busybox hostname -f)
+f=$(busybox hostname -f).
 d=$(busybox hostname -d)
-test x"${f#*.}" = x"$d"
+test x"${f#*.}" = x"$d${d:+.}"
index a8123ec..3a49bed 100644 (file)
@@ -6,4 +6,3 @@ if [ $? != 0 ] ; then
        exit 0;
 fi
 exit 1;
-
index dc84212..9309d36 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright 2010 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 test -f "$bindir/.config" && . "$bindir/.config"
@@ -251,6 +251,17 @@ test x"$CONFIG_UNICODE_SUPPORT" = x"y" \
 0110_5.3.2__U+FFFF_=_ef_bf_bf_=_"?"_______________________________________|
 ' "" ""
 
+rm -rf ls.testdir 2>/dev/null
+mkdir ls.testdir || exit 1
+
+# testing "test name" "command" "expected result" "file input" "stdin"
+
+test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \
+&& testing "ls symlink_to_dir" \
+"touch ls.testdir/A ls.testdir/B; ln -s ls.testdir ls.link; ls ls.link; ls -1 ls.link/; ls -1 ls.link; rm -f ls.link" \
+"A\nB\nA\nB\nA\nB\n" \
+"" ""
+
 # Clean up
 rm -rf ls.testdir 2>/dev/null
 
index f69b4a6..fd12460 100755 (executable)
@@ -1,9 +1,18 @@
 #!/bin/sh
 # Copyright 2008 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
+test x"`id -u`" = x"0" || {
+       echo "SKIPPED: makedevs (must be root to test this)"
+       exit 0
+}
+
+unset LANG
+unset LC_COLLATE
+unset LC_ALL
+
 # ls -ln is showing date. Need to remove that, it's variable
 # sed: (1) "maj, min" -> "maj,min" (2) coalesce spaces
 # cut: remove date
diff --git a/testsuite/md5sum.tests b/testsuite/md5sum.tests
new file mode 100755 (executable)
index 0000000..6c75b6d
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/sh
+# Used by {ms5,shaN}sum
+
+# We pipe texts 0...999 bytes long, {md5,shaN}sum them,
+# then {md5,shaN}sum the resulting list.
+# Then we compare the result with expected result.
+#
+# Here are the expected results:
+# efe30c482e0b687e0cca0612f42ca29b
+# d41337e834377140ae7f98460d71d908598ef04f
+# 8e1d3ed57ebc130f0f72508446559eeae06451ae6d61b1e8ce46370cfb8963c3
+# fe413e0f177324d1353893ca0772ceba83fd41512ba63895a0eebb703ef9feac2fb4e92b2cb430b3bda41b46b0cb4ea8307190a5cc795157cfb680a9cd635d0f
+
+if ! test "$1"; then
+       set -- md5sum efe30c482e0b687e0cca0612f42ca29b
+fi
+
+sum="$1"
+expected="$2"
+
+test -f "$bindir/.config" && . "$bindir/.config"
+
+test x"$CONFIG_FEATURE_FANCY_HEAD" != x"y" \
+&& { echo "SKIPPED: $sum"; exit 0; }
+
+text="The quick brown fox jumps over the lazy dog"
+text=`yes "$text" | head -c 9999`
+
+result=`(
+n=0
+while test $n -le 999; do
+       echo "$text" | head -c $n | "$sum"
+       n=$(($n+1))
+done | "$sum"
+)`
+
+if test x"$result" = x"$expected  -"; then
+       echo "PASS: $sum"
+       exit 0
+fi
+
+echo "FAIL: $sum (r:$result exp:$expected)"
+exit 1
index c375fc7..48d3dcc 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright 2008 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
@@ -11,7 +11,7 @@ FILTER_LS="grep -v '^total ' | sed -e 's/,  */,/g' -e 's/  */ /g' | cut -d' ' -f
 # cut: remove size+date
 FILTER_LS2="grep -v '^total ' | sed -e 's/,  */,/g' -e 's/  */ /g' | cut -d' ' -f 1-4,9-"
 
-# testing "test name" "options" "expected result" "file input" "stdin"
+# testing "test name" "commands" "expected result" "file input" "stdin"
 
 rm -rf mdev.testdir
 mkdir mdev.testdir
@@ -38,6 +38,16 @@ brw-rw---- 1 0 0 8,0 sda
 SKIP=
 
 # continuing to use directory structure from prev test
+optional STATIC FEATURE_MDEV_CONF FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME
+testing "mdev deletes /block/sda" \
+       "env - PATH=$PATH ACTION=remove DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1;
+       ls -ln mdev.testdir/dev | $FILTER_LS" \
+"\
+" \
+       "" ""
+SKIP=
+
+# continuing to use directory structure from prev test
 rm -rf mdev.testdir/dev/*
 echo ".* 1:1 666" >mdev.testdir/etc/mdev.conf
 echo "sda 2:2 444" >>mdev.testdir/etc/mdev.conf
@@ -118,6 +128,26 @@ SKIP=
 
 # continuing to use directory structure from prev test
 rm -rf mdev.testdir/dev/*
+echo "sda 0:0 444 =disk/sd/a" >mdev.testdir/etc/mdev.conf
+optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_RENAME FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME
+testing "mdev move rule '=bar/baz/fname'" \
+       "env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1;
+       ls -lnR mdev.testdir/dev | $FILTER_LS2" \
+"\
+mdev.testdir/dev:
+drwxr-xr-x 3 0 0 disk
+
+mdev.testdir/dev/disk:
+drwxr-xr-x 2 0 0 sd
+
+mdev.testdir/dev/disk/sd:
+br--r--r-- 1 0 0 a
+" \
+       "" ""
+SKIP=
+
+# continuing to use directory structure from prev test
+rm -rf mdev.testdir/dev/*
 # here we complicate things by having non-matching group 1 and using %0
 echo "s([0-9])*d([a-z]+) 0:0 644 >sd/%2_%0" >mdev.testdir/etc/mdev.conf
 optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_RENAME FEATURE_MDEV_RENAME_REGEXP FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME FEATURE_LS_SORTFILES
index 7cad7da..324eaaf 100755 (executable)
@@ -2,12 +2,20 @@
 
 # mkfs.minix tests.
 # Copyright 2007 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
 # testing "test name" "options" "expected result" "file input" "stdin"
 
+# '\n' produces 10 on little endian, but not on big endian
+cr=`echo | od -i | sed 's/ *$//g;s/.* //g;2d'`
+if [ x"$cr" = x"10" ]; then
+       hash=4f35f7afeba07d56055bed1f29ae20b7
+else
+       hash=5adbc1b3ccd20ca5d0ab5bc1e13ac3fc
+fi
+
 testing "mkfs.minix" \
        "dd if=/dev/zero of=input bs=1k count=1024 2>/dev/null; mkfs.minix input; md5sum <input" \
 "352 inodes\n"\
@@ -15,7 +23,7 @@ testing "mkfs.minix" \
 "Firstdatazone=15 (15)\n"\
 "Zonesize=1024\n"\
 "Maxsize=268966912\n"\
-"4f35f7afeba07d56055bed1f29ae20b7  -\n" \
+"$hash  -\n" \
        "" \
        ""
 
index e18d046..4f34c57 100755 (executable)
@@ -2,7 +2,7 @@
 
 # SUSv3 compliant mount and umount tests.
 # Copyright 2005 by Rob Landley <rob@landley.net>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 if [ -z "$TESTDIR" ]
 then
index ce1a600..a0bc508 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright 2007 by Denys Vlasenko <vda.linux@googlemail.com>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 test -f "$bindir/.config" && . "$bindir/.config"
@@ -20,7 +20,7 @@ if test x"$CONFIG_MKFS_MINIX" != x"y" \
        exit 0
 fi
 
-testdir=$PWD/mount.testdir
+testdir="$PWD/mount.testdir"
 
 dd if=/dev/zero of=mount.image1m count=1 bs=1M 2>/dev/null || { echo "dd error"; exit 1; }
 mkfs.minix -v mount.image1m >/dev/null 2>&1 || { echo "mkfs.minix error"; exit 1; }
@@ -36,7 +36,8 @@ testing "mount -o remount,mand" \
 "mount -o loop mount.image1m $testdir "\
 "&& grep -Fc $testdir </proc/mounts "\
 "&& mount -o remount,mand $testdir "\
-"&& grep -F $testdir </proc/mounts | grep -c '[, ]mand[, ]'" \
+"&& grep -F $testdir </proc/mounts | grep -c '[, ]mand[, ]'"\
+"|| grep -F $testdir </proc/mounts" \
        "1\n""1\n" \
        "" ""
 
@@ -44,4 +45,67 @@ umount -d "$testdir"
 rmdir "$testdir"
 rm mount.image1m
 
+
+# Bug: mount.shared1 directory shows no files (has to show files a and b)
+optional FEATURE_LS_RECURSIVE FEATURE_LS_SORTFILES
+testing "mount bind+rshared" "\
+mkdir -p mount.dir mount.shared1 mount.shared2
+touch mount.dir/a mount.dir/b
+
+mount --bind         mount.shared1 mount.shared1 2>&1
+mount --make-rshared mount.shared1               2>&1
+mount --bind         mount.shared2 mount.shared2 2>&1
+mount --make-rshared mount.shared2               2>&1
+
+mount --bind mount.shared2 mount.shared1         2>&1
+mount --bind mount.dir     mount.shared2         2>&1
+
+ls -R mount.dir mount.shared1 mount.shared2      2>&1
+
+umount mount.dir mount.shared1 mount.shared2 2>/dev/null
+umount mount.dir mount.shared1 mount.shared2 2>/dev/null
+umount mount.dir mount.shared1 mount.shared2 2>/dev/null
+rm -f mount.dir/a mount.dir/b mount.dir/c
+rmdir mount.dir mount.shared1 mount.shared2
+" \
+"\
+mount.dir:
+a
+b
+
+mount.shared1:
+a
+b
+
+mount.shared2:
+a
+b
+" \
+       "" ""
+SKIP=
+
+
+testing "mount RO loop" "\
+exec 2>&1
+umount -d mount.dir 2>/dev/null
+rmdir mount.dir     2>/dev/null
+mkdir -p mount.dir
+(
+cd mount.dir                               || { echo 'cd error'; exit 1; }
+mkdir z1 z2                                || { echo 'mkdir error'; exit 1; }
+mount -t tmpfs tmpfs z1                    || { echo 'mount tmpfs error'; exit 1; }
+dd if=/dev/zero of=z1/e2img count=10 bs=1M 2>/dev/null || { echo 'dd error'; exit 1; }
+mke2fs -F z1/e2img                         2>/dev/null >&2 || { echo 'mke2fs error'; exit 1; }
+mount -r -o loop -t ext2 z1/e2img z2       || { echo 'mount -r -o loop error'; exit 1; }
+mount -o remount,ro z1                     || { echo 'mount -o remount,ro error'; exit 1; }
+)
+umount -d mount.dir/z2
+##losetup -d /dev/loop*
+umount -d mount.dir/z1
+rm -rf mount.dir
+echo DONE
+" \
+"DONE\n" "" ""
+
+
 exit $FAILCOUNT
index a2d015a..7a9da3e 100755 (executable)
@@ -1,10 +1,10 @@
 #!/bin/sh
 # Copyright 2008 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
-# testing "test name" "options" "expected result" "file input" "stdin"
+# testing "test name" "commands" "expected result" "file input" "stdin"
 
 optional DESKTOP
 testing "od -b" \
@@ -16,4 +16,24 @@ testing "od -b" \
        "" "HELLO"
 SKIP=
 
+optional DESKTOP LONG_OPTS
+testing "od -b --traditional" \
+       "od -b --traditional" \
+"\
+0000000 110 105 114 114 117
+0000005
+" \
+       "" "HELLO"
+SKIP=
+
+optional DESKTOP LONG_OPTS
+testing "od -b --traditional FILE" \
+       "od -b --traditional input" \
+"\
+0000000 110 105 114 114 117
+0000005
+" \
+       "HELLO" ""
+SKIP=
+
 exit $FAILCOUNT
index 999efef..904e1a1 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 # Copyright 2008 by Denys Vlasenko <vda.linux@googlemail.com>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
@@ -96,6 +96,7 @@ cat >$FILE.res <<EOF
 [option][dns][129.219.13.81]
 [option][domain][local]
 [option][lease][864000]
+[option][0x08][01020304]
 EOF
 
 testing "parse udhcpd.conf from examples" \
index 749d936..2759d2a 100755 (executable)
@@ -1,13 +1,13 @@
 #!/bin/sh
 # Copyright 2008 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
-# testing "test name" "options" "expected result" "file input" "stdin"
+# testing "test name" "command(s)" "expected result" "file input" "stdin"
 
 testing "patch with old_file == new_file" \
-       'patch; echo $?; cat input' \
+       'patch 2>&1; echo $?; cat input' \
 "\
 patching file input
 0
@@ -29,7 +29,7 @@ zxc
 " \
 
 testing "patch with nonexistent old_file" \
-       'patch; echo $?; cat input' \
+       'patch 2>&1; echo $?; cat input' \
 "\
 patching file input
 0
@@ -51,7 +51,7 @@ zxc
 " \
 
 testing "patch -R with nonexistent old_file" \
-       'patch -R; echo $?; cat input' \
+       'patch -R 2>&1; echo $?; cat input' \
 "\
 patching file input
 0
@@ -75,9 +75,12 @@ zxc
 testing "patch detects already applied hunk" \
        'patch 2>&1; echo $?; cat input' \
 "\
+Possibly reversed hunk 1 at 4
+Hunk 1 FAILED 1/1.
+ abc
++def
+ 123
 patching file input
-patch: hunk #1 FAILED at 1
-patch: 1 out of 1 hunk FAILED
 1
 abc
 def
@@ -97,13 +100,15 @@ def
  123
 " \
 
-# Currently fails (erroneously appends second "456" line):
-false && testing "patch detects already applied hunk" \
+testing "patch detects already applied hunk at the EOF" \
        'patch 2>&1; echo $?; cat input' \
 "\
+Possibly reversed hunk 1 at 4
+Hunk 1 FAILED 1/1.
+ abc
+ 123
++456
 patching file input
-patch: hunk #1 FAILED at 2
-patch: 1 out of 1 hunk FAILED
 1
 abc
 123
@@ -123,6 +128,120 @@ abc
 +456
 " \
 
+# testing "test name" "command(s)" "expected result" "file input" "stdin"
+testing "patch -N ignores already applied hunk" \
+       'patch -N 2>&1; echo $?; cat input' \
+"\
+patching file input
+0
+abc
+def
+123
+" \
+"\
+abc
+def
+123
+" \
+"\
+--- input
++++ input
+@@ -1,2 +1,3 @@
+ abc
++def
+ 123
+" \
+
+# testing "test name" "command(s)" "expected result" "file input" "stdin"
+testing "patch FILE PATCH" \
+       'cat >a.patch; patch input a.patch 2>&1; echo $?; cat input; rm a.patch' \
+"\
+patching file input
+0
+abc
+def
+123
+" \
+"\
+abc
+123
+" \
+"\
+--- foo.old
++++ foo
+@@ -1,2 +1,3 @@
+ abc
++def
+ 123
+" \
+
+# testing "test name" "command(s)" "expected result" "file input" "stdin"
+testing "patch at the beginning" \
+       'patch 2>&1; cat input' \
+"\
+patching file input
+111changed
+444
+555
+666
+777
+888
+999
+" \
+"\
+111
+222
+333
+444
+555
+666
+777
+888
+999
+" \
+"\
+--- input
++++ input
+@@ -1,6 +1,4 @@
+-111
+-222
+-333
++111changed
+ 444
+ 555
+ 666
+" \
+
+# testing "test name" "command(s)" "expected result" "file input" "stdin"
+testing "patch creates new file" \
+       'patch 2>&1; echo $?; cat testfile; rm testfile' \
+"\
+creating testfile
+0
+qwerty
+" "" "\
+--- /dev/null
++++ testfile
+@@ -0,0 +1 @@
++qwerty
+"
+
+# testing "test name" "command(s)" "expected result" "file input" "stdin"
+testing "patch understands ...dir///dir..." \
+       'patch -p1 2>&1; echo $?' \
+"\
+patching file dir2///file
+patch: can't open 'dir2///file': No such file or directory
+1
+" "" "\
+--- bogus_dir///dir2///file
++++ bogus_dir///dir2///file
+@@ -1,2 +1,3 @@
+ qwe
++asd
+ zxc
+"
+
 rm input.orig 2>/dev/null
 
 exit $FAILCOUNT
index 624b0a7..2a06d2b 100755 (executable)
@@ -2,7 +2,7 @@
 
 # pidof tests.
 # Copyright 2005 by Bernhard Reutner-Fischer
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 # AUDIT:
 
index 95f8110..9a3c874 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright 2008 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
@@ -89,9 +89,9 @@ testing "printf understands %Ld" \
 
 testing "printf handles %d bad_input" \
        "${bb}printf '%d\n' 1 - 2 bad 3 123bad 4 2>&1; echo \$?" \
-"1\n""printf: -: invalid number\n""0\n"\
-"2\n""printf: bad: invalid number\n""0\n"\
-"3\n""printf: 123bad: invalid number\n""0\n"\
+"1\n""printf: invalid number '-'\n""0\n"\
+"2\n""printf: invalid number 'bad'\n""0\n"\
+"3\n""printf: invalid number '123bad'\n""0\n"\
 "4\n""1\n" \
        "" ""
 
index e6822df..c7fc8ad 100755 (executable)
@@ -2,7 +2,7 @@
 
 # Readlink tests.
 # Copyright 2006 by Natanael Copa <n@tanael.org>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
@@ -30,3 +30,4 @@ testing "readlink -f on a wierd dir" "readlink -f $TESTDIR/../$TESTFILE" "$PWD/$
 # clean up
 rm -r "$TESTLINK" "$TESTDIR"
 
+exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255))
index c300233..51575d9 100755 (executable)
@@ -4,6 +4,8 @@
 
 . ./testing.sh
 
+total_failed=0
+
 # Run one old-style test.
 # Tests are stored in applet/testcase shell scripts.
 # They are run using "sh -x -e applet/testcase".
@@ -72,7 +74,10 @@ run_oldstyle_applet_tests()
                        *.mine) continue ;;    # svn-produced junk
                        *.r[0-9]*) continue ;; # svn-produced junk
                esac
-               run_applet_testcase "$applet" "$testcase" || status=1
+               run_applet_testcase "$applet" "$testcase" || {
+                       status=1
+                       total_failed=$((total_failed + 1))
+               }
        done
        return $status
 }
@@ -150,7 +155,10 @@ for applet in $applets; do
                fi
 #              echo "Running test $tsdir/$applet.tests"
                PATH="$LINKSDIR:$tsdir:$bindir:$PATH" \
-                       "$tsdir/$applet.tests" || status=1
+                       "$tsdir/$applet.tests"
+               rc=$?
+               total_failed=$((total_failed + rc))
+               test $rc -ne 0 && status=1
        fi
 done
 
@@ -158,6 +166,6 @@ done
 #rm -rf "$LINKSDIR"
 
 if [ $status -ne 0 ] && [ x"$VERBOSE" = x ]; then
-       echo "Failures detected, running with -v (verbose) will give more info"
+       echo "$total_failed failure(s) detected; running with -v (verbose) will give more info"
 fi
 exit $status
index 985fcdc..37fa02e 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright 2009 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
index 3301a25..9494ac2 100755 (executable)
@@ -2,11 +2,11 @@
 
 # SUSv3 compliant sed tests.
 # Copyright 2005 by Rob Landley <rob@landley.net>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
-# testing "description" "arguments" "result" "infile" "stdin"
+# testing "description" "commands" "result" "infile" "stdin"
 
 # Corner cases
 testing "sed no files (stdin)" 'sed ""' "hello\n" "" "hello\n"
@@ -48,13 +48,12 @@ testing "sed accepts multiple -e" "sed -e 'i\' -e '1' -e 'a\' -e '3'" \
 
 # substitutions
 testing "sed -n" "sed -n -e s/foo/bar/ -e s/bar/baz/" "" "" "foo\n"
+testing "sed with empty match" "sed 's/z*//g'" "string\n" "" "string\n"
 testing "sed s//p" "sed -e s/foo/bar/p -e s/bar/baz/p" "bar\nbaz\nbaz\n" \
        "" "foo\n"
 testing "sed -n s//p" "sed -ne s/abc/def/p" "def\n" "" "abc\n"
-test x"$SKIP_KNOWN_BUGS" = x"" && {
 testing "sed s//g (exhaustive)" "sed -e 's/[[:space:]]*/,/g'" ",1,2,3,4,5,\n" \
        "" "12345\n"
-}
 testing "sed s arbitrary delimiter" "sed -e 's woo boing '" "boing\n" "" "woo\n"
 testing "sed s chains" "sed -e s/foo/bar/ -e s/bar/baz/" "baz\n" "" "foo\n"
 testing "sed s chains2" "sed -e s/foo/bar/ -e s/baz/nee/" "bar\n" "" "foo\n"
@@ -80,10 +79,18 @@ test x"$SKIP_KNOWN_BUGS" = x"" && {
 # Query: how does this interact with no newline at EOF?
 testing "sed n (flushes pattern space, terminates early)" "sed -e 'n;p'" \
        "a\nb\nb\nc\n" "" "a\nb\nc\n"
-# N does _not_ flush pattern space, therefore c is still in there @ script end.
-testing "sed N (doesn't flush pattern space when terminating)" "sed -e 'N;p'" \
-       "a\nb\na\nb\nc\n" "" "a\nb\nc\n"
 }
+# non-GNU sed: N does _not_ flush pattern space, therefore c is eaten @ script end
+# GNU sed: N flushes pattern space, therefore c is printed too @ script end
+testing "sed N (flushes pattern space (GNU behavior))" "sed -e 'N;p'" \
+       "a\nb\na\nb\nc\n" "" "a\nb\nc\n"
+
+testing "sed N test2" "sed ':a;N;s/\n/ /;ta'" \
+       "a b c\n" "" "a\nb\nc\n"
+
+testing "sed N test3" "sed 'N;s/\n/ /'" \
+       "a b\nc\n" "" "a\nb\nc\n"
+
 testing "sed address match newline" 'sed "/b/N;/b\\nc/i woo"' \
        "a\nwoo\nb\nc\nd\n" "" "a\nb\nc\nd\n"
 
@@ -147,11 +154,9 @@ testing "sed selective matches insert newline" \
 testing "sed selective matches noinsert newline" \
        "sed -ne 's/woo/bang/p' input -" "a bang\nb bang" "a woo\nb woo" \
        "c no\nd no"
-test x"$SKIP_KNOWN_BUGS" = x"" && {
 testing "sed clusternewline" \
        "sed -e '/one/a 111' -e '/two/i 222' -e p input -" \
        "one\none\n111\n222\ntwo\ntwo" "one" "two"
-}
 testing "sed subst+write" \
        "sed -e 's/i/z/' -e 'woutputw' input -; $ECHO -n X; cat outputw" \
        "thzngy\nagaznXthzngy\nagazn" "thingy" "again"
@@ -217,7 +222,7 @@ testing "sed s/xxx/[/" "sed -e 's/xxx/[/'" "[\n" "" "xxx\n"
 #testing "sed -g (exhaustive)" "sed -e 's/[[:space:]]*/,/g'" ",1,2,3,4,5," \
 #      "" "12345"
 
-# testing "description" "arguments" "result" "infile" "stdin"
+# testing "description" "commands" "result" "infile" "stdin"
 
 testing "sed n command must reset 'substituted' bit" \
        "sed 's/1/x/;T;n;: next;s/3/y/;t quit;n;b next;: quit;q'" \
@@ -270,11 +275,60 @@ testing "sed a cmd ended by double backslash" \
        | two \\
 '
 
-# fisrt three lines are deleted; 4th line is matched and printed by "2,3" and by "4" ranges
+# first three lines are deleted; 4th line is matched and printed by "2,3" and by "4" ranges
 testing "sed with N skipping lines past ranges on next cmds" \
        "sed -n '1{N;N;d};1p;2,3p;3p;4p'" \
        "4\n4\n" "" "1\n2\n3\n4\n"
 
-# testing "description" "arguments" "result" "infile" "stdin"
+testing "sed -i with address modifies all files, not only first" \
+       "cp input input2; sed -i -e '1s/foo/bar/' input input2 && cat input input2; rm input2" \
+       "bar\nbar\n" "foo\n" ""
+
+testing "sed understands \r" \
+       "sed 's/r/\r/'" \
+       "\rrr\n" "" "rrr\n"
+
+testing "sed -i finishes ranges correctly" \
+       "sed '1,2d' -i input; echo \$?; cat input" \
+       "0\n3\n4\n" "1\n2\n3\n4\n" ""
+
+testing "sed zero chars match/replace advances correctly 1" \
+       "sed 's/l*/@/g'" \
+       "@h@e@o@\n" "" "helllo\n"
+
+testing "sed zero chars match/replace advances correctly 2" \
+       "sed 's [^ .]* x g'" \
+       "x x.x\n" "" " a.b\n"
+
+testing "sed zero chars match/replace logic must not falsely trigger here 1" \
+       "sed 's/a/A/g'" \
+       "_AAA1AA\n" "" "_aaa1aa\n"
+
+testing "sed zero chars match/replace logic must not falsely trigger here 2" \
+       "sed 's/ *$/_/g'" \
+       "qwerty_\n" "" "qwerty\n"
+
+testing "sed /\$_in_regex/ should not match newlines, only end-of-line" \
+       "sed ': testcont; /\\\\$/{ =; N; b testcont }'" \
+       "\
+this is a regular line
+2
+line with \\
+continuation
+more regular lines
+5
+line with \\
+continuation
+" \
+       "" "\
+this is a regular line
+line with \\
+continuation
+more regular lines
+line with \\
+continuation
+"
+
+# testing "description" "commands" "result" "infile" "stdin"
 
 exit $FAILCOUNT
index 7a55374..1e1116c 100755 (executable)
@@ -2,7 +2,7 @@
 
 # SUSv3 compliant seq tests.
 # Copyright 2006 by Rob Landley <rob@landley.net>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 # AUDIT: Full SUSv3 coverage (except internationalization).
 
diff --git a/testsuite/sha1sum.tests b/testsuite/sha1sum.tests
new file mode 100755 (executable)
index 0000000..a968fa8
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+. ./md5sum.tests sha1sum d41337e834377140ae7f98460d71d908598ef04f
diff --git a/testsuite/sha256sum.tests b/testsuite/sha256sum.tests
new file mode 100755 (executable)
index 0000000..d2dd78f
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+. ./md5sum.tests sha256sum 8e1d3ed57ebc130f0f72508446559eeae06451ae6d61b1e8ce46370cfb8963c3
diff --git a/testsuite/sha3sum.tests b/testsuite/sha3sum.tests
new file mode 100755 (executable)
index 0000000..82fada6
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+. ./md5sum.tests sha3sum c29d77bc548fa2b20a04c861400a5360879c52156e2a54a3415b99a9a3123e1d5f36714a24eca8c1f05a8e2d8ba859c930d41141f64a255c6794436fc99c486a
diff --git a/testsuite/sha512sum.tests b/testsuite/sha512sum.tests
new file mode 100755 (executable)
index 0000000..3bc7cae
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+. ./md5sum.tests sha512sum fe413e0f177324d1353893ca0772ceba83fd41512ba63895a0eebb703ef9feac2fb4e92b2cb430b3bda41b46b0cb4ea8307190a5cc795157cfb680a9cd635d0f
index 8a810f8..91b282e 100755 (executable)
@@ -2,7 +2,7 @@
 
 # SUSv3 compliant sort tests.
 # Copyright 2005 by Rob Landley <rob@landley.net>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
index 76b0be0..d07aeef 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright 2008 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
index da278c7..b9f4cbf 100755 (executable)
@@ -2,7 +2,7 @@
 
 # unit test for sum.
 # Copyright 2007 by Bernhard Reutner-Fischer
-# Licensed under GPL v2 or later, see file LICENSE for details.
+# Licensed under GPLv2 or later, see file LICENSE in this source tree.
 
 # AUDIT: Unit tests for sum
 
index d2427d4..305a83b 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 # Copyright 2009 by Denys Vlasenko <vda.linux@googlemail.com>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
@@ -14,4 +14,12 @@ testing "tail: +N with N > file length" \
        "0\n" \
        "" "qw"
 
+testing "tail: -c +N with largish N" \
+       "
+       dd if=/dev/zero bs=16k count=1 2>/dev/null | tail -c +8200 | wc -c;
+       dd if=/dev/zero bs=16k count=1 2>/dev/null | tail -c +8208 | wc -c;
+       " \
+       "8185\n8177\n" \
+       "" ""
+
 exit $FAILCOUNT
index f400790..4929f4e 100755 (executable)
@@ -1,14 +1,58 @@
 #!/bin/sh
 # Copyright 2009 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
+unset LANG
+unset LANGUAGE
+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" '\
+tar xvf - 2>&1; echo $?
+' "\
+tar: short read
+1
+" \
+"" ""
+SKIP=
+
+optional FEATURE_SEAMLESS_GZ
+# In NOMMU case, "invalid magic" message comes from gunzip child process.
+# Otherwise, it comes from tar.
+# Need to fix output up to avoid false positive.
+testing "Empty file is not a tarball.tar.gz" '\
+{ tar xvzf - 2>&1; echo $?; } | grep -Fv "invalid magic"
+' "\
+tar: short read
+1
+" \
+"" ""
+SKIP=
+
+testing "Two zeroed blocks is a ('truncated') empty tarball" '\
+dd if=/dev/zero bs=512 count=2 2>/dev/null | tar xvf - 2>&1; echo $?
+' "\
+0
+" \
+"" ""
+SKIP=
+
+testing "Twenty zeroed blocks is an empty tarball" '\
+dd if=/dev/zero bs=512 count=20 2>/dev/null | tar xvf - 2>&1; echo $?
+' "\
+0
+" \
+"" ""
+SKIP=
+
 optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES
 testing "tar hardlinks and repeated files" '\
 rm -rf input_* test.tar 2>/dev/null
@@ -49,16 +93,19 @@ rm -rf input_* test.tar 2>/dev/null
 chmod 741 input_hard1
 ln input_hard1 input_hard2
 mkdir input_dir
-chmod 550 input_dir
 ln input_hard1 input_dir
 ln input_hard2 input_dir
-tar cf test.tar input_*
+chmod 550 input_dir
+# On some filesystems, input_dir/input_hard2 is returned by readdir
+# BEFORE input_dir/input_hard1! Thats why we cant just "tar cf ... input_*":
+tar cf test.tar input_dir/input_hard* input_hard*
 tar tvf test.tar | sed "s/.*[0-9] input/input/"
+chmod 770 input_dir
+rm -rf input_*
 tar xf test.tar 2>&1
 echo Ok: $?
-ls -l . input_dir/* | grep input_ | sed "s/\\(^[^ ]*\\) .* input/\\1 input/"
+ls -l . input_dir/* | grep "input.*hard" | sed "s/\\(^[^ ]*\\) .* input/\\1 input/"
 ' "\
-input_dir/
 input_dir/input_hard1
 input_dir/input_hard2 -> input_dir/input_hard1
 input_hard1 -> input_dir/input_hard1
@@ -66,7 +113,6 @@ input_hard2 -> input_dir/input_hard1
 Ok: 0
 -rwxr----x input_dir/input_hard1
 -rwxr----x input_dir/input_hard2
-dr-xr-x--- input_dir
 -rwxr----x input_hard1
 -rwxr----x input_hard2
 " \
@@ -80,16 +126,17 @@ rm -rf input_* test.tar 2>/dev/null
 chmod 741 input_file
 ln -s input_file input_soft
 mkdir input_dir
-chmod 550 input_dir
 ln input_file input_dir
 ln input_soft input_dir
-tar cf test.tar input_*
+chmod 550 input_dir
+tar cf test.tar input_dir/* input_[fs]*
 tar tvf test.tar | sed "s/.*[0-9] input/input/" | sort
+chmod 770 input_dir
+rm -rf input_*
 tar xf test.tar 2>&1
 echo Ok: $?
-ls -l . input_dir/* | grep input_ | sed "s/\\(^[^ ]*\\) .* input/\\1 input/"
+ls -l . input_dir/* | grep "input_[fs]" | sed "s/\\(^[^ ]*\\) .* input/\\1 input/"
 ' "\
-input_dir/
 input_dir/input_file
 input_dir/input_soft -> input_file
 input_file -> input_dir/input_file
@@ -97,7 +144,6 @@ input_soft -> input_dir/input_soft
 Ok: 0
 -rwxr----x input_dir/input_file
 lrwxrwxrwx input_file
-dr-xr-x--- input_dir
 -rwxr----x input_file
 lrwxrwxrwx input_file
 " \
@@ -119,6 +165,88 @@ Ok
 "Ok\n" ""
 SKIP=
 
+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
+tar cf test.tar input_dir
+tar tvf test.tar | sed "s/.*[0-9] input/input/"
+chmod 770 input_dir
+rm -rf input_*
+tar xf test.tar 2>&1
+echo Ok: $?
+ls -l input_dir/* . | grep input_ | sed "s/\\(^[^ ]*\\) .* input/\\1 input/"
+chmod 770 input_dir
+' "\
+input_dir/
+input_dir/input_file
+Ok: 0
+-rw-r--r-- input_dir/input_file
+dr-xr-x--- input_dir
+" \
+"" ""
+SKIP=
+}
+
+# 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
+testing "tar extract tgz" "\
+dd count=1 bs=1M if=/dev/zero of=F0 2>/dev/null
+tar -czf F0.tgz F0
+rm F0
+tar -xzvf F0.tgz && echo Ok
+rm F0 || echo BAD
+" "\
+F0
+Ok
+" \
+"" ""
+SKIP=
+
+# 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
+testing "tar extract txz" "\
+uudecode -o input && tar tf input && echo Ok
+" "\
+hello_world
+Ok
+" \
+"" "\
+begin-base64 644 hello_world.txz
+/Td6WFoAAATm1rRGAgAhARYAAAB0L+Wj4AX/AEldADQZSe6ODIZQ3rSQ8kAJ
+SnMPTX+XWGKW3Yu/Rwqg4Ik5wqgQKgVH97J8yA8IvZ4ahaCQogUNHRkXibr2
+Q615wcb2G7fJU49AhWAAAAAAUA8gu9DyXfAAAWWADAAAAB5FXGCxxGf7AgAA
+AAAEWVo=
+====
+"
+SKIP=
+
+# On extract, everything up to and including last ".." component is stripped
+optional FEATURE_TAR_CREATE
+testing "tar strips /../ on extract" "\
+rm -rf input_* test.tar 2>/dev/null
+mkdir input_dir
+echo Ok >input_dir/file
+tar cf test.tar ./../tar.tempdir/input_dir/../input_dir 2>&1
+rm -rf input_* 2>/dev/null
+tar -vxf test.tar 2>&1
+cat input_dir/file 2>&1
+" "\
+tar: removing leading './../tar.tempdir/input_dir/../' from member names
+input_dir/
+input_dir/file
+Ok
+" \
+"" ""
+SKIP=
+
+
 cd .. && rm -rf tar.tempdir || exit 1
 
 exit $FAILCOUNT
index 886c37c..f5351f4 100644 (file)
@@ -9,4 +9,4 @@ find foo | sort >logfile.bb
 rm -rf foo/*
 tar xf foo.tar -C foo ./1/10
 find foo | sort >logfile.gnu
-cmp logfile.gnu logfile.bb
+diff -u logfile.gnu logfile.bb
index 1a19285..3fb5c90 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright 2006 Bernhard Reutner-Fischer
-# Licensed under GPL v2 or later, see file LICENSE for details.
+# Licensed under GPLv2 or later, see file LICENSE in this source tree.
 
 . ./testing.sh
 a="taskset"
index 7d80444..2c92e34 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 # Copyright 2007 by Denys Vlasenko <vda.linux@googlemail.com>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
index 913d7f8..f5b7569 100644 (file)
@@ -1,4 +1,4 @@
-# Simple test harness infrastructurei for BusyBox
+# Simple test harness infrastructure for BusyBox
 #
 # Copyright 2005 by Rob Landley
 #
@@ -26,7 +26,7 @@
 # number of failed tests.
 
 # The "optional" function is used to skip certain tests, ala:
-#   optional CONFIG_FEATURE_THINGY
+#   optional FEATURE_THINGY
 #
 # The "optional" function checks the environment variable "OPTIONFLAGS",
 # which is either empty (in which case it always clears SKIP) or
@@ -56,10 +56,10 @@ optional()
 {
        SKIP=
        while test "$1"; do
-               if test x"${OPTIONFLAGS/*:$1:*/y}" != x"y"; then
-                       SKIP=1
-                       return
-               fi
+               case "${OPTIONFLAGS}" in
+                       *:$1:*) ;;
+                       *) SKIP=1; return ;;
+               esac
                shift
        done
 }
@@ -87,6 +87,8 @@ testing()
 
   $ECHO -ne "$3" > expected
   $ECHO -ne "$4" > input
+  [ -z "$VERBOSE" ] || echo ======================
+  [ -z "$VERBOSE" ] || echo "echo -ne '$4' >input"
   [ -z "$VERBOSE" ] || echo "echo -ne '$5' | $2"
   $ECHO -ne "$5" | eval "$2" > actual
   RETVAL=$?
index a1f83bf..5cca299 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 # Copyright 2009 by Denys Vlasenko <vda.linux@googlemail.com>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
@@ -15,19 +15,19 @@ testing "tr understands 0-9A-F" \
        "tr -cd '[0-9A-F]'" \
        "19AF" "" "19AFH\n"
 
-optional CONFIG_FEATURE_TR_CLASSES
+optional FEATURE_TR_CLASSES
 testing "tr understands [:xdigit:]" \
        "tr -cd '[:xdigit:]'" \
        "19AF" "" "19AFH\n"
 SKIP=
 
-optional CONFIG_FEATURE_TR_CLASSES
+optional FEATURE_TR_CLASSES
 testing "tr does not stop after [:digit:]" \
        "tr '[:digit:]y-z' 111111111123" \
        "111abcx23\n" "" "789abcxyz\n"
 SKIP=
 
-optional CONFIG_FEATURE_TR_CLASSES
+optional FEATURE_TR_CLASSES
 testing "tr has correct xdigit sequence" \
        "tr '[:xdigit:]Gg' 1111111151242222333330xX" \
        "#1111111151242222x333330X\n" "" \
diff --git a/testsuite/uncompress.tests b/testsuite/uncompress.tests
new file mode 100755 (executable)
index 0000000..51a2334
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/sh
+# Copyright 2011 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "test name" "commands" "expected result" "file input" "stdin"
+
+testing "uncompress < \x1f\x9d\x90 \x01 x N" \
+'uncompress 2>&1 1>/dev/null; echo $?' \
+"\
+uncompress: corrupted data
+1
+" \
+"" "\
+\x1f\x9d\x90\
+\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\
+"
+
+exit $FAILCOUNT
index 8dbe3eb..7b326dc 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright 2008 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 test -f "$bindir/.config" && . "$bindir/.config"
index 02c595a..83bf382 100755 (executable)
@@ -2,7 +2,7 @@
 
 # SUSv3 compliant uniq tests.
 # Copyright 2005 by Rob Landley <rob@landley.net>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 # AUDIT: Full SUSv3 coverage (except internationalization).
 
index 0ce5da2..8677a03 100755 (executable)
@@ -3,7 +3,7 @@
 # Tests for unzip.
 # Copyright 2006 Rob Landley <rob@landley.net>
 # Copyright 2006 Glenn McGrath
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
index 6556e60..6ce70f7 100755 (executable)
@@ -2,22 +2,21 @@
 
 # unit test for uuencode to test functionality.
 # Copyright 2006 by Erik Hovland <erik@hovland.org>
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 # AUDIT: Unit tests for uuencode
 
 . ./testing.sh
 
-# testing "test name" "options" "expected result" "file input" "stdin"
-#   file input will be file called "input"
-#   test can create a file "actual" instead of writing to stdout
+# testing "test name" "command(s)" "expected result" "file input" "stdin"
+# file input will be file called "input"
+# test can create a file "actual" instead of writing to stdout
 
 # Test setup of standard input
-saved_umask=$(umask)
 umask 0
 testing "uuencode sets standard input mode correctly" \
         "uuencode foo </dev/null | head -n 1 | grep -q 666 && echo yes" "yes\n" "" ""
-umask $saved_umask
+umask 022
 
 testing "uuencode correct encoding" "uuencode bb_uuenc_test.out" \
 "begin 644 bb_uuenc_test.out\nM5&AE(&9A<W0@9W)E>2!F;W@@:G5M<&5D(&]V97(@=&AE(&QA>GD@8G)O=VX@\n%9&]G+@H\`\n\`\nend\n" \
@@ -25,4 +24,99 @@ testing "uuencode correct encoding" "uuencode bb_uuenc_test.out" \
 testing "uuencode correct base64 encoding" "uuencode -m bb_uuenc_test.out" \
 "begin-base64 644 bb_uuenc_test.out\nVGhlIGZhc3QgZ3JleSBmb3gganVtcGVkIG92ZXIgdGhlIGxhenkgYnJvd24g\nZG9nLgo=\n====\n" \
         "" "The fast grey fox jumped over the lazy brown dog.\n"
+
+testing "uuencode empty file" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+`
+end
+' "" ""
+testing "uuencode -m empty file" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+====
+' "" ""
+
+testing "uuencode file 'A'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+!00``
+`
+end
+A' "" "A"
+testing "uuencode -m file 'A'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+QQ==
+====
+A' "" "A"
+
+testing "uuencode file 'AB'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+"04(`
+`
+end
+AB' "" "AB"
+testing "uuencode -m file 'AB'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+QUI=
+====
+AB' "" "AB"
+
+testing "uuencode file 'ABC'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+#04)#
+`
+end
+ABC' "" "ABC"
+testing "uuencode -m file 'ABC'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+QUJD
+====
+ABC' "" "ABC"
+
+testing "uuencode file 'ABCD'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+$04)#1```
+`
+end
+ABCD' "" "ABCD"
+testing "uuencode -m file 'ABCD'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+QUJDRA==
+====
+ABCD' "" "ABCD"
+
+testing "uuencode file 'ABCDE'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+%04)#1$4`
+`
+end
+ABCDE' "" "ABCDE"
+testing "uuencode -m file 'ABCDE'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+QUJDREU=
+====
+ABCDE' "" "ABCDE"
+
+testing "uuencode file 'ABCDEF'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+&04)#1$5&
+`
+end
+ABCDEF' "" "ABCDEF"
+testing "uuencode -m file 'ABCDEF'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+QUJDREVG
+====
+ABCDEF' "" "ABCDEF"
+
+testing "uuencode file 'A<NUL><0xff>Z'" 'r=`uuencode FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin 644 FILE
+$00#_6@``
+`
+end
+A\x0\xffZ' "" "A\x0\xffZ"
+testing "uuencode -m file 'A<NUL><0xff>Z'" 'r=`uuencode -m FILE`; echo "$r"; echo "$r" | uudecode -o -;' \
+'begin-base64 644 FILE
+QQD/Wg==
+====
+A\x0\xffZ' "" "A\x0\xffZ"
+
 exit $FAILCOUNT
index 63ceb9f..349583d 100644 (file)
@@ -1,4 +1,4 @@
-BUSYBOX=$(type -p busybox)
+BUSYBOX=$(command -pv busybox)
 SAVED_PATH=$PATH
 unset PATH
 $BUSYBOX which ls
index 6463252..2d0a201 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # Copyright 2008 by Denys Vlasenko
-# Licensed under GPL v2, see file LICENSE for details.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 . ./testing.sh
 
index 91d1fc2..5a8b006 100644 (file)
@@ -10,6 +10,7 @@ INSERT
 config ACPID
        bool "acpid"
        default y
+       select PLATFORM_LINUX
        help
          acpid listens to ACPI events coming either in textual form from
          /proc/acpi/event (though it is marked deprecated it is still widely
@@ -32,15 +33,24 @@ config FEATURE_ACPID_COMPAT
 config BLKID
        bool "blkid"
        default y
+       select PLATFORM_LINUX
        select VOLUMEID
        help
          Lists labels and UUIDs of all filesystems.
          WARNING:
          With all submodules selected, it will add ~8k to busybox.
 
+config FEATURE_BLKID_TYPE
+       bool "Print filesystem type"
+       default n
+       depends on BLKID
+       help
+         Show TYPE="filesystem type"
+
 config DMESG
        bool "dmesg"
        default y
+       select PLATFORM_LINUX
        help
          dmesg is used to examine or control the kernel ring buffer. When the
          Linux kernel prints messages to the system log, they are stored in
@@ -74,6 +84,7 @@ config FEATURE_DMESG_PRETTY
 config FBSET
        bool "fbset"
        default y
+       select PLATFORM_LINUX
        help
          fbset is used to show or change the settings of a Linux frame buffer
          device. The frame buffer device provides a simple and unique
@@ -102,6 +113,7 @@ config FEATURE_FBSET_READMODE
 config FDFLUSH
        bool "fdflush"
        default y
+       select PLATFORM_LINUX
        help
          fdflush is only needed when changing media on slightly-broken
          removable media drives. It is used to make Linux believe that a
@@ -114,12 +126,14 @@ config FDFLUSH
 config FDFORMAT
        bool "fdformat"
        default y
+       select PLATFORM_LINUX
        help
          fdformat is used to low-level format a floppy disk.
 
 config FDISK
        bool "fdisk"
        default y
+       select PLATFORM_LINUX
        help
          The fdisk utility is used to divide hard disks into one or more
          logical disks, which are generally called partitions. This utility
@@ -130,6 +144,7 @@ config FDISK_SUPPORT_LARGE_DISKS
        bool "Support over 4GB disks"
        default y
        depends on FDISK
+       depends on !LFS   # with LFS no special code is needed
        help
          Enable this option to support large disks > 4GB.
 
@@ -174,6 +189,14 @@ config FEATURE_OSF_LABEL
          Enabling this option allows you to create or change BSD disklabels
          and define and edit BSD disk slices.
 
+config FEATURE_GPT_LABEL
+       bool "Support GPT disklabels"
+       default n
+       depends on FDISK && FEATURE_FDISK_WRITABLE
+       help
+         Enabling this option allows you to view GUID Partition Table
+         disklabels.
+
 config FEATURE_FDISK_ADVANCED
        bool "Support expert mode"
        default y
@@ -187,6 +210,7 @@ config FEATURE_FDISK_ADVANCED
 config FINDFS
        bool "findfs"
        default y
+       select PLATFORM_LINUX
        select VOLUMEID
        help
          Prints the name of a filesystem with given label or UUID.
@@ -202,6 +226,7 @@ config FLOCK
 config FREERAMDISK
        bool "freeramdisk"
        default y
+       select PLATFORM_LINUX
        help
          Linux allows you to create ramdisks. This utility allows you to
          delete them and completely free all memory that was used for the
@@ -224,12 +249,14 @@ config FSCK_MINIX
 config MKFS_EXT2
        bool "mkfs_ext2"
        default y
+       select PLATFORM_LINUX
        help
          Utility to create EXT2 filesystems.
 
 config MKFS_MINIX
        bool "mkfs_minix"
        default y
+       select PLATFORM_LINUX
        help
          The minix filesystem is a nice, small, compact, read-write filesystem
          with little overhead. If you wish to be able to create minix
@@ -247,6 +274,7 @@ config FEATURE_MINIX2
 config MKFS_REISER
        bool "mkfs_reiser"
        default n
+       select PLATFORM_LINUX
        help
          Utility to create ReiserFS filesystems.
          Note: this applet needs a lot of testing and polishing.
@@ -254,6 +282,7 @@ config MKFS_REISER
 config MKFS_VFAT
        bool "mkfs_vfat"
        default y
+       select PLATFORM_LINUX
        help
          Utility to create FAT32 filesystems.
 
@@ -302,6 +331,7 @@ config HD
 config HWCLOCK
        bool "hwclock"
        default y
+       select PLATFORM_LINUX
        help
          The hwclock utility is used to read and set the hardware clock
          on a system. This is primarily used to set the current time on
@@ -319,7 +349,7 @@ config FEATURE_HWCLOCK_LONG_OPTIONS
 
 config FEATURE_HWCLOCK_ADJTIME_FHS
        bool "Use FHS /var/lib/hwclock/adjtime"
-       default y
+       default n  # util-linux-ng in Fedora 13 still uses /etc/adjtime
        depends on HWCLOCK
        help
          Starting with FHS 2.3, the adjtime state file is supposed to exist
@@ -332,7 +362,6 @@ config FEATURE_HWCLOCK_ADJTIME_FHS
 config IPCRM
        bool "ipcrm"
        default y
-       select FEATURE_SUID
        help
          The ipcrm utility allows the removal of System V interprocess
          communication (IPC) objects and the associated data structures
@@ -341,7 +370,7 @@ config IPCRM
 config IPCS
        bool "ipcs"
        default y
-       select FEATURE_SUID
+       select PLATFORM_LINUX
        help
          The ipcs utility is used to provide information on the currently
          allocated System V interprocess (IPC) objects in the system.
@@ -349,6 +378,7 @@ config IPCS
 config LOSETUP
        bool "losetup"
        default y
+       select PLATFORM_LINUX
        help
          losetup is used to associate or detach a loop device with a regular
          file or block device, and to query the status of a loop device. This
@@ -357,6 +387,7 @@ config LOSETUP
 config LSPCI
        bool "lspci"
        default y
+       #select PLATFORM_LINUX
        help
          lspci is a utility for displaying information about PCI buses in the
          system and devices connected to them.
@@ -366,69 +397,13 @@ config LSPCI
 config LSUSB
        bool "lsusb"
        default y
+       #select PLATFORM_LINUX
        help
          lsusb is a utility for displaying information about USB buses in the
          system and devices connected to them.
 
          This version uses sysfs (/sys/bus/usb/devices) only.
 
-config MDEV
-       bool "mdev"
-       default y
-       help
-         mdev is a mini-udev implementation for dynamically creating device
-         nodes in the /dev directory.
-
-         For more information, please see docs/mdev.txt
-
-config FEATURE_MDEV_CONF
-       bool "Support /etc/mdev.conf"
-       default y
-       depends on MDEV
-       help
-         Add support for the mdev config file to control ownership and
-         permissions of the device nodes.
-
-         For more information, please see docs/mdev.txt
-
-config FEATURE_MDEV_RENAME
-       bool "Support subdirs/symlinks"
-       default y
-       depends on FEATURE_MDEV_CONF
-       help
-         Add support for renaming devices and creating symlinks.
-
-         For more information, please see docs/mdev.txt
-
-config FEATURE_MDEV_RENAME_REGEXP
-       bool "Support regular expressions substitutions when renaming device"
-       default y
-       depends on FEATURE_MDEV_RENAME
-       help
-         Add support for regular expressions substitutions when renaming
-         device.
-
-config FEATURE_MDEV_EXEC
-       bool "Support command execution at device addition/removal"
-       default y
-       depends on FEATURE_MDEV_CONF
-       help
-         This adds support for an optional field to /etc/mdev.conf for
-         executing commands when devices are created/removed.
-
-         For more information, please see docs/mdev.txt
-
-config FEATURE_MDEV_LOAD_FIRMWARE
-       bool "Support loading of firmwares"
-       default y
-       depends on MDEV
-       help
-         Some devices need to load firmware before they can be usable.
-
-         These devices will request userspace look up the files in
-         /lib/firmware/ and if it exists, send it to the kernel for
-         loading into the hardware.
-
 config MKSWAP
        bool "mkswap"
        default y
@@ -459,20 +434,10 @@ config MORE
          you will probably find this utility very helpful. If you don't have
          any need to reading text files, you can leave this disabled.
 
-config FEATURE_USE_TERMIOS
-       bool "Use termios to manipulate the screen"
-       default y
-       depends on MORE || TOP
-       help
-         This option allows utilities such as 'more' and 'top' to determine
-         the size of the screen. If you leave this disabled, your utilities
-         that display things on the screen will be especially primitive and
-         will be unable to determine the current screen size, and will be
-         unable to move the cursor.
-
 config MOUNT
        bool "mount"
        default y
+       select PLATFORM_LINUX
        help
          All files and filesystems in Unix are arranged into one big directory
          tree. The 'mount' utility is used to graft a filesystem onto a
@@ -520,13 +485,18 @@ config FEATURE_MOUNT_LABEL
          This also enables label or uuid support for swapon.
 
 config FEATURE_MOUNT_NFS
-       bool "Support mounting NFS file systems"
-       default y
+       bool "Support mounting NFS file systems on Linux < 2.6.23"
+       default n
        depends on MOUNT
        select FEATURE_HAVE_RPC
        select FEATURE_SYSLOG
        help
-         Enable mounting of NFS file systems.
+         Enable mounting of NFS file systems on Linux kernels prior
+         to version 2.6.23. Note that in this case mounting of NFS
+         over IPv6 will not be possible.
+
+         Note that this option links in RPC support from libc,
+         which is rather large (~10 kbytes on uclibc).
 
 config FEATURE_MOUNT_CIFS
        bool "Support mounting CIFS/SMB file systems"
@@ -555,6 +525,7 @@ config FEATURE_MOUNT_FSTAB
 config PIVOT_ROOT
        bool "pivot_root"
        default y
+       select PLATFORM_LINUX
        help
          The pivot_root utility swaps the mount points for the root filesystem
          with some other mounted filesystem. This allows you to do all sorts
@@ -582,12 +553,14 @@ config RDEV
 config READPROFILE
        bool "readprofile"
        default y
+       #select PLATFORM_LINUX
        help
          This allows you to parse /proc/profile for basic profiling.
 
 config RTCWAKE
        bool "rtcwake"
        default y
+       select PLATFORM_LINUX
        help
          Enter a system sleep state until specified wakeup time.
 
@@ -607,6 +580,7 @@ config SCRIPTREPLAY
 config SETARCH
        bool "setarch"
        default y
+       select PLATFORM_LINUX
        help
          The linux32 utility is used to create a 32bit environment for the
          specified program (usually a shell). It only makes sense to have
@@ -616,6 +590,7 @@ config SETARCH
 config SWAPONOFF
        bool "swaponoff"
        default y
+       select PLATFORM_LINUX
        help
          This option enables both the 'swapon' and the 'swapoff' utilities.
          Once you have created some swap space using 'mkswap', you also need
@@ -634,6 +609,7 @@ config FEATURE_SWAPON_PRI
 config SWITCH_ROOT
        bool "switch_root"
        default y
+       select PLATFORM_LINUX
        help
          The switch_root utility is used from initramfs to select a new
          root device. Under initramfs, you have to use this instead of
@@ -653,6 +629,7 @@ config SWITCH_ROOT
 config UMOUNT
        bool "umount"
        default y
+       select PLATFORM_LINUX
        help
          When you want to remove a mounted filesystem from its current mount
          point, for example when you are shutting down the system, the
@@ -722,224 +699,6 @@ config FEATURE_MTAB_SUPPORT
          About the only reason to use this is if you've removed /proc from
          your kernel.
 
-config VOLUMEID
-       bool #No description makes it a hidden option
-       default n
-
-menu "Filesystem/Volume identification"
-       depends on VOLUMEID
-
-config FEATURE_VOLUMEID_EXT
-       bool "Ext filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-config FEATURE_VOLUMEID_BTRFS
-       bool "btrfs filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-config FEATURE_VOLUMEID_REISERFS
-       bool "Reiser filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-config FEATURE_VOLUMEID_FAT
-       bool "fat filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-config FEATURE_VOLUMEID_HFS
-       bool "hfs filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-config FEATURE_VOLUMEID_JFS
-       bool "jfs filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-### config FEATURE_VOLUMEID_UFS
-###    bool "ufs filesystem"
-###    default y
-###    depends on VOLUMEID
-###    help
-###      TODO
-
-config FEATURE_VOLUMEID_XFS
-       bool "xfs filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-config FEATURE_VOLUMEID_NTFS
-       bool "ntfs filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-config FEATURE_VOLUMEID_ISO9660
-       bool "iso9660 filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-config FEATURE_VOLUMEID_UDF
-       bool "udf filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-config FEATURE_VOLUMEID_LUKS
-       bool "luks filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-config FEATURE_VOLUMEID_LINUXSWAP
-       bool "linux swap filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-### config FEATURE_VOLUMEID_LVM
-###    bool "lvm"
-###    default y
-###    depends on VOLUMEID
-###    help
-###      TODO
-
-config FEATURE_VOLUMEID_CRAMFS
-       bool "cramfs filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-### config FEATURE_VOLUMEID_HPFS
-###    bool "hpfs filesystem"
-###    default y
-###    depends on VOLUMEID
-###    help
-###      TODO
-
-config FEATURE_VOLUMEID_ROMFS
-       bool "romfs filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-config FEATURE_VOLUMEID_SYSV
-       bool "sysv filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-### config FEATURE_VOLUMEID_MINIX
-###    bool "minix filesystem"
-###    default y
-###    depends on VOLUMEID
-###    help
-###      TODO
-
-### These only detect partition tables - not used (yet?)
-### config FEATURE_VOLUMEID_MAC
-###    bool "mac filesystem"
-###    default y
-###    depends on VOLUMEID
-###    help
-###      TODO
-###
-### config FEATURE_VOLUMEID_MSDOS
-###    bool "msdos filesystem"
-###    default y
-###    depends on VOLUMEID
-###    help
-###      TODO
-
-config FEATURE_VOLUMEID_OCFS2
-       bool "ocfs2 filesystem"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-### config FEATURE_VOLUMEID_HIGHPOINTRAID
-###    bool "highpoint raid"
-###    default y
-###    depends on VOLUMEID
-###    help
-###      TODO
-
-### config FEATURE_VOLUMEID_ISWRAID
-###    bool "intel raid"
-###    default y
-###    depends on VOLUMEID
-###    help
-###      TODO
-
-### config FEATURE_VOLUMEID_LSIRAID
-###    bool "lsi raid"
-###    default y
-###    depends on VOLUMEID
-###    help
-###      TODO
-
-### config FEATURE_VOLUMEID_VIARAID
-###    bool "via raid"
-###    default y
-###    depends on VOLUMEID
-###    help
-###      TODO
-
-### config FEATURE_VOLUMEID_SILICONRAID
-###    bool "silicon raid"
-###    default y
-###    depends on VOLUMEID
-###    help
-###      TODO
-
-### config FEATURE_VOLUMEID_NVIDIARAID
-###    bool "nvidia raid"
-###    default y
-###    depends on VOLUMEID
-###    help
-###      TODO
-
-### config FEATURE_VOLUMEID_PROMISERAID
-###    bool "promise raid"
-###    default y
-###    depends on VOLUMEID
-###    help
-###      TODO
-
-config FEATURE_VOLUMEID_LINUXRAID
-       bool "linuxraid"
-       default y
-       depends on VOLUMEID
-       help
-         TODO
-
-endmenu
+source util-linux/volume_id/Config.in
 
 endmenu
index afc0db5..468fc6b 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
@@ -26,7 +26,6 @@ lib-$(CONFIG_IPCS)              += ipcs.o
 lib-$(CONFIG_LOSETUP)           += losetup.o
 lib-$(CONFIG_LSPCI)             += lspci.o
 lib-$(CONFIG_LSUSB)             += lsusb.o
-lib-$(CONFIG_MDEV)              += mdev.o
 lib-$(CONFIG_MKFS_EXT2)         += mkfs_ext2.o
 lib-$(CONFIG_MKFS_MINIX)        += mkfs_minix.o
 lib-$(CONFIG_MKFS_REISER)       += mkfs_reiser.o
index 11a9f62..38421c2 100644 (file)
@@ -4,11 +4,35 @@
  *
  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
-#include "libbb.h"
 
+//usage:#define acpid_trivial_usage
+//usage:       "[-df] [-c CONFDIR] [-l LOGFILE] [-a ACTIONFILE] [-M MAPFILE] [-e PROC_EVENT_FILE] [-p PIDFILE]"
+//usage:#define acpid_full_usage "\n\n"
+//usage:       "Listen to ACPI events and spawn specific helpers on event arrival\n"
+//usage:     "\n       -d      Log to stderr, not log file (implies -f)"
+//usage:     "\n       -f      Run in foreground"
+//usage:     "\n       -c DIR  Config directory [/etc/acpi]"
+//usage:     "\n       -e FILE /proc event file [/proc/acpi/event]"
+//usage:     "\n       -l FILE Log file [/var/log/acpid.log]"
+//usage:     "\n       -p FILE Pid file [/var/run/acpid.pid]"
+//usage:     "\n       -a FILE Action file [/etc/acpid.conf]"
+//usage:     "\n       -M FILE Map file [/etc/acpi.map]"
+//usage:       IF_FEATURE_ACPID_COMPAT(
+//usage:     "\n\nAccept and ignore compatibility options -g -m -s -S -v"
+//usage:       )
+//usage:
+//usage:#define acpid_example_usage
+//usage:       "Without -e option, acpid uses all /dev/input/event* files\n"
+//usage:       "# acpid\n"
+//usage:       "# acpid -l /var/log/my-acpi-log\n"
+//usage:       "# acpid -e /proc/acpi/event\n"
+
+#include "libbb.h"
+#include <syslog.h>
 #include <linux/input.h>
+
 #ifndef EV_SW
 # define EV_SW         0x05
 #endif
 # define SW_RFKILL_ALL 0x03
 #endif
 #ifndef KEY_POWER
-# define KEY_POWER     116     /* SC System Power Down */
+# define KEY_POWER      116     /* SC System Power Down */
 #endif
 #ifndef KEY_SLEEP
-# define KEY_SLEEP     142     /* SC System Sleep */
+# define KEY_SLEEP      142     /* SC System Sleep */
 #endif
 
+enum {
+       OPT_c = (1 << 0),
+       OPT_d = (1 << 1),
+       OPT_e = (1 << 2),
+       OPT_f = (1 << 3),
+       OPT_l = (1 << 4),
+       OPT_a = (1 << 5),
+       OPT_M = (1 << 6),
+       OPT_p = (1 << 7) * ENABLE_FEATURE_PIDFILE,
+};
+
+struct acpi_event {
+       const char *s_type;
+       uint16_t n_type;
+       const char *s_code;
+       uint16_t n_code;
+       uint32_t value;
+       const char *desc;
+};
+
+static const struct acpi_event f_evt_tab[] = {
+       { "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRF 00000080" },
+       { "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRB 00000080" },
+       { "EV_SW", 0x05, "SW_LID", 0x00, 1, "button/lid LID0 00000080" },
+};
+
+struct acpi_action {
+       const char *key;
+       const char *action;
+};
+
+static const struct acpi_action f_act_tab[] = {
+       { "PWRF", "PWRF/00000080" },
+       { "LID0", "LID/00000080" },
+};
+
+struct globals {
+       struct acpi_action *act_tab;
+       int n_act;
+       struct acpi_event *evt_tab;
+       int n_evt;
+} FIX_ALIASING;
+#define G (*ptr_to_globals)
+#define act_tab         (G.act_tab)
+#define n_act           (G.n_act  )
+#define evt_tab         (G.evt_tab)
+#define n_evt           (G.n_evt  )
+#define INIT_G() do { \
+       SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
 
 /*
  * acpid listens to ACPI events coming either in textual form
@@ -48,7 +122,7 @@ static void process_event(const char *event)
        const char *args[] = { "run-parts", handler, NULL };
 
        // debug info
-       if (option_mask32 & 8) { // -d
+       if (option_mask32 & OPT_d) {
                bb_error_msg("%s", event);
        }
 
@@ -60,125 +134,225 @@ static void process_event(const char *event)
                spawn((char **)args + (0==(st.st_mode & S_IFDIR)));
        else
                bb_simple_perror_msg(event);
+
        free(handler);
 }
 
+static const char *find_action(struct input_event *ev, const char *buf)
+{
+       const char *action = NULL;
+       int i;
+
+       // map event
+       for (i = 0; i < n_evt; i++) {
+               if (ev) {
+                       if (ev->type == evt_tab[i].n_type && ev->code == evt_tab[i].n_code && ev->value == evt_tab[i].value) {
+                               action = evt_tab[i].desc;
+                               break;
+                       }
+               }
+
+               if (buf) {
+                       if (strncmp(buf, evt_tab[i].desc, strlen(buf)) == 0) {
+                               action = evt_tab[i].desc;
+                               break;
+                       }
+               }
+       }
+
+       // get action
+       if (action) {
+               for (i = 0; i < n_act; i++) {
+                       if (strstr(action, act_tab[i].key)) {
+                               action = act_tab[i].action;
+                               break;
+                       }
+               }
+       }
+
+       return action;
+}
+
+static void parse_conf_file(const char *filename)
+{
+       parser_t *parser;
+       char *tokens[2];
+
+       parser = config_open2(filename, fopen_for_read);
+
+       if (parser) {
+               while (config_read(parser, tokens, 2, 2, "# \t", PARSE_NORMAL)) {
+                       act_tab = xrealloc_vector(act_tab, 1, n_act);
+                       act_tab[n_act].key = xstrdup(tokens[0]);
+                       act_tab[n_act].action = xstrdup(tokens[1]);
+                       n_act++;
+               }
+               config_close(parser);
+       } else {
+               act_tab = (void*)f_act_tab;
+               n_act = ARRAY_SIZE(f_act_tab);
+       }
+}
+
+static void parse_map_file(const char *filename)
+{
+       parser_t *parser;
+       char *tokens[6];
+
+       parser = config_open2(filename, fopen_for_read);
+
+       if (parser) {
+               while (config_read(parser, tokens, 6, 6, "# \t", PARSE_NORMAL)) {
+                       evt_tab = xrealloc_vector(evt_tab, 1, n_evt);
+                       evt_tab[n_evt].s_type = xstrdup(tokens[0]);
+                       evt_tab[n_evt].n_type = xstrtou(tokens[1], 16);
+                       evt_tab[n_evt].s_code = xstrdup(tokens[2]);
+                       evt_tab[n_evt].n_code = xatou16(tokens[3]);
+                       evt_tab[n_evt].value = xatoi_positive(tokens[4]);
+                       evt_tab[n_evt].desc = xstrdup(tokens[5]);
+                       n_evt++;
+               }
+               config_close(parser);
+       } else {
+               evt_tab = (void*)f_evt_tab;
+               n_evt = ARRAY_SIZE(f_evt_tab);
+       }
+}
+
 /*
- * acpid [-c conf_dir] [-l log_file] [-e proc_event_file] [evdev_event_file...]
-*/
+ * acpid [-c conf_dir] [-r conf_file ] [-a map_file ] [-l log_file] [-e proc_event_file]
+ */
 
 int acpid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int acpid_main(int argc, char **argv)
+int acpid_main(int argc UNUSED_PARAM, char **argv)
 {
+       int nfd;
+       int opts;
        struct pollfd *pfd;
-       int i, nfd;
-       const char *opt_conf = "/etc/acpi";
-       const char *opt_input = "/proc/acpi/event";
+       const char *opt_dir = "/etc/acpi";
+       const char *opt_input = "/dev/input/event";
        const char *opt_logfile = "/var/log/acpid.log";
+       const char *opt_action = "/etc/acpid.conf";
+       const char *opt_map = "/etc/acpi.map";
+#if ENABLE_FEATURE_PIDFILE
+       const char *opt_pidfile = CONFIG_PID_FILE_PATH "/acpid.pid";
+#endif
 
-       getopt32(argv, "c:e:l:d"
-               IF_FEATURE_ACPID_COMPAT("g:m:s:S:v"),
-               &opt_conf, &opt_input, &opt_logfile
-               IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL, NULL)
+       INIT_G();
+
+       opt_complementary = "df:e--e";
+       opts = getopt32(argv, "c:de:fl:a:M:" IF_FEATURE_PIDFILE("p:") IF_FEATURE_ACPID_COMPAT("g:m:s:S:v"),
+               &opt_dir, &opt_input, &opt_logfile, &opt_action, &opt_map
+               IF_FEATURE_PIDFILE(, &opt_pidfile)
+               IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL)
        );
 
-       // daemonize unless -d given
-       if (!(option_mask32 & 8)) { // ! -d
-               bb_daemonize_or_rexec(0, argv);
-               close(2);
-               xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC);
+       if (!(opts & OPT_f)) {
+               /* No -f "Foreground", we go to background */
+               bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
        }
 
-       argv += optind;
-       argc -= optind;
+       if (!(opts & OPT_d)) {
+               /* No -d "Debug", we log to log file.
+                * This includes any output from children.
+                */
+               xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO);
+               xdup2(STDOUT_FILENO, STDERR_FILENO);
+               /* Also, acpid's messages (but not children) will go to syslog too */
+               openlog(applet_name, LOG_PID, LOG_DAEMON);
+               logmode = LOGMODE_SYSLOG | LOGMODE_STDIO;
+       }
+       /* else: -d "Debug", log is not redirected */
 
-       // goto configuration directory
-       xchdir(opt_conf);
+       parse_conf_file(opt_action);
+       parse_map_file(opt_map);
 
-       // prevent zombies
-       signal(SIGCHLD, SIG_IGN);
+       xchdir(opt_dir);
 
-       // no explicit evdev files given? -> use proc event interface
-       if (!*argv) {
-               // proc_event file is just a "config" :)
-               char *token[4];
-               parser_t *parser = config_open(opt_input);
+       /* We spawn children but don't wait for them. Prevent zombies: */
+       bb_signals((1 << SIGCHLD), SIG_IGN);
+       // If you enable this, (1) explain why, (2)
+       // make sure while(poll) loop below is still interruptible
+       // by SIGTERM et al:
+       //bb_signals(BB_FATAL_SIGS, record_signo);
 
-               // dispatch events
-               while (config_read(parser, token, 4, 4, "\0 ", PARSE_NORMAL)) {
-                       char *event = xasprintf("%s/%s", token[1], token[2]);
-                       process_event(event);
-                       free(event);
-               }
+       pfd = NULL;
+       nfd = 0;
+       while (1) {
+               int fd;
+               char *dev_event;
 
-               if (ENABLE_FEATURE_CLEAN_UP)
-                       config_close(parser);
-               return EXIT_SUCCESS;
+               dev_event = xasprintf((opts & OPT_e) ? "%s" : "%s%u", opt_input, nfd);
+               fd = open(dev_event, O_RDONLY | O_NONBLOCK);
+               if (fd < 0) {
+                       if (nfd == 0)
+                               bb_simple_perror_msg_and_die(dev_event);
+                       break;
+               }
+               free(dev_event);
+               pfd = xrealloc_vector(pfd, 1, nfd);
+               pfd[nfd].fd = fd;
+               pfd[nfd].events = POLLIN;
+               nfd++;
        }
 
-       // evdev files given, use evdev interface
+       write_pidfile(opt_pidfile);
 
-       // open event devices
-       pfd = xzalloc(sizeof(*pfd) * argc);
-       nfd = 0;
-       while (*argv) {
-               pfd[nfd].fd = open_or_warn(*argv++, O_RDONLY | O_NONBLOCK);
-               if (pfd[nfd].fd >= 0)
-                       pfd[nfd++].events = POLLIN;
-       }
-
-       // dispatch events
-       while (/* !bb_got_signal && */ poll(pfd, nfd, -1) > 0) {
+       while (safe_poll(pfd, nfd, -1) > 0) {
+               int i;
                for (i = 0; i < nfd; i++) {
                        const char *event;
-                       struct input_event ev;
-
-                       if (!(pfd[i].revents & POLLIN))
-                               continue;
 
-                       if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev)))
-                               continue;
-//bb_info_msg("%d: %d %d %4d", i, ev.type, ev.code, ev.value);
+                       if (!(pfd[i].revents & POLLIN)) {
+                               if (pfd[i].revents == 0)
+                                       continue; /* this fd has nothing */
 
-                       // filter out unneeded events
-                       if (ev.value != 1)
-                               continue;
+                               /* Likely POLLERR, POLLHUP, POLLNVAL.
+                                * Do not listen on this fd anymore.
+                                */
+                               close(pfd[i].fd);
+                               nfd--;
+                               for (; i < nfd; i++)
+                                       pfd[i].fd = pfd[i + 1].fd;
+                               break; /* do poll() again */
+                       }
 
                        event = NULL;
+                       if (option_mask32 & OPT_e) {
+                               char *buf;
+                               int len;
 
-                       // N.B. we will conform to /proc/acpi/event
-                       // naming convention when assigning event names
+                               buf = xmalloc_reads(pfd[i].fd, NULL);
+                               /* buf = "button/power PWRB 00000080 00000000" */
+                               len = strlen(buf) - 9;
+                               if (len >= 0)
+                                       buf[len] = '\0';
+                               event = find_action(NULL, buf);
+                               free(buf);
+                       } else {
+                               struct input_event ev;
 
-                       // TODO: do we want other events?
+                               if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev)))
+                                       continue;
 
-                       // power and sleep buttons delivered as keys pressed
-                       if (EV_KEY == ev.type) {
-                               if (KEY_POWER == ev.code)
-                                       event = "PWRF/00000080";
-                               else if (KEY_SLEEP == ev.code)
-                                       event = "SLPB/00000080";
-                       }
-                       // switches
-                       else if (EV_SW == ev.type) {
-                               if (SW_LID == ev.code)
-                                       event = "LID/00000080";
-                               else if (SW_RFKILL_ALL == ev.code)
-                                       event = "RFKILL";
+                               if (ev.value != 1 && ev.value != 0)
+                                       continue;
+
+                               event = find_action(&ev, NULL);
                        }
-                       // filter out unneeded events
                        if (!event)
                                continue;
-
-                       // spawn event handler
+                       /* spawn event handler */
                        process_event(event);
                }
        }
 
        if (ENABLE_FEATURE_CLEAN_UP) {
-               for (i = 0; i < nfd; i++)
-                       close(pfd[i].fd);
+               while (nfd--)
+                       close(pfd[nfd].fd);
                free(pfd);
        }
+       remove_pidfile(opt_pidfile);
 
        return EXIT_SUCCESS;
 }
index ec699d1..1bbc803 100644 (file)
@@ -4,15 +4,28 @@
  *
  * Copyright (C) 2008 Denys Vlasenko.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#define blkid_trivial_usage
+//usage:       "[BLOCKDEV]..."
+//usage:#define blkid_full_usage "\n\n"
+//usage:       "Print UUIDs of all filesystems"
+
 #include "libbb.h"
 #include "volume_id.h"
 
 int blkid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int blkid_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+int blkid_main(int argc UNUSED_PARAM, char **argv)
 {
-       display_uuid_cache();
+       int scan_devices = 1;
+
+       while (*++argv) {
+               /* Note: bogus device names don't cause any error messages */
+               add_to_uuid_cache(*argv);
+               scan_devices = 0;
+       }
+
+       display_uuid_cache(scan_devices);
        return 0;
 }
diff --git a/util-linux/blockdev.c b/util-linux/blockdev.c
new file mode 100644 (file)
index 0000000..e25e529
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * blockdev implementation for busybox
+ *
+ * Copyright (C) 2010 Sergey Naumov <sknaumov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//applet:IF_BLOCKDEV(APPLET(blockdev, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_BLOCKDEV) += blockdev.o
+
+//config:config BLOCKDEV
+//config:      bool "blockdev"
+//config:      default y
+//config:      help
+//config:        Performs some ioctls with block devices.
+
+//usage:#define blockdev_trivial_usage
+//usage:       "OPTION BLOCKDEV"
+//usage:#define blockdev_full_usage "\n\n"
+//usage:       "       --setro         Set ro"
+//usage:     "\n       --setrw         Set rw"
+//usage:     "\n       --getro         Get ro"
+//usage:     "\n       --getss         Get sector size"
+//usage:     "\n       --getbsz        Get block size"
+//usage:     "\n       --setbsz BYTES  Set block size"
+//usage:     "\n       --getsz         Get device size in 512-byte sectors"
+/*//usage:     "\n     --getsize       Get device size in sectors (deprecated)"*/
+//usage:     "\n       --getsize64     Get device size in bytes"
+//usage:     "\n       --flushbufs     Flush buffers"
+//usage:     "\n       --rereadpt      Reread partition table"
+
+
+#include "libbb.h"
+#include <linux/fs.h>
+
+enum {
+       ARG_NONE   = 0,
+       ARG_INT    = 1,
+       ARG_ULONG  = 2,
+       /* Yes, BLKGETSIZE64 takes pointer to uint64_t, not ullong! */
+       ARG_U64    = 3,
+       ARG_MASK   = 3,
+
+       FL_USRARG   = 4, /* argument is provided by user */
+       FL_NORESULT = 8,
+       FL_SCALE512 = 16,
+};
+
+struct bdc {
+       uint32_t   ioc;                       /* ioctl code */
+       const char name[sizeof("flushbufs")]; /* "--setfoo" wothout "--" */
+       uint8_t    flags;
+       int8_t     argval;                    /* default argument value */
+};
+
+static const struct bdc bdcommands[] = {
+       {
+               .ioc = BLKROSET,
+               .name = "setro",
+               .flags = ARG_INT + FL_NORESULT,
+               .argval = 1,
+       },{
+               .ioc = BLKROSET,
+               .name = "setrw",
+               .flags = ARG_INT + FL_NORESULT,
+               .argval = 0,
+       },{
+               .ioc = BLKROGET,
+               .name = "getro",
+               .flags = ARG_INT,
+               .argval = -1,
+       },{
+               .ioc = BLKSSZGET,
+               .name = "getss",
+               .flags = ARG_INT,
+               .argval = -1,
+       },{
+               .ioc = BLKBSZGET,
+               .name = "getbsz",
+               .flags = ARG_INT,
+               .argval = -1,
+       },{
+               .ioc = BLKBSZSET,
+               .name = "setbsz",
+               .flags = ARG_INT + FL_NORESULT + FL_USRARG,
+               .argval = 0,
+       },{
+               .ioc = BLKGETSIZE64,
+               .name = "getsz",
+               .flags = ARG_U64 + FL_SCALE512,
+               .argval = -1,
+       },{
+               .ioc = BLKGETSIZE,
+               .name = "getsize",
+               .flags = ARG_ULONG,
+               .argval = -1,
+       },{
+               .ioc = BLKGETSIZE64,
+               .name = "getsize64",
+               .flags = ARG_U64,
+               .argval = -1,
+       },{
+               .ioc = BLKFLSBUF,
+               .name = "flushbufs",
+               .flags = ARG_NONE + FL_NORESULT,
+               .argval = 0,
+       },{
+               .ioc = BLKRRPART,
+               .name = "rereadpt",
+               .flags = ARG_NONE + FL_NORESULT,
+               .argval = 0,
+       }
+};
+
+static const struct bdc *find_cmd(const char *s)
+{
+       const struct bdc *bdcmd = bdcommands;
+       if (s[0] == '-' && s[1] == '-') {
+               s += 2;
+               do {
+                       if (strcmp(s, bdcmd->name) == 0)
+                               return bdcmd;
+                       bdcmd++;
+               } while (bdcmd != bdcommands + ARRAY_SIZE(bdcommands));
+       }
+       bb_show_usage();
+}
+
+int blockdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int blockdev_main(int argc UNUSED_PARAM, char **argv)
+{
+       const struct bdc *bdcmd;
+       int fd;
+       uint64_t u64;
+       union {
+               int i;
+               unsigned long lu;
+               uint64_t u64;
+       } ioctl_val_on_stack;
+
+       argv++;
+       if (!argv[0] || !argv[1]) /* must have at least 2 args */
+               bb_show_usage();
+
+       bdcmd = find_cmd(*argv);
+
+       u64 = (int)bdcmd->argval;
+       if (bdcmd->flags & FL_USRARG)
+               u64 = xatoi_positive(*++argv);
+
+       argv++;
+       if (!argv[0] || argv[1])
+               bb_show_usage();
+       fd = xopen(argv[0], O_RDONLY);
+
+       ioctl_val_on_stack.u64 = u64;
+#if BB_BIG_ENDIAN
+       /* Store data properly wrt data size.
+        * (1) It's no-op for little-endian.
+        * (2) it's no-op for 0 and -1. Only --setro uses arg != 0 and != -1,
+        * and it is ARG_INT. --setbsz USER_VAL is also ARG_INT.
+        * Thus, we don't need to handle ARG_ULONG.
+        */
+       switch (bdcmd->flags & ARG_MASK) {
+       case ARG_INT:
+               ioctl_val_on_stack.i = (int)u64;
+               break;
+# if 0 /* unused */
+       case ARG_ULONG:
+               ioctl_val_on_stack.lu = (unsigned long)u64;
+               break;
+# endif
+       }
+#endif
+
+       if (ioctl(fd, bdcmd->ioc, &ioctl_val_on_stack.u64) == -1)
+               bb_simple_perror_msg_and_die(*argv);
+
+       /* Fetch it into register(s) */
+       u64 = ioctl_val_on_stack.u64;
+
+       if (bdcmd->flags & FL_SCALE512)
+               u64 >>= 9;
+
+       /* Zero- or one-extend the value if needed, then print */
+       switch (bdcmd->flags & (ARG_MASK+FL_NORESULT)) {
+       case ARG_INT:
+               /* Smaller code when we use long long
+                * (gcc tail-merges printf call)
+                */
+               printf("%lld\n", (long long)(int)u64);
+               break;
+       case ARG_ULONG:
+               u64 = (unsigned long)u64;
+               /* FALLTHROUGH */
+       case ARG_U64:
+               printf("%llu\n", (unsigned long long)u64);
+               break;
+       }
+
+       if (ENABLE_FEATURE_CLEAN_UP)
+               close(fd);
+       return EXIT_SUCCESS;
+}
index b0dc592..81ba1c9 100644 (file)
@@ -6,8 +6,17 @@
  * Copyright 2006 Rob Landley <rob@landley.net>
  * Copyright 2006 Bernhard Reutner-Fischer <rep.nop@aon.at>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define dmesg_trivial_usage
+//usage:       "[-c] [-n LEVEL] [-s SIZE]"
+//usage:#define dmesg_full_usage "\n\n"
+//usage:       "Print or control the kernel ring buffer\n"
+//usage:     "\n       -c              Clear ring buffer after printing"
+//usage:     "\n       -n LEVEL        Set console logging level"
+//usage:     "\n       -s SIZE         Buffer size"
+
 #include <sys/klog.h>
 #include "libbb.h"
 
@@ -45,20 +54,24 @@ int dmesg_main(int argc UNUSED_PARAM, char **argv)
        if (len == 0)
                return EXIT_SUCCESS;
 
-       /* Skip <#> at the start of lines, and make sure we end with a newline */
 
        if (ENABLE_FEATURE_DMESG_PRETTY) {
                int last = '\n';
                int in = 0;
 
-               do {
-                       if (last == '\n' && buf[in] == '<')
-                               in += 3;
-                       else {
+               /* Skip <[0-9]+> at the start of lines */
+               while (1) {
+                       if (last == '\n' && buf[in] == '<') {
+                               while (buf[in++] != '>' && in < len)
+                                       ;
+                       } else {
                                last = buf[in++];
-                               bb_putchar(last);
+                               putchar(last);
                        }
-               } while (in < len);
+                       if (in >= len)
+                               break;
+               }
+               /* Make sure we end with a newline */
                if (last != '\n')
                        bb_putchar('\n');
        } else {
index 2769a37..e9aacce 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  * This is a from-scratch implementation of fbset; but the de facto fbset
  * implementation was a good reference. fbset (original) is released under
  *     Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be)
  */
 
+//usage:#define fbset_trivial_usage
+//usage:       "[OPTIONS] [MODE]"
+//usage:#define fbset_full_usage "\n\n"
+//usage:       "Show and modify frame buffer settings"
+//usage:
+//usage:#define fbset_example_usage
+//usage:       "$ fbset\n"
+//usage:       "mode \"1024x768-76\"\n"
+//usage:       "       # D: 78.653 MHz, H: 59.949 kHz, V: 75.694 Hz\n"
+//usage:       "       geometry 1024 768 1024 768 16\n"
+//usage:       "       timings 12714 128 32 16 4 128 4\n"
+//usage:       "       accel false\n"
+//usage:       "       rgba 5/11,6/5,5/0,0/0\n"
+//usage:       "endmode\n"
+
 #include "libbb.h"
 
 #define DEFAULTFBDEV  FB_0
@@ -26,7 +41,7 @@ enum {
 
 struct fb_bitfield {
        uint32_t offset;                /* beginning of bitfield */
-       uint32_t length;                /* length of bitfield */
+       uint32_t length;                /* length of bitfield */
        uint32_t msb_right;             /* !=0: Most significant bit is right */
 };
 struct fb_var_screeninfo {
@@ -52,7 +67,7 @@ struct fb_var_screeninfo {
        uint32_t height;                /* height of picture in mm */
        uint32_t width;                 /* width of picture in mm */
 
-       uint32_t accel_flags;           /* acceleration flags (hints) */
+       uint32_t accel_flags;           /* acceleration flags (hints) */
 
        /* Timing: All values in pixclocks, except pixclock (of course) */
        uint32_t pixclock;              /* pixel clock in ps (pico seconds) */
@@ -256,7 +271,7 @@ static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
                }
                p = token[1];
                i = index_in_strings(
-                       "geometry\0timings\0interlaced\0double\0vsync\0hsync\0csync\0extsync\0",
+                       "geometry\0timings\0interlaced\0double\0vsync\0hsync\0csync\0extsync\0rgba\0",
                        token[0]);
                switch (i) {
                case 0:
@@ -317,7 +332,7 @@ static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
                }
                case 4:
                case 5:
-               case 6: {
+               case 6: {
                        static const uint32_t syncs[] = {FB_SYNC_VERT_HIGH_ACT, FB_SYNC_HOR_HIGH_ACT, FB_SYNC_COMP_HIGH_ACT};
                        ss(&base->sync, syncs[i-4], p, "low");
 //bb_info_msg("SYNC[%s]", p);
@@ -327,6 +342,30 @@ static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
                        ss(&base->sync, FB_SYNC_EXT, p, "false");
 //bb_info_msg("EXTSYNC[%s]", p);
                        break;
+               case 8: {
+                       int red_offset, red_length;
+                       int green_offset, green_length;
+                       int blue_offset, blue_length;
+                       int transp_offset, transp_length;
+
+                       sscanf(p, "%d/%d,%d/%d,%d/%d,%d/%d",
+                               &red_offset, &red_length,
+                               &green_offset, &green_length,
+                               &blue_offset, &blue_length,
+                               &transp_offset, &transp_length);
+                       base->red.offset = red_offset;
+                       base->red.length = red_length;
+                       base->red.msb_right = 0;
+                       base->green.offset = green_offset;
+                       base->green.length = green_length;
+                       base->green.msb_right = 0;
+                       base->blue.offset = blue_offset;
+                       base->blue.length = blue_length;
+                       base->blue.msb_right = 0;
+                       base->transp.offset = transp_offset;
+                       base->transp.length = transp_length;
+                       base->transp.msb_right = 0;
+               }
                }
        }
        return 0;
@@ -370,7 +409,7 @@ int fbset_main(int argc, char **argv)
                OPT_CHANGE   = (1 << 0),
                OPT_SHOW     = (1 << 1),
                OPT_READMODE = (1 << 2),
-               OPT_ALL      = (1 << 9),
+               OPT_ALL      = (1 << 3),
        };
        struct fb_var_screeninfo var_old, var_set;
        int fh, i;
@@ -387,7 +426,14 @@ int fbset_main(int argc, char **argv)
        argv++;
        argc--;
        for (; argc > 0 && (thisarg = *argv) != NULL; argc--, argv++) {
-               if (thisarg[0] == '-') for (i = 0; i < ARRAY_SIZE(g_cmdoptions); i++) {
+               if (thisarg[0] != '-') {
+                       if (!ENABLE_FEATURE_FBSET_READMODE || argc != 1)
+                               bb_show_usage();
+                       mode = thisarg;
+                       options |= OPT_READMODE;
+                       goto contin;
+               }
+               for (i = 0; i < ARRAY_SIZE(g_cmdoptions); i++) {
                        if (strcmp(thisarg + 1, g_cmdoptions[i].name) != 0)
                                continue;
                        if (argc <= g_cmdoptions[i].param_count)
@@ -456,10 +502,7 @@ int fbset_main(int argc, char **argv)
                        argv += g_cmdoptions[i].param_count;
                        goto contin;
                }
-               if (!ENABLE_FEATURE_FBSET_READMODE || argc != 1)
-                       bb_show_usage();
-               mode = *argv;
-               options |= OPT_READMODE;
+               bb_show_usage();
  contin: ;
        }
 
@@ -471,6 +514,7 @@ int fbset_main(int argc, char **argv)
                if (!read_mode_db(&var_old, modefile, mode)) {
                        bb_error_msg_and_die("unknown video mode '%s'", mode);
                }
+               options |= OPT_CHANGE;
 #endif
        }
 
index 3831ab4..6f49cec 100644 (file)
@@ -2,9 +2,15 @@
 /* fdformat.c  -  Low-level formats a floppy disk - Werner Almesberger
  * 5 July 2003 -- modified for Busybox by Erik Andersen
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#define fdformat_trivial_usage
+//usage:       "[-n] DEVICE"
+//usage:#define fdformat_full_usage "\n\n"
+//usage:       "Format floppy disk\n"
+//usage:     "\n       -n      Don't verify after format"
+
 #include "libbb.h"
 
 
@@ -36,7 +42,7 @@ struct format_descr {
        unsigned int device,head,track;
 };
 #define FDFMTBEG _IO(2,0x47)
-#define        FDFMTTRK _IOW(2,0x48, struct format_descr)
+#define FDFMTTRK _IOW(2,0x48, struct format_descr)
 #define FDFMTEND _IO(2,0x49)
 #define FDGETPRM _IOR(2, 0x04, struct floppy_struct)
 #define FD_FILL_BYTE 0xF6 /* format fill byte. */
@@ -66,7 +72,7 @@ int fdformat_main(int argc UNUSED_PARAM, char **argv)
        /* original message was: "Could not determine current format type" */
        xioctl(fd, FDGETPRM, &param);
 
-       printf("%s-sided, %d tracks, %d sec/track. Total capacity %d kB\n",
+       printf("%s-sided, %u tracks, %u sec/track. Total capacity %d kB\n",
                (param.head == 2) ? "Double" : "Single",
                param.track, param.sect, param.size >> 1);
 
@@ -110,7 +116,7 @@ int fdformat_main(int argc UNUSED_PARAM, char **argv)
                        /* Check backwards so we don't need a counter */
                        while (--read_bytes >= 0) {
                                if (data[read_bytes] != FD_FILL_BYTE) {
-                                        printf("bad data in cyl %d\nContinuing... ", cyl);
+                                       printf("bad data in cyl %d\nContinuing... ", cyl);
                                }
                        }
                }
index aa718c7..39eb27b 100644 (file)
@@ -4,9 +4,30 @@
  * Copyright (C) 1992  A. V. Le Blanc (LeBlanc@mcc.ac.uk)
  * Copyright (C) 2001,2002 Vladimir Oleynik <dzo@simtreas.ru> (initial bb port)
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+/* Looks like someone forgot to add this to config system */
+//usage:#ifndef ENABLE_FEATURE_FDISK_BLKSIZE
+//usage:# define ENABLE_FEATURE_FDISK_BLKSIZE 0
+//usage:# define IF_FEATURE_FDISK_BLKSIZE(a)
+//usage:#endif
+//usage:
+//usage:#define fdisk_trivial_usage
+//usage:       "[-ul" IF_FEATURE_FDISK_BLKSIZE("s") "] "
+//usage:       "[-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SSZ] DISK"
+//usage:#define fdisk_full_usage "\n\n"
+//usage:       "Change partition table\n"
+//usage:     "\n       -u              Start and End are in sectors (instead of cylinders)"
+//usage:     "\n       -l              Show partition table for each DISK, then exit"
+//usage:       IF_FEATURE_FDISK_BLKSIZE(
+//usage:     "\n       -s              Show partition sizes in kb for each DISK, then exit"
+//usage:       )
+//usage:     "\n       -b 2048         (for certain MO disks) use 2048-byte sectors"
+//usage:     "\n       -C CYLINDERS    Set number of cylinders/heads/sectors"
+//usage:     "\n       -H HEADS"
+//usage:     "\n       -S SECTORS"
+
 #ifndef _LARGEFILE64_SOURCE
 /* For lseek64 */
 # define _LARGEFILE64_SOURCE
@@ -107,12 +128,30 @@ struct partition {
        unsigned char size4[4];         /* nr of sectors in partition */
 } PACKED;
 
+/*
+ * per partition table entry data
+ *
+ * The four primary partitions have the same sectorbuffer (MBRbuffer)
+ * and have NULL ext_pointer.
+ * Each logical partition table entry has two pointers, one for the
+ * partition and one link to the next one.
+ */
+struct pte {
+       struct partition *part_table;   /* points into sectorbuffer */
+       struct partition *ext_pointer;  /* points into sectorbuffer */
+       sector_t offset_from_dev_start; /* disk sector number */
+       char *sectorbuffer;             /* disk sector contents */
+#if ENABLE_FEATURE_FDISK_WRITABLE
+       char changed;                   /* boolean */
+#endif
+};
+
 #define unable_to_open "can't open '%s'"
 #define unable_to_read "can't read from %s"
 #define unable_to_seek "can't seek on %s"
 
 enum label_type {
-       LABEL_DOS, LABEL_SUN, LABEL_SGI, LABEL_AIX, LABEL_OSF
+       LABEL_DOS, LABEL_SUN, LABEL_SGI, LABEL_AIX, LABEL_OSF, LABEL_GPT
 };
 
 #define LABEL_IS_DOS   (LABEL_DOS == current_label_type)
@@ -149,6 +188,14 @@ enum label_type {
 #define STATIC_OSF extern
 #endif
 
+#if ENABLE_FEATURE_GPT_LABEL
+#define LABEL_IS_GPT   (LABEL_GPT == current_label_type)
+#define STATIC_GPT static
+#else
+#define LABEL_IS_GPT   0
+#define STATIC_GPT extern
+#endif
+
 enum action { OPEN_MAIN, TRY_ONLY, CREATE_EMPTY_DOS, CREATE_EMPTY_SUN };
 
 static void update_units(void);
@@ -162,6 +209,7 @@ static sector_t read_int(sector_t low, sector_t dflt, sector_t high, sector_t ba
 #endif
 static const char *partition_type(unsigned char type);
 static void get_geometry(void);
+static void read_pte(struct pte *pe, sector_t offset);
 #if ENABLE_FEATURE_SUN_LABEL || ENABLE_FEATURE_FDISK_WRITABLE
 static int get_boot(enum action what);
 #else
@@ -174,24 +222,6 @@ static int get_boot(void);
 static sector_t get_start_sect(const struct partition *p);
 static sector_t get_nr_sects(const struct partition *p);
 
-/*
- * per partition table entry data
- *
- * The four primary partitions have the same sectorbuffer (MBRbuffer)
- * and have NULL ext_pointer.
- * Each logical partition table entry has two pointers, one for the
- * partition and one link to the next one.
- */
-struct pte {
-       struct partition *part_table;   /* points into sectorbuffer */
-       struct partition *ext_pointer;  /* points into sectorbuffer */
-       sector_t offset_from_dev_start; /* disk sector number */
-       char *sectorbuffer;             /* disk sector contents */
-#if ENABLE_FEATURE_FDISK_WRITABLE
-       char changed;                   /* boolean */
-#endif
-};
-
 /* DOS partition types */
 
 static const char *const i386_sys_types[] = {
@@ -539,7 +569,7 @@ read_line(const char *prompt)
 {
        int sz;
 
-       sz = read_line_input(prompt, line_buffer, sizeof(line_buffer), NULL);
+       sz = read_line_input(NULL, prompt, line_buffer, sizeof(line_buffer), /*timeout*/ -1);
        if (sz <= 0)
                exit(EXIT_SUCCESS); /* Ctrl-D or Ctrl-C */
 
@@ -653,6 +683,9 @@ STATIC_OSF void bsd_select(void);
 STATIC_OSF void xbsd_print_disklabel(int);
 #include "fdisk_osf.c"
 
+STATIC_GPT void gpt_list_table(int xtra);
+#include "fdisk_gpt.c"
+
 #if ENABLE_FEATURE_SGI_LABEL || ENABLE_FEATURE_SUN_LABEL
 static uint16_t
 fdisk_swap16(uint16_t x)
@@ -833,6 +866,11 @@ menu(void)
                puts("o\tcreate a new empty DOS partition table");
                puts("q\tquit without saving changes");
                puts("s\tcreate a new empty Sun disklabel");  /* sun */
+       } else if (LABEL_IS_GPT) {
+               puts("o\tcreate a new empty DOS partition table");
+               puts("p\tprint the partition table");
+               puts("q\tquit without saving changes");
+               puts("s\tcreate a new empty Sun disklabel");  /* sun */
        } else {
                puts("a\ttoggle a bootable flag");
                puts("b\tedit bsd disklabel");
@@ -1308,7 +1346,18 @@ get_geometry(void)
 
 /*
  * Opens disk_device and optionally reads MBR.
- *    FIXME: document what each 'what' value will do!
+ *    If what == OPEN_MAIN:
+ *      Open device, read MBR.  Abort program on short read.  Create empty
+ *      disklabel if the on-disk structure is invalid (WRITABLE mode).
+ *    If what == TRY_ONLY:
+ *      Open device, read MBR.  Return an error if anything is out of place.
+ *      Do not create an empty disklabel.  This is used for the "list"
+ *      operations: "fdisk -l /dev/sda" and "fdisk -l" (all devices).
+ *    If what == CREATE_EMPTY_*:
+ *      This means that get_boot() was called recursively from create_*label().
+ *      Do not re-open the device; just set up the ptes array and print
+ *      geometry warnings.
+ *
  * Returns:
  *   -1: no 0xaa55 flag present (possibly entire disk BSD)
  *    0: found or created label
@@ -1390,6 +1439,10 @@ static int get_boot(void)
        if (check_aix_label())
                return 0;
 #endif
+#if ENABLE_FEATURE_GPT_LABEL
+       if (check_gpt_label())
+               return 0;
+#endif
 #if ENABLE_FEATURE_OSF_LABEL
        if (check_osf_label()) {
                possibly_osf_label = 1;
@@ -1409,7 +1462,7 @@ static int get_boot(void)
        if (!valid_part_table_flag(MBRbuffer)) {
                if (what == OPEN_MAIN) {
                        printf("Device contains neither a valid DOS "
-                                 "partition table, nor Sun, SGI or OSF "
+                                 "partition table, nor Sun, SGI, OSF or GPT "
                                  "disklabel\n");
 #ifdef __sparc__
                        IF_FEATURE_SUN_LABEL(create_sunlabel();)
@@ -2043,7 +2096,6 @@ fix_partition_table_order(void)
                fix_chain_of_logicals();
 
        printf("Done.\n");
-
 }
 #endif
 
@@ -2057,10 +2109,14 @@ list_table(int xtra)
                sun_list_table(xtra);
                return;
        }
-       if (LABEL_IS_SUN) {
+       if (LABEL_IS_SGI) {
                sgi_list_table(xtra);
                return;
        }
+       if (LABEL_IS_GPT) {
+               gpt_list_table(xtra);
+               return;
+       }
 
        list_disk_geometry();
 
@@ -2487,6 +2543,35 @@ new_partition(void)
 }
 
 static void
+reread_partition_table(int leave)
+{
+       int i;
+
+       printf("Calling ioctl() to re-read partition table\n");
+       sync();
+       /* Users with slow external USB disks on a 320MHz ARM system (year 2011)
+        * report that sleep is needed, otherwise BLKRRPART may fail with -EIO:
+        */
+       sleep(1);
+       i = ioctl_or_perror(dev_fd, BLKRRPART, NULL,
+                       "WARNING: rereading partition table "
+                       "failed, kernel still uses old table");
+#if 0
+       if (dos_changed)
+               printf(
+               "\nWARNING: If you have created or modified any DOS 6.x\n"
+               "partitions, please see the fdisk manual page for additional\n"
+               "information\n");
+#endif
+
+       if (leave) {
+               if (ENABLE_FEATURE_CLEAN_UP)
+                       close_dev_fd();
+               exit(i != 0);
+       }
+}
+
+static void
 write_table(void)
 {
        int i;
@@ -2497,7 +2582,6 @@ write_table(void)
                                ptes[3].changed = 1;
                for (i = 3; i < g_partitions; i++) {
                        struct pte *pe = &ptes[i];
-
                        if (pe->changed) {
                                write_part_table_flag(pe->sectorbuffer);
                                write_sector(pe->offset_from_dev_start, pe->sectorbuffer);
@@ -2509,44 +2593,17 @@ write_table(void)
                sgi_write_table();
        }
        else if (LABEL_IS_SUN) {
-               int needw = 0;
-
-               for (i = 0; i < 8; i++)
-                       if (ptes[i].changed)
-                               needw = 1;
-               if (needw)
-                       sun_write_table();
+               for (i = 0; i < 8; i++) {
+                       if (ptes[i].changed) {
+                               sun_write_table();
+                               break;
+                       }
+               }
        }
 
-       printf("The partition table has been altered!\n\n");
+       printf("The partition table has been altered.\n");
        reread_partition_table(1);
 }
-
-static void
-reread_partition_table(int leave)
-{
-       int i;
-
-       printf("Calling ioctl() to re-read partition table\n");
-       sync();
-       /* sleep(2); Huh? */
-       i = ioctl_or_perror(dev_fd, BLKRRPART, NULL,
-                       "WARNING: rereading partition table "
-                       "failed, kernel still uses old table");
-#if 0
-       if (dos_changed)
-               printf(
-               "\nWARNING: If you have created or modified any DOS 6.x\n"
-               "partitions, please see the fdisk manual page for additional\n"
-               "information\n");
-#endif
-
-       if (leave) {
-               if (ENABLE_FEATURE_CLEAN_UP)
-                       close_dev_fd();
-               exit(i != 0);
-       }
-}
 #endif /* FEATURE_FDISK_WRITABLE */
 
 #if ENABLE_FEATURE_FDISK_ADVANCED
@@ -2789,13 +2846,37 @@ open_list_and_close(const char *device, int user_specified)
        close_dev_fd();
 }
 
+/* Is it a whole disk? The digit check is still useful
+   for Xen devices for example. */
+static int is_whole_disk(const char *disk)
+{
+       unsigned len;
+       int fd = open(disk, O_RDONLY);
+
+       if (fd != -1) {
+               struct hd_geometry geometry;
+               int err = ioctl(fd, HDIO_GETGEO, &geometry);
+               close(fd);
+               if (!err)
+                       return (geometry.start == 0);
+       }
+
+       /* Treat "nameN" as a partition name, not whole disk */
+       /* note: mmcblk0 should work from the geometry check above */
+       len = strlen(disk);
+       if (len != 0 && isdigit(disk[len - 1]))
+               return 0;
+
+       return 1;
+}
+
 /* for fdisk -l: try all things in /proc/partitions
    that look like a partition name (do not end in a digit) */
 static void
 list_devs_in_proc_partititons(void)
 {
        FILE *procpt;
-       char line[100], ptname[100], devname[120], *s;
+       char line[100], ptname[100], devname[120];
        int ma, mi, sz;
 
        procpt = fopen_or_warn("/proc/partitions", "r");
@@ -2804,13 +2885,10 @@ list_devs_in_proc_partititons(void)
                if (sscanf(line, " %u %u %u %[^\n ]",
                                &ma, &mi, &sz, ptname) != 4)
                        continue;
-               for (s = ptname; *s; s++)
-                       continue;
-               /* note: excluding '0': e.g. mmcblk0 is not a partition name! */
-               if (s[-1] >= '1' && s[-1] <= '9')
-                       continue;
+
                sprintf(devname, "/dev/%s", ptname);
-               open_list_and_close(devname, 0);
+               if (is_whole_disk(devname))
+                       open_list_and_close(devname, 0);
        }
 #if ENABLE_FEATURE_CLEAN_UP
        fclose(procpt);
@@ -2945,7 +3023,7 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv)
                                printf("\nThe current boot file is: %s\n",
                                        sgi_get_bootfile());
                                if (read_maybe_empty("Please enter the name of the "
-                                                  "new boot file: ") == '\n')
+                                               "new boot file: ") == '\n')
                                        printf("Boot file unchanged\n");
                                else
                                        sgi_set_bootfile(line_ptr);
@@ -3023,7 +3101,7 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv)
                        verify();
                        break;
                case 'w':
-                       write_table();          /* does not return */
+                       write_table();  /* does not return */
                        break;
 #if ENABLE_FEATURE_FDISK_ADVANCED
                case 'x':
index 2a0ab17..ee5df50 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) Andreas Neuper, Sep 1998.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 typedef struct {
diff --git a/util-linux/fdisk_gpt.c b/util-linux/fdisk_gpt.c
new file mode 100644 (file)
index 0000000..5786d5f
--- /dev/null
@@ -0,0 +1,193 @@
+#if ENABLE_FEATURE_GPT_LABEL
+/*
+ * Copyright (C) 2010 Kevin Cernekee <cernekee@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#define GPT_MAGIC 0x5452415020494645ULL
+enum {
+       LEGACY_GPT_TYPE = 0xee,
+       GPT_MAX_PARTS   = 256,
+       GPT_MAX_PART_ENTRY_LEN = 4096,
+       GUID_LEN        = 16,
+};
+
+typedef struct {
+       uint64_t magic;
+       uint32_t revision;
+       uint32_t hdr_size;
+       uint32_t hdr_crc32;
+       uint32_t reserved;
+       uint64_t current_lba;
+       uint64_t backup_lba;
+       uint64_t first_usable_lba;
+       uint64_t last_usable_lba;
+       uint8_t  disk_guid[GUID_LEN];
+       uint64_t first_part_lba;
+       uint32_t n_parts;
+       uint32_t part_entry_len;
+       uint32_t part_array_crc32;
+} gpt_header;
+
+typedef struct {
+       uint8_t  type_guid[GUID_LEN];
+       uint8_t  part_guid[GUID_LEN];
+       uint64_t lba_start;
+       uint64_t lba_end;
+       uint64_t flags;
+       uint16_t name[36];
+} gpt_partition;
+
+static gpt_header *gpt_hdr;
+
+static char *part_array;
+static unsigned int n_parts;
+static unsigned int part_array_len;
+static unsigned int part_entry_len;
+
+static inline gpt_partition *
+gpt_part(int i)
+{
+       if (i >= n_parts) {
+               return NULL;
+       }
+       return (gpt_partition *)&part_array[i * part_entry_len];
+}
+
+static uint32_t
+gpt_crc32(void *buf, int len)
+{
+       return ~crc32_block_endian0(0xffffffff, buf, len, global_crc32_table);
+}
+
+static void
+gpt_print_guid(uint8_t *buf)
+{
+       printf(
+               "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+               buf[3], buf[2], buf[1], buf[0],
+               buf[5], buf[4],
+               buf[7], buf[6],
+               buf[8], buf[9],
+               buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
+}
+
+/* TODO: real unicode support */
+static void
+gpt_print_wide(uint16_t *s, int max_len)
+{
+       int i = 0;
+
+       while (i < max_len) {
+               if (*s == 0)
+                       return;
+               fputc(*s, stdout);
+               s++;
+       }
+}
+
+static void
+gpt_list_table(int xtra UNUSED_PARAM)
+{
+       int i;
+       char numstr6[6];
+
+       smart_ulltoa5(total_number_of_sectors * sector_size, numstr6, " KMGTPEZY")[0] = '\0';
+       printf("Disk %s: %llu sectors, %s\n", disk_device,
+               (unsigned long long)total_number_of_sectors,
+               numstr6);
+       printf("Logical sector size: %u\n", sector_size);
+       printf("Disk identifier (GUID): ");
+       gpt_print_guid(gpt_hdr->disk_guid);
+       printf("\nPartition table holds up to %u entries\n",
+               (int)SWAP_LE32(gpt_hdr->n_parts));
+       printf("First usable sector is %llu, last usable sector is %llu\n\n",
+               (unsigned long long)SWAP_LE64(gpt_hdr->first_usable_lba),
+               (unsigned long long)SWAP_LE64(gpt_hdr->last_usable_lba));
+
+       printf("Number  Start (sector)    End (sector)  Size       Code  Name\n");
+       for (i = 0; i < n_parts; i++) {
+               gpt_partition *p = gpt_part(i);
+               if (p->lba_start) {
+                       smart_ulltoa5((1 + SWAP_LE64(p->lba_end) - SWAP_LE64(p->lba_start)) * sector_size,
+                               numstr6, " KMGTPEZY")[0] = '\0';
+                       printf("%4u %15llu %15llu %11s   %04x  ",
+                               i + 1,
+                               (unsigned long long)SWAP_LE64(p->lba_start),
+                               (unsigned long long)SWAP_LE64(p->lba_end),
+                               numstr6,
+                               0x0700 /* FIXME */);
+                       gpt_print_wide(p->name, 18);
+                       printf("\n");
+               }
+       }
+}
+
+static int
+check_gpt_label(void)
+{
+       struct partition *first = pt_offset(MBRbuffer, 0);
+       struct pte pe;
+       uint32_t crc;
+
+       /* LBA 0 contains the legacy MBR */
+
+       if (!valid_part_table_flag(MBRbuffer)
+        || first->sys_ind != LEGACY_GPT_TYPE
+       ) {
+               current_label_type = 0;
+               return 0;
+       }
+
+       /* LBA 1 contains the GPT header */
+
+       read_pte(&pe, 1);
+       gpt_hdr = (void *)pe.sectorbuffer;
+
+       if (gpt_hdr->magic != SWAP_LE64(GPT_MAGIC)) {
+               current_label_type = 0;
+               return 0;
+       }
+
+       if (!global_crc32_table) {
+               global_crc32_table = crc32_filltable(NULL, 0);
+       }
+
+       crc = SWAP_LE32(gpt_hdr->hdr_crc32);
+       gpt_hdr->hdr_crc32 = 0;
+       if (gpt_crc32(gpt_hdr, SWAP_LE32(gpt_hdr->hdr_size)) != crc) {
+               /* FIXME: read the backup table */
+               puts("\nwarning: GPT header CRC is invalid\n");
+       }
+
+       n_parts = SWAP_LE32(gpt_hdr->n_parts);
+       part_entry_len = SWAP_LE32(gpt_hdr->part_entry_len);
+       if (n_parts > GPT_MAX_PARTS
+        || part_entry_len > GPT_MAX_PART_ENTRY_LEN
+        || SWAP_LE32(gpt_hdr->hdr_size) > sector_size
+       ) {
+               puts("\nwarning: unable to parse GPT disklabel\n");
+               current_label_type = 0;
+               return 0;
+       }
+
+       part_array_len = n_parts * part_entry_len;
+       part_array = xmalloc(part_array_len);
+       seek_sector(SWAP_LE64(gpt_hdr->first_part_lba));
+       if (full_read(dev_fd, part_array, part_array_len) != part_array_len) {
+               fdisk_fatal(unable_to_read);
+       }
+
+       if (gpt_crc32(part_array, part_array_len) != gpt_hdr->part_array_crc32) {
+               /* FIXME: read the backup table */
+               puts("\nwarning: GPT array CRC is invalid\n");
+       }
+
+       puts("Found valid GPT with protective MBR; using GPT\n");
+
+       current_label_type = LABEL_GPT;
+       return 1;
+}
+
+#endif /* GPT_LABEL */
index b89a2b2..ff16389 100644 (file)
 
 #define BSD_LINUX_BOOTDIR "/usr/ucb/mdec"
 
-#if defined(i386) || defined(__sparc__) || defined(__arm__) \
- || defined(__m68k__) || defined(__mips__) || defined(__s390__) \
- || defined(__s390__) || defined(__s390x__) \
- || defined(__sh__) || defined(__x86_64__) || defined(__avr32__)
-# define BSD_LABELSECTOR   1
-# define BSD_LABELOFFSET   0
-#elif defined(__alpha__) || defined(__powerpc__) || defined(__ia64__) \
+#if defined(__alpha__) \
+ || defined(__powerpc__) \
+ || defined(__ia64__) \
  || defined(__hppa__)
 # define BSD_LABELSECTOR   0
 # define BSD_LABELOFFSET   64
 #else
-# error unknown architecture
+# define BSD_LABELSECTOR   1
+# define BSD_LABELOFFSET   0
 #endif
 
 #define BSD_BBSIZE        8192          /* size of boot area, with label */
@@ -901,8 +898,7 @@ xbsd_initlabel(struct partition *p)
        pp->p_fstype = BSD_FS_UNUSED;
 #else
        d->d_npartitions = 3;
-       pp = &d->d_partitions[2];             /* Partition C should be
-                                                  the whole disk */
+       pp = &d->d_partitions[2]; /* Partition C should be the whole disk */
        pp->p_offset = 0;
        pp->p_size   = d->d_secperunit;
        pp->p_fstype = BSD_FS_UNUSED;
@@ -938,7 +934,7 @@ xbsd_readlabel(struct partition *p)
                fdisk_fatal(unable_to_read);
 
        memmove(d, &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET],
-                  sizeof(struct xbsd_disklabel));
+                       sizeof(struct xbsd_disklabel));
 
        if (d->d_magic != BSD_DISKMAGIC || d->d_magic2 != BSD_DISKMAGIC)
                return 0;
index 0e3cff5..785fc66 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) Andreas Neuper, Sep 1998.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #if ENABLE_FEATURE_SGI_LABEL
index 463082f..e32740d 100644 (file)
@@ -9,7 +9,7 @@
  * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
  *      Internationalization
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 #if ENABLE_FEATURE_SUN_LABEL
@@ -348,6 +348,7 @@ create_sunlabel(void)
 
        set_all_unchanged();
        set_changed(0);
+       check_sun_label();
        get_boot(CREATE_EMPTY_SUN);
 }
 
@@ -497,11 +498,14 @@ add_sun_partition(int n, int sys)
                else
                        first = read_int(scround(start), scround(stop)+1,
                                         scround(stop), 0, mesg);
-               if (display_in_cyl_units)
+               if (display_in_cyl_units) {
                        first *= units_per_sector;
-               else
+               } else {
                        /* Starting sector has to be properly aligned */
-                       first = (first + g_heads * g_sectors - 1) / (g_heads * g_sectors);
+                       first = (first + g_heads * g_sectors - 1) /
+                               (g_heads * g_sectors);
+                       first *= g_heads * g_sectors;
+               }
                if (n == 2 && first != 0)
                        printf("\
 It is highly recommended that the third partition covers the whole disk\n\
@@ -654,7 +658,7 @@ sun_list_table(int xtra)
                        uint32_t start = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * g_heads * g_sectors;
                        uint32_t len = SUN_SSWAP32(sunlabel->partitions[i].num_sectors);
                        printf("%s %c%c %9lu %9lu %9lu%c  %2x  %s\n",
-                               partname(disk_device, i+1, w),                  /* device */
+                               partname(disk_device, i+1, w),                  /* device */
                                (sunlabel->infos[i].flags & 0x01) ? 'u' : ' ',  /* flags */
                                (sunlabel->infos[i].flags & 0x10) ? 'r' : ' ',
                                (long) scround(start),                          /* start */
index 1e9c687..49e8979 100644 (file)
@@ -5,9 +5,17 @@
  * Copyright (C) 2006 by Jason Schoon <floydpink@gmail.com>
  * Some portions cribbed from e2fsprogs, util-linux, dosfstools
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define findfs_trivial_usage
+//usage:       "LABEL=label or UUID=uuid"
+//usage:#define findfs_full_usage "\n\n"
+//usage:       "Find a filesystem device based on a label or UUID"
+//usage:
+//usage:#define findfs_example_usage
+//usage:       "$ findfs LABEL=MyDevice"
+
 #include "libbb.h"
 #include "volume_id.h"
 
index 78b1e4b..05a747f 100644 (file)
@@ -3,6 +3,16 @@
  *
  * This is free software, licensed under the GNU General Public License v2.
  */
+
+//usage:#define flock_trivial_usage
+//usage:       "[-sxun] FD|{FILE [-c] PROG ARGS}"
+//usage:#define flock_full_usage "\n\n"
+//usage:       "[Un]lock file descriptor, or lock FILE, run PROG\n"
+//usage:     "\n       -s      Shared lock"
+//usage:     "\n       -x      Exclusive lock (default)"
+//usage:     "\n       -u      Unlock FD"
+//usage:     "\n       -n      Fail rather than wait"
+
 #include <sys/file.h>
 #include "libbb.h"
 
@@ -19,7 +29,7 @@ int flock_main(int argc UNUSED_PARAM, char **argv)
        };
 
 #if ENABLE_LONG_OPTS
-        static const char getopt_longopts[] ALIGN1 =
+       static const char getopt_longopts[] ALIGN1 =
                "shared\0"      No_argument       "s"
                "exclusive\0"   No_argument       "x"
                "unlock\0"      No_argument       "u"
@@ -35,12 +45,12 @@ int flock_main(int argc UNUSED_PARAM, char **argv)
        if (argv[1]) {
                fd = open(argv[0], O_RDONLY|O_NOCTTY|O_CREAT, 0666);
                if (fd < 0 && errno == EISDIR)
-                       fd = open(argv[0], O_RDONLY|O_NOCTTY);
+                       fd = open(argv[0], O_RDONLY|O_NOCTTY);
                if (fd < 0)
                        bb_perror_msg_and_die("can't open '%s'", argv[0]);
                //TODO? close_on_exec_on(fd);
        } else {
-               fd = xatoi_u(argv[0]);
+               fd = xatoi_positive(argv[0]);
        }
        argv++;
 
index 4949056..a89ae1a 100644 (file)
@@ -6,8 +6,22 @@
  * Adjusted a bit by Erik Andersen <andersen@codepoet.org>
  * Unified with fdflush by Tito Ragusa <farmatito@tiscali.it>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define freeramdisk_trivial_usage
+//usage:       "DEVICE"
+//usage:#define freeramdisk_full_usage "\n\n"
+//usage:       "Free all memory used by the specified ramdisk"
+//usage:
+//usage:#define freeramdisk_example_usage
+//usage:       "$ freeramdisk /dev/ram2\n"
+//usage:
+//usage:#define fdflush_trivial_usage
+//usage:       "DEVICE"
+//usage:#define fdflush_full_usage "\n\n"
+//usage:       "Force floppy disk drive to detect disk change"
+
 #include <sys/mount.h>
 #include "libbb.h"
 
index 970f5f7..33767a1 100644 (file)
@@ -4,7 +4,7 @@
  *
  * (C) 1991, 1992 Linus Torvalds.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /*
@@ -13,7 +13,7 @@
  * 10.11.91  -  updated, does checking, no repairs yet.
  *             Sent out to the mailing-list for testing.
  *
- * 14.11.91  - Testing seems to have gone well. Added some
+ * 14.11.91  -  Testing seems to have gone well. Added some
  *             correction-code, and changed some functions.
  *
  * 15.11.91  -  More correction code. Hopefully it notices most
  * 16.11.91  -  More corrections (thanks to Mika Jalava). Most
  *             things seem to work now. Yeah, sure.
  *
- *
- * 19.04.92  - Had to start over again from this old version, as a
+ * 19.04.92  -  Had to start over again from this old version, as a
  *             kernel bug ate my enhanced fsck in february.
  *
- * 28.02.93  - added support for different directory entry sizes..
+ * 28.02.93  -  added support for different directory entry sizes..
  *
  * Sat Mar  6 18:59:42 1993, faith@cs.unc.edu: Output namelen with
  *                           superblock information
  *                           to that required by fsutil
  *
  * Mon Jan  3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu)
- *                           Added support for file system valid flag.  Also
- *                           added program_version variable and output of
- *                           program name and version number when program
- *                           is executed.
+ *                            Added support for file system valid flag.  Also
+ *                            added program_version variable and output of
+ *                            program name and version number when program
+ *                            is executed.
  *
- * 30.10.94 - added support for v2 filesystem
- *            (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
+ * 30.10.94  - added support for v2 filesystem
+ *             (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
  *
- * 10.12.94  -  added test to prevent checking of mounted fs adapted
- *              from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck
- *              program.  (Daniel Quinlan, quinlan@yggdrasil.com)
+ * 10.12.94  - added test to prevent checking of mounted fs adapted
+ *             from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck
+ *             program.  (Daniel Quinlan, quinlan@yggdrasil.com)
  *
  * 01.07.96  - Fixed the v2 fs stuff to use the right #defines and such
- *            for modern libcs (janl@math.uio.no, Nicolai Langfeldt)
+ *             for modern libcs (janl@math.uio.no, Nicolai Langfeldt)
  *
  * 02.07.96  - Added C bit fiddling routines from rmk@ecs.soton.ac.uk
  *             (Russell King).  He made them for ARM.  It would seem
- *            that the ARM is powerful enough to do this in C whereas
+ *             that the ARM is powerful enough to do this in C whereas
  *             i386 and m64k must use assembly to get it fast >:-)
- *            This should make minix fsck system-independent.
- *            (janl@math.uio.no, Nicolai Langfeldt)
+ *             This should make minix fsck system-independent.
+ *             (janl@math.uio.no, Nicolai Langfeldt)
  *
  * 04.11.96  - Added minor fixes from Andreas Schwab to avoid compiler
  *             warnings.  Added mc68k bitops from
- *            Joerg Dorchain <dorchain@mpi-sb.mpg.de>.
+ *             Joerg Dorchain <dorchain@mpi-sb.mpg.de>.
  *
  * 06.11.96  - Added v2 code submitted by Joerg Dorchain, but written by
  *             Andreas Schwab.
  * enforced (but it's not much fun on a character device :-).
  */
 
+//usage:#define fsck_minix_trivial_usage
+//usage:       "[-larvsmf] BLOCKDEV"
+//usage:#define fsck_minix_full_usage "\n\n"
+//usage:       "Check MINIX filesystem\n"
+//usage:     "\n       -l      List all filenames"
+//usage:     "\n       -r      Perform interactive repairs"
+//usage:     "\n       -a      Perform automatic repairs"
+//usage:     "\n       -v      Verbose"
+//usage:     "\n       -s      Output superblock information"
+//usage:     "\n       -m      Show \"mode not cleared\" warnings"
+//usage:     "\n       -f      Force file system check"
+
 #include <mntent.h>
 #include "libbb.h"
 #include "minix.h"
@@ -675,7 +686,7 @@ static void get_inode_common(unsigned nr, uint16_t i_mode)
        total++;
        if (!inode_count[nr]) {
                if (!inode_in_use(nr)) {
-                       printf("Inode %d is marked as 'unused', but it is used "
+                       printf("Inode %u is marked as 'unused', but it is used "
                                        "for file '%s'\n", nr, current_name);
                        if (OPT_repair) {
                                if (ask("Mark as 'in use'", 1))
@@ -1119,7 +1130,7 @@ static void check_counts(void)
                        continue;
                }
                printf("Zone %d: %sin use, counted=%d\n",
-                          i, zone_in_use(i) ? "" : "not ", zone_count[i]);
+                       i, zone_in_use(i) ? "" : "not ", zone_count[i]);
        }
 }
 
@@ -1171,7 +1182,7 @@ static void check_counts2(void)
                        continue;
                }
                printf("Zone %d: %sin use, counted=%d\n",
-                          i, zone_in_use(i) ? "" : "not ", zone_count[i]);
+                       i, zone_in_use(i) ? "" : "not ", zone_count[i]);
        }
 }
 #endif
@@ -1241,7 +1252,7 @@ int fsck_minix_main(int argc UNUSED_PARAM, char **argv)
                printf("Forcing filesystem check on %s\n", device_name);
        else if (OPT_repair)
                printf("Filesystem on %s is dirty, needs checking\n",
-                          device_name);
+                       device_name);
 
        read_tables();
 
@@ -1268,23 +1279,23 @@ int fsck_minix_main(int argc UNUSED_PARAM, char **argv)
                        if (!inode_in_use(i))
                                free_cnt++;
                printf("\n%6u inodes used (%u%%)\n", (INODES - free_cnt),
-                          100 * (INODES - free_cnt) / INODES);
+                       100 * (INODES - free_cnt) / INODES);
                for (i = FIRSTZONE, free_cnt = 0; i < ZONES; i++)
                        if (!zone_in_use(i))
                                free_cnt++;
                printf("%6u zones used (%u%%)\n\n"
-                          "%6u regular files\n"
-                          "%6u directories\n"
-                          "%6u character device files\n"
-                          "%6u block device files\n"
-                          "%6u links\n"
-                          "%6u symbolic links\n"
-                          "------\n"
-                          "%6u files\n",
-                          (ZONES - free_cnt), 100 * (ZONES - free_cnt) / ZONES,
-                          regular, directory, chardev, blockdev,
-                          links - 2 * directory + 1, symlinks,
-                          total - 2 * directory + 1);
+                       "%6u regular files\n"
+                       "%6u directories\n"
+                       "%6u character device files\n"
+                       "%6u block device files\n"
+                       "%6u links\n"
+                       "%6u symbolic links\n"
+                       "------\n"
+                       "%6u files\n",
+                       (ZONES - free_cnt), 100 * (ZONES - free_cnt) / ZONES,
+                       regular, directory, chardev, blockdev,
+                       links - 2 * directory + 1, symlinks,
+                       total - 2 * directory + 1);
        }
        if (changed) {
                write_tables();
diff --git a/util-linux/fstrim.c b/util-linux/fstrim.c
new file mode 100644 (file)
index 0000000..675a021
--- /dev/null
@@ -0,0 +1,119 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fstrim.c - discard the part (or whole) of mounted filesystem.
+ *
+ * 03 March 2012 - Malek Degachi <malek-degachi@laposte.net>
+ * Adapted for busybox from util-linux-2.12a.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//config:config FSTRIM
+//config:      bool "fstrim"
+//config:      default y
+//config:      select PLATFORM_LINUX
+//config:      help
+//config:        Discard unused blocks on a mounted filesystem.
+
+//applet:IF_FSTRIM(APPLET(fstrim, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FSTRIM) += fstrim.o
+
+//usage:#define fstrim_trivial_usage
+//usage:       "[OPTIONS] MOUNTPOINT"
+//usage:#define fstrim_full_usage "\n\n"
+//usage:       IF_LONG_OPTS(
+//usage:       "       -o,--offset=OFFSET      Offset in bytes to discard from"
+//usage:     "\n       -l,--length=LEN         Bytes to discard"
+//usage:     "\n       -m,--minimum=MIN        Minimum extent length"
+//usage:     "\n       -v,--verbose            Print number of discarded bytes"
+//usage:       )
+//usage:       IF_NOT_LONG_OPTS(
+//usage:       "       -o OFFSET       Offset in bytes to discard from"
+//usage:     "\n       -l LEN          Bytes to discard"
+//usage:     "\n       -m MIN          Minimum extent length"
+//usage:     "\n       -v,             Print number of discarded bytes"
+//usage:       )
+
+#include "libbb.h"
+#include <linux/fs.h>
+
+#ifndef FITRIM
+struct fstrim_range {
+       uint64_t start;
+       uint64_t len;
+       uint64_t minlen;
+};
+#define FITRIM         _IOWR('X', 121, struct fstrim_range)
+#endif
+
+static const struct suffix_mult fstrim_sfx[] = {
+       { "KiB", 1024 },
+       { "kiB", 1024 },
+       { "K", 1024 },
+       { "k", 1024 },
+       { "MiB", 1048576 },
+       { "miB", 1048576 },
+       { "M", 1048576 },
+       { "m", 1048576 },
+       { "GiB", 1073741824 },
+       { "giB", 1073741824 },
+       { "G", 1073741824 },
+       { "g", 1073741824 },
+       { "KB", 1000 },
+       { "MB", 1000000 },
+       { "GB", 1000000000 },
+       { "", 0 }
+};
+
+int fstrim_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fstrim_main(int argc UNUSED_PARAM, char **argv)
+{
+       struct fstrim_range range;
+       char *arg_o, *arg_l, *arg_m, *mp;
+       unsigned opts;
+       int fd;
+
+       enum {
+               OPT_o = (1 << 0),
+               OPT_l = (1 << 1),
+               OPT_m = (1 << 2),
+               OPT_v = (1 << 3),
+       };
+
+#if ENABLE_LONG_OPTS
+       static const char getopt_longopts[] ALIGN1 =
+               "offset\0"    Required_argument    "o"
+               "length\0"    Required_argument    "l"
+               "minimum\0"   Required_argument    "m"
+               "verbose\0"   No_argument          "v"
+               ;
+       applet_long_options = getopt_longopts;
+#endif
+
+       opt_complementary = "=1"; /* exactly one non-option arg: the mountpoint */
+       opts = getopt32(argv, "o:l:m:v", &arg_o, &arg_l, &arg_m);
+
+       memset(&range, 0, sizeof(range));
+       range.len = ULLONG_MAX;
+
+       if (opts & OPT_o)
+               range.start = xatoull_sfx(arg_o, fstrim_sfx);
+       if (opts & OPT_l)
+               range.len = xatoull_sfx(arg_l, fstrim_sfx);
+       if (opts & OPT_m)
+               range.minlen = xatoull_sfx(arg_m, fstrim_sfx);
+
+       mp = argv[optind];
+       if (find_block_device(mp)) {
+               fd = xopen_nonblocking(mp);
+               xioctl(fd, FITRIM, &range);
+               if (ENABLE_FEATURE_CLEAN_UP)
+                       close(fd);
+
+               if (opts & OPT_v)
+                       printf("%s: %llu bytes trimmed\n", mp, (unsigned long long)range.len);
+               return EXIT_SUCCESS;
+       }
+       return EXIT_FAILURE;
+}
index adb9675..58df1c8 100644 (file)
@@ -3,7 +3,7 @@
  * getopt.c - Enhanced implementation of BSD getopt(1)
  *   Copyright (c) 1997, 1998, 1999, 2000  Frodo Looijaard <frodol@dds.nl>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*
  *   Added NLS support (partly written by Arkadiusz Mickiewicz
  *     <misiek@misiek.eu.org>)
  * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org>
- *  Removed --version/-V and --help/-h in
+ *  Removed --version/-V and --help/-h
  *  Removed parse_error(), using bb_error_msg() from Busybox instead
  *  Replaced our_malloc with xmalloc and our_realloc with xrealloc
  *
  */
 
-#include <getopt.h>
+//usage:#define getopt_trivial_usage
+//usage:       "[OPTIONS] [--] OPTSTRING PARAMS"
+//usage:#define getopt_full_usage "\n\n"
+//usage:       IF_LONG_OPTS(
+//usage:       IF_FEATURE_GETOPT_LONG(
+//usage:       "       -a,--alternative                Allow long options starting with single -\n"
+//usage:       "       -l,--longoptions=LOPT[,...]     Long options to recognize\n"
+//usage:       )
+//usage:       "       -n,--name=PROGNAME              The name under which errors are reported"
+//usage:     "\n       -o,--options=OPTSTRING          Short options to recognize"
+//usage:     "\n       -q,--quiet                      No error messages on unrecognized options"
+//usage:     "\n       -Q,--quiet-output               No normal output"
+//usage:     "\n       -s,--shell=SHELL                Set shell quoting conventions"
+//usage:     "\n       -T,--test                       Version test (exits with 4)"
+//usage:     "\n       -u,--unquoted                   Don't quote output"
+//usage:       )
+//usage:       IF_NOT_LONG_OPTS(
+//usage:       IF_FEATURE_GETOPT_LONG(
+//usage:       "       -a              Allow long options starting with single -\n"
+//usage:       "       -l LOPT[,...]   Long options to recognize\n"
+//usage:       )
+//usage:       "       -n PROGNAME     The name under which errors are reported"
+//usage:     "\n       -o OPTSTRING    Short options to recognize"
+//usage:     "\n       -q              No error messages on unrecognized options"
+//usage:     "\n       -Q              No normal output"
+//usage:     "\n       -s SHELL        Set shell quoting conventions"
+//usage:     "\n       -T              Version test (exits with 4)"
+//usage:     "\n       -u              Don't quote output"
+//usage:       )
+//usage:       IF_FEATURE_GETOPT_LONG( /* example uses -l, needs FEATURE_GETOPT_LONG */
+//usage:     "\n"
+//usage:     "\nExample:"
+//usage:     "\n"
+//usage:     "\nO=`getopt -l bb: -- ab:c:: \"$@\"` || exit 1"
+//usage:     "\neval set -- \"$O\""
+//usage:     "\nwhile true; do"
+//usage:     "\n       case \"$1\" in"
+//usage:     "\n       -a)     echo A; shift;;"
+//usage:     "\n       -b|--bb) echo \"B:'$2'\"; shift 2;;"
+//usage:     "\n       -c)     case \"$2\" in"
+//usage:     "\n               \"\")   echo C; shift 2;;"
+//usage:     "\n               *)      echo \"C:'$2'\"; shift 2;;"
+//usage:     "\n               esac;;"
+//usage:     "\n       --)     shift; break;;"
+//usage:     "\n       *)      echo Error; exit 1;;"
+//usage:     "\n       esac"
+//usage:     "\ndone"
+//usage:       )
+//usage:
+//usage:#define getopt_example_usage
+//usage:       "$ cat getopt.test\n"
+//usage:       "#!/bin/sh\n"
+//usage:       "GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \\\n"
+//usage:       "       -n 'example.busybox' -- \"$@\"`\n"
+//usage:       "if [ $? != 0 ]; then exit 1; fi\n"
+//usage:       "eval set -- \"$GETOPT\"\n"
+//usage:       "while true; do\n"
+//usage:       " case $1 in\n"
+//usage:       "   -a|--a-long) echo \"Option a\"; shift;;\n"
+//usage:       "   -b|--b-long) echo \"Option b, argument '$2'\"; shift 2;;\n"
+//usage:       "   -c|--c-long)\n"
+//usage:       "     case \"$2\" in\n"
+//usage:       "       \"\") echo \"Option c, no argument\"; shift 2;;\n"
+//usage:       "       *)  echo \"Option c, argument '$2'\"; shift 2;;\n"
+//usage:       "     esac;;\n"
+//usage:       "   --) shift; break;;\n"
+//usage:       "   *) echo \"Internal error!\"; exit 1;;\n"
+//usage:       " esac\n"
+//usage:       "done\n"
+
+#if ENABLE_FEATURE_GETOPT_LONG
+# include <getopt.h>
+#endif
 #include "libbb.h"
 
 /* NON_OPT is the code that is returned when a non-option is found in '+'
@@ -148,11 +220,6 @@ static const char *normalize(const char *arg)
 static int generate_output(char **argv, int argc, const char *optstr, const struct option *longopts)
 {
        int exit_code = 0; /* We assume everything will be OK */
-       int opt;
-#if ENABLE_FEATURE_GETOPT_LONG
-       int longindex;
-#endif
-       const char *charptr;
 
        if (quiet_errors) /* No error reporting from getopt(3) */
                opterr = 0;
@@ -167,13 +234,14 @@ static int generate_output(char **argv, int argc, const char *optstr, const stru
 #endif
 
        while (1) {
-               opt =
 #if ENABLE_FEATURE_GETOPT_LONG
-                       alternative ?
-                       getopt_long_only(argc, argv, optstr, longopts, &longindex) :
-                       getopt_long(argc, argv, optstr, longopts, &longindex);
+               int longindex;
+               int opt = alternative
+                       ? getopt_long_only(argc, argv, optstr, longopts, &longindex)
+                       : getopt_long(argc, argv, optstr, longopts, &longindex)
+               ;
 #else
-                       getopt(argc, argv, optstr);
+               int opt = getopt(argc, argv, optstr);
 #endif
                if (opt == -1)
                        break;
@@ -191,9 +259,10 @@ static int generate_output(char **argv, int argc, const char *optstr, const stru
                        if (opt == NON_OPT)
                                printf(" %s", normalize(optarg));
                        else {
+                               const char *charptr;
                                printf(" -%c", opt);
                                charptr = strchr(optstr, opt);
-                               if (charptr != NULL && *++charptr == ':')
+                               if (charptr && *++charptr == ':')
                                        printf(" %s",
                                                normalize(optarg ? optarg : ""));
                        }
@@ -201,9 +270,11 @@ static int generate_output(char **argv, int argc, const char *optstr, const stru
        }
 
        if (!quiet_output) {
+               unsigned idx;
                printf(" --");
-               while (optind < argc)
-                       printf(" %s", normalize(argv[optind++]));
+               idx = optind;
+               while (argv[idx])
+                       printf(" %s", normalize(argv[idx++]));
                bb_putchar('\n');
        }
        return exit_code;
@@ -290,6 +361,7 @@ static const char getopt_longopts[] ALIGN1 =
 int getopt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int getopt_main(int argc, char **argv)
 {
+       int n;
        char *optstr = NULL;
        char *name = NULL;
        unsigned opt;
@@ -302,10 +374,10 @@ int getopt_main(int argc, char **argv)
 
        compatible = getenv("GETOPT_COMPATIBLE"); /* used as yes/no flag */
 
-       if (argc == 1) {
+       if (!argv[1]) {
                if (compatible) {
                        /* For some reason, the original getopt gave no error
-                          when there were no arguments. */
+                        * when there were no arguments. */
                        printf(" --\n");
                        return 0;
                }
@@ -313,10 +385,10 @@ int getopt_main(int argc, char **argv)
        }
 
        if (argv[1][0] != '-' || compatible) {
-               char *s;
+               char *s = argv[1];
 
                option_mask32 |= OPT_u; /* quoting off */
-               s = xstrdup(argv[1] + strspn(argv[1], "-+"));
+               s = xstrdup(s + strspn(s, "-+"));
                argv[1] = argv[0];
                return generate_output(argv+1, argc-1, s, long_options);
        }
@@ -343,12 +415,13 @@ int getopt_main(int argc, char **argv)
        }
 
        /* All options controlling the applet have now been parsed */
+       n = optind - 1;
        if (!optstr) {
-               if (optind >= argc)
+               optstr = argv[++n];
+               if (!optstr)
                        bb_error_msg_and_die("missing optstring argument");
-               optstr = argv[optind++];
        }
 
-       argv[optind-1] = name ? name : argv[0];
-       return generate_output(argv+optind-1, argc-optind+1, optstr, long_options);
+       argv[n] = name ? name : argv[0];
+       return generate_output(argv + n, argc - n, optstr, long_options);
 }
index 2eb5e57..ac7e24f 100644 (file)
@@ -4,11 +4,34 @@
  * Based on code from util-linux v 2.11l
  *
  * Copyright (c) 1989
- *     The Regents of the University of California.  All rights reserved.
+ * The Regents of the University of California.  All rights reserved.
  *
- * Licensed under GPLv2 or later, see file License in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define hexdump_trivial_usage
+//usage:       "[-bcCdefnosvx" IF_FEATURE_HEXDUMP_REVERSE("R") "] [FILE]..."
+//usage:#define hexdump_full_usage "\n\n"
+//usage:       "Display FILEs (or stdin) in a user specified format\n"
+//usage:     "\n       -b              One-byte octal display"
+//usage:     "\n       -c              One-byte character display"
+//usage:     "\n       -C              Canonical hex+ASCII, 16 bytes per line"
+//usage:     "\n       -d              Two-byte decimal display"
+//usage:     "\n       -e FORMAT_STRING"
+//usage:     "\n       -f FORMAT_FILE"
+//usage:     "\n       -n LENGTH       Interpret only LENGTH bytes of input"
+//usage:     "\n       -o              Two-byte octal display"
+//usage:     "\n       -s OFFSET       Skip OFFSET bytes"
+//usage:     "\n       -v              Display all input data"
+//usage:     "\n       -x              Two-byte hexadecimal display"
+//usage:       IF_FEATURE_HEXDUMP_REVERSE(
+//usage:     "\n       -R              Reverse of 'hexdump -Cv'")
+//usage:
+//usage:#define hd_trivial_usage
+//usage:       "FILE..."
+//usage:#define hd_full_usage "\n\n"
+//usage:       "hd is an alias for hexdump -C"
+
 #include "libbb.h"
 #include "dump.h"
 
@@ -32,24 +55,17 @@ static void bb_dump_addfile(dumper_t *dumper, char *name)
 }
 
 static const char *const add_strings[] = {
-       "\"%07.7_ax \" 16/1 \"%03o \" \"\\n\"",         /* b */
-       "\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\"",         /* c */
-       "\"%07.7_ax \" 8/2 \"  %05u \" \"\\n\"",        /* d */
-       "\"%07.7_ax \" 8/2 \" %06o \" \"\\n\"",         /* o */
-       "\"%07.7_ax \" 8/2 \"   %04x \" \"\\n\"",       /* x */
+       "\"%07.7_ax \" 16/1 \"%03o \" \"\\n\"",   /* b */
+       "\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\"",   /* c */
+       "\"%07.7_ax \" 8/2 \"  %05u \" \"\\n\"",  /* d */
+       "\"%07.7_ax \" 8/2 \" %06o \" \"\\n\"",   /* o */
+       "\"%07.7_ax \" 8/2 \"   %04x \" \"\\n\"", /* x */
 };
 
 static const char add_first[] ALIGN1 = "\"%07.7_Ax\n\"";
 
 static const char hexdump_opts[] ALIGN1 = "bcdoxCe:f:n:s:v" IF_FEATURE_HEXDUMP_REVERSE("R");
 
-static const struct suffix_mult suffixes[] = {
-       { "b", 512 },
-       { "k", 1024 },
-       { "m", 1024*1024 },
-       { "", 0 }
-};
-
 int hexdump_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int hexdump_main(int argc, char **argv)
 {
@@ -90,10 +106,15 @@ int hexdump_main(int argc, char **argv)
                        bb_dump_addfile(dumper, optarg);
                } /* else */
                if (ch == 'n') {
-                       dumper->dump_length = xatoi_u(optarg);
+                       dumper->dump_length = xatoi_positive(optarg);
                } /* else */
                if (ch == 's') { /* compat: -s accepts hex numbers too */
-                       dumper->dump_skip = xstrtoul_range_sfx(optarg, /*base:*/ 0, /*lo:*/ 0, /*hi:*/ LONG_MAX, suffixes);
+                       dumper->dump_skip = xstrtoull_range_sfx(
+                               optarg,
+                               /*base:*/ 0,
+                               /*lo:*/ 0, /*hi:*/ OFF_T_MAX,
+                               bkm_suffixes
+                       );
                } /* else */
                if (ch == 'v') {
                        dumper->dump_vflag = ALL;
index 3da2e23..379eeb2 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2002 Robert Griebl <griebl@gmx.de>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 */
 
 #include "libbb.h"
@@ -43,7 +43,7 @@ static time_t read_rtc(const char **pp_rtcname, struct timeval *sys_tv, int utc)
                while (1) {
                        rtc_read_tm(&tm_time, fd);
                        gettimeofday(sys_tv, NULL);
-                       if (before != tm_time.tm_sec)
+                       if (before != (int)tm_time.tm_sec)
                                break;
                }
        }
@@ -60,25 +60,31 @@ static void show_clock(const char **pp_rtcname, int utc)
 #if SHOW_HWCLOCK_DIFF
        struct timeval sys_tv;
 #endif
-       time_t t;
-       char *cp;
+       time_t t = read_rtc(pp_rtcname, &sys_tv, utc);
 
-       t = read_rtc(pp_rtcname, &sys_tv, utc);
-       cp = ctime(&t);
+#if ENABLE_LOCALE_SUPPORT
+       /* Standard hwclock uses locale-specific output format */
+       char cp[64];
+       struct tm *ptm = localtime(&t);
+       strftime(cp, sizeof(cp), "%c", ptm);
+#else
+       char *cp = ctime(&t);
        strchrnul(cp, '\n')[0] = '\0';
+#endif
+
 #if !SHOW_HWCLOCK_DIFF
        printf("%s  0.000000 seconds\n", cp);
 #else
        {
                long diff = sys_tv.tv_sec - t;
                if (diff < 0 /*&& tv.tv_usec != 0*/) {
-                       /* Why? */
-                       /* diff >= 0 is ok:   diff < 0, can't just use tv.tv_usec: */
-                       /*   45.520820          43.520820 */
-                       /* - 44.000000        - 45.000000 */
-                       /* =  1.520820        = -1.479180, not -2.520820! */
+                       /* Why we need diff++? */
+                       /* diff >= 0 is ok: | diff < 0, can't just use tv.tv_usec: */
+                       /*   45.520820      |   43.520820 */
+                       /* - 44.000000      | - 45.000000 */
+                       /* =  1.520820      | = -1.479180, not -2.520820! */
                        diff++;
-                       /* should be 1000000 - tv.tv_usec, but then we must check tv.tv_usec != 0 */
+                       /* Should be 1000000 - tv.tv_usec, but then we must check tv.tv_usec != 0 */
                        sys_tv.tv_usec = 999999 - sys_tv.tv_usec;
                }
                printf("%s  %ld.%06lu seconds\n", cp, diff, (unsigned long)sys_tv.tv_usec);
@@ -223,12 +229,62 @@ static void from_sys_clock(const char **pp_rtcname, int utc)
                close(rtc);
 }
 
+/*
+ * At system boot, kernel may set system time from RTC,
+ * but it knows nothing about timezones. If RTC is in local time,
+ * then system time is wrong - it is offset by timezone.
+ * This option corrects system time if RTC is in local time,
+ * and (always) sets in-kernel timezone.
+ *
+ * This is an alternate option to --hctosys that does not read the
+ * hardware clock.
+ */
+static void set_system_clock_timezone(int utc)
+{
+       struct timeval tv;
+       struct tm *broken;
+       struct timezone tz;
+
+       gettimeofday(&tv, NULL);
+       broken = localtime(&tv.tv_sec);
+       tz.tz_minuteswest = timezone / 60;
+       if (broken->tm_isdst)
+               tz.tz_minuteswest -= 60;
+       tz.tz_dsttime = 0;
+       gettimeofday(&tv, NULL);
+       if (!utc)
+               tv.tv_sec += tz.tz_minuteswest * 60;
+       if (settimeofday(&tv, &tz))
+               bb_perror_msg_and_die("settimeofday");
+}
+
+//usage:#define hwclock_trivial_usage
+//usage:       IF_FEATURE_HWCLOCK_LONG_OPTIONS(
+//usage:       "[-r|--show] [-s|--hctosys] [-w|--systohc] [-t|--systz]"
+//usage:       " [-l|--localtime] [-u|--utc]"
+//usage:       " [-f|--rtc FILE]"
+//usage:       )
+//usage:       IF_NOT_FEATURE_HWCLOCK_LONG_OPTIONS(
+//usage:       "[-r] [-s] [-w] [-t] [-l] [-u] [-f FILE]"
+//usage:       )
+//usage:#define hwclock_full_usage "\n\n"
+//usage:       "Query and set hardware clock (RTC)\n"
+//usage:     "\n       -r      Show hardware clock time"
+//usage:     "\n       -s      Set system time from hardware clock"
+//usage:     "\n       -w      Set hardware clock from system time"
+//usage:     "\n       -t      Set in-kernel timezone, correct system time"
+//usage:     "\n               if hardware clock is in local time"
+//usage:     "\n       -u      Assume hardware clock is kept in UTC"
+//usage:     "\n       -l      Assume hardware clock is kept in local time"
+//usage:     "\n       -f FILE Use specified device (e.g. /dev/rtc2)"
+
 #define HWCLOCK_OPT_LOCALTIME   0x01
 #define HWCLOCK_OPT_UTC         0x02
 #define HWCLOCK_OPT_SHOW        0x04
 #define HWCLOCK_OPT_HCTOSYS     0x08
 #define HWCLOCK_OPT_SYSTOHC     0x10
-#define HWCLOCK_OPT_RTCFILE     0x20
+#define HWCLOCK_OPT_SYSTZ       0x20
+#define HWCLOCK_OPT_RTCFILE     0x40
 
 int hwclock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int hwclock_main(int argc UNUSED_PARAM, char **argv)
@@ -239,17 +295,18 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv)
 
 #if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
        static const char hwclock_longopts[] ALIGN1 =
-               "localtime\0" No_argument "l"
+               "localtime\0" No_argument "l" /* short opt is non-standard */
                "utc\0"       No_argument "u"
                "show\0"      No_argument "r"
                "hctosys\0"   No_argument "s"
                "systohc\0"   No_argument "w"
-               "file\0"      Required_argument "f"
+               "systz\0"     No_argument "t" /* short opt is non-standard */
+               "rtc\0"       Required_argument "f"
                ;
        applet_long_options = hwclock_longopts;
 #endif
-       opt_complementary = "r--ws:w--rs:s--wr:l--u:u--l";
-       opt = getopt32(argv, "lurswf:", &rtcname);
+       opt_complementary = "r--wst:w--rst:s--wrt:t--rsw:l--u:u--l";
+       opt = getopt32(argv, "lurswtf:", &rtcname);
 
        /* If -u or -l wasn't given check if we are using utc */
        if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
@@ -261,6 +318,8 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv)
                to_sys_clock(&rtcname, utc);
        else if (opt & HWCLOCK_OPT_SYSTOHC)
                from_sys_clock(&rtcname, utc);
+       else if (opt & HWCLOCK_OPT_SYSTZ)
+               set_system_clock_timezone(utc);
        else
                /* default HWCLOCK_OPT_SHOW */
                show_clock(&rtcname, utc);
index 8b0b226..888f70e 100644 (file)
@@ -5,9 +5,18 @@
  * 01 Sept 2004 - Rodney Radford <rradford@mindspring.com>
  * Adapted for busybox from util-linux-2.12a.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define ipcrm_trivial_usage
+//usage:       "[-MQS key] [-mqs id]"
+//usage:#define ipcrm_full_usage "\n\n"
+//usage:       "Upper-case options MQS remove an object by shmkey value.\n"
+//usage:       "Lower-case options remove an object by shmid value.\n"
+//usage:     "\n       -mM     Remove memory segment after last detach"
+//usage:     "\n       -qQ     Remove message queue"
+//usage:     "\n       -sS     Remove semaphore"
+
 #include "libbb.h"
 
 /* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */
@@ -40,21 +49,21 @@ typedef enum type_id {
        MSG
 } type_id;
 
-static int remove_ids(type_id type, int argc, char **argv)
+static int remove_ids(type_id type, char **argv)
 {
        unsigned long id;
-       int ret = 0;            /* silence gcc */
        int nb_errors = 0;
        union semun arg;
 
        arg.val = 0;
 
-       while (argc) {
+       while (argv[0]) {
                id = bb_strtoul(argv[0], NULL, 10);
                if (errno || id > INT_MAX) {
                        bb_error_msg("invalid id: %s", argv[0]);
                        nb_errors++;
                } else {
+                       int ret = 0;
                        if (type == SEM)
                                ret = semctl(id, 0, IPC_RMID, arg);
                        else if (type == MSG)
@@ -67,7 +76,6 @@ static int remove_ids(type_id type, int argc, char **argv)
                                nb_errors++;
                        }
                }
-               argc--;
                argv++;
        }
 
@@ -92,14 +100,13 @@ int ipcrm_main(int argc, char **argv)
                type_id what = 0; /* silence gcc */
                char w;
 
-               w=argv[1][0];
+               w = argv[1][0];
                if ( ((w == 'm' && argv[1][1] == 's' && argv[1][2] == 'g')
                       || (argv[1][0] == 's'
-                          && ((w=argv[1][1]) == 'h' || w == 'e')
+                          && ((w = argv[1][1]) == 'h' || w == 'e')
                           && argv[1][2] == 'm')
                     ) && argv[1][3] == '\0'
                ) {
-
                        if (argc < 3)
                                bb_show_usage();
 
@@ -110,7 +117,7 @@ int ipcrm_main(int argc, char **argv)
                        else if (w == 'e')
                                what = SEM;
 
-                       if (remove_ids(what, argc-2, &argv[2]))
+                       if (remove_ids(what, &argv[2]))
                                fflush_stdout_and_exit(EXIT_FAILURE);
                        printf("resource(s) deleted\n");
                        return 0;
@@ -153,7 +160,7 @@ int ipcrm_main(int argc, char **argv)
 
                        /* convert key to id */
                        id = ((c == 'q') ? msgget(key, 0) :
-                                 (c == 'm') ? shmget(key, 0, 0) : semget(key, 0, 0));
+                               (c == 'm') ? shmget(key, 0, 0) : semget(key, 0, 0));
 
                        if (id < 0) {
                                const char *errmsg;
@@ -182,8 +189,8 @@ int ipcrm_main(int argc, char **argv)
                }
 
                result = ((c == 'q') ? msgctl(id, IPC_RMID, NULL) :
-                                 (c == 'm') ? shmctl(id, IPC_RMID, NULL) :
-                                 semctl(id, 0, IPC_RMID, arg));
+                               (c == 'm') ? shmctl(id, IPC_RMID, NULL) :
+                               semctl(id, 0, IPC_RMID, arg));
 
                if (result) {
                        const char *errmsg;
index c1103b0..67a25a8 100644 (file)
@@ -5,9 +5,25 @@
  * 01 Sept 2004 - Rodney Radford <rradford@mindspring.com>
  * Adapted for busybox from util-linux-2.12a.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define ipcs_trivial_usage
+//usage:       "[[-smq] -i shmid] | [[-asmq] [-tcplu]]"
+//usage:#define ipcs_full_usage "\n\n"
+//usage:       "       -i      Show specific resource"
+//usage:     "\nResource specification:"
+//usage:     "\n       -m      Shared memory segments"
+//usage:     "\n       -q      Message queues"
+//usage:     "\n       -s      Semaphore arrays"
+//usage:     "\n       -a      All (default)"
+//usage:     "\nOutput format:"
+//usage:     "\n       -t      Time"
+//usage:     "\n       -c      Creator"
+//usage:     "\n       -p      Pid"
+//usage:     "\n       -l      Limits"
+//usage:     "\n       -u      Summary"
+
 /* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */
 /* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */
 /* X/OPEN tells us to use <sys/{types,ipc,shm}.h> for shmctl() */
 #define SHM_INFO        14
 struct shm_info {
        int used_ids;
-       ulong shm_tot;          /* total allocated shm */
-       ulong shm_rss;          /* total resident shm */
-       ulong shm_swp;          /* total swapped shm */
-       ulong swap_attempts;
-       ulong swap_successes;
+       unsigned long shm_tot;          /* total allocated shm */
+       unsigned long shm_rss;          /* total resident shm */
+       unsigned long shm_swp;          /* total swapped shm */
+       unsigned long swap_attempts;
+       unsigned long swap_successes;
 };
 #endif
 
@@ -136,54 +152,54 @@ static NOINLINE void do_shm(void)
                if ((shmctl(0, IPC_INFO, (struct shmid_ds *) (void *) &shminfo)) < 0)
                        return;
                /* glibc 2.1.3 and all earlier libc's have ints as fields
-                  of struct shminfo; glibc 2.1.91 has unsigned long; ach */
+                * of struct shminfo; glibc 2.1.91 has unsigned long; ach */
                printf("max number of segments = %lu\n"
-                                 "max seg size (kbytes) = %lu\n"
-                                 "max total shared memory (pages) = %lu\n"
-                                 "min seg size (bytes) = %lu\n",
-                                 (unsigned long) shminfo.shmmni,
-                                 (unsigned long) (shminfo.shmmax >> 10),
-                                 (unsigned long) shminfo.shmall,
-                                 (unsigned long) shminfo.shmmin);
+                               "max seg size (kbytes) = %lu\n"
+                               "max total shared memory (pages) = %lu\n"
+                               "min seg size (bytes) = %lu\n",
+                               (unsigned long) shminfo.shmmni,
+                               (unsigned long) (shminfo.shmmax >> 10),
+                               (unsigned long) shminfo.shmall,
+                               (unsigned long) shminfo.shmmin);
                return;
 
        case STATUS:
                printf("------ Shared Memory %s --------\n", "Status");
-               printf(   "segments allocated %d\n"
-                                 "pages allocated %ld\n"
-                                 "pages resident  %ld\n"
-                                 "pages swapped   %ld\n"
-                                 "Swap performance: %ld attempts\t%ld successes\n",
-                                 shm_info.used_ids,
-                                 shm_info.shm_tot,
-                                 shm_info.shm_rss,
-                                 shm_info.shm_swp,
-                                 shm_info.swap_attempts, shm_info.swap_successes);
+               printf("segments allocated %d\n"
+                               "pages allocated %lu\n"
+                               "pages resident  %lu\n"
+                               "pages swapped   %lu\n"
+                               "Swap performance: %lu attempts\t%lu successes\n",
+                               shm_info.used_ids,
+                               shm_info.shm_tot,
+                               shm_info.shm_rss,
+                               shm_info.shm_swp,
+                               shm_info.swap_attempts, shm_info.swap_successes);
                return;
 
        case CREATOR:
                printf("------ Shared Memory %s --------\n", "Segment Creators/Owners");
-               printf(   "%-10s %-10s %-10s %-10s %-10s %-10s\n",
-                                 "shmid", "perms", "cuid", "cgid", "uid", "gid");
+               printf("%-10s %-10s %-10s %-10s %-10s %-10s\n",
+                               "shmid", "perms", "cuid", "cgid", "uid", "gid");
                break;
 
        case TIME:
                printf("------ Shared Memory %s --------\n", "Attach/Detach/Change Times");
-               printf(   "%-10s %-10s %-20s %-20s %-20s\n",
-                                 "shmid", "owner", "attached", "detached", "changed");
+               printf("%-10s %-10s %-20s %-20s %-20s\n",
+                               "shmid", "owner", "attached", "detached", "changed");
                break;
 
        case PID:
                printf("------ Shared Memory %s --------\n", "Creator/Last-op");
-               printf(   "%-10s %-10s %-10s %-10s\n",
-                                 "shmid", "owner", "cpid", "lpid");
+               printf("%-10s %-10s %-10s %-10s\n",
+                               "shmid", "owner", "cpid", "lpid");
                break;
 
        default:
                printf("------ Shared Memory %s --------\n", "Segments");
-               printf(   "%-10s %-10s %-10s %-10s %-10s %-10s %-12s\n",
-                                 "key", "shmid", "owner", "perms", "bytes", "nattch",
-                                 "status");
+               printf("%-10s %-10s %-10s %-10s %-10s %-10s %-12s\n",
+                               "key", "shmid", "owner", "perms", "bytes", "nattch",
+                               "status");
                break;
        }
 
@@ -204,11 +220,11 @@ static NOINLINE void do_shm(void)
                                printf("%-10d %-10d", shmid, ipcp->uid);
                        /* ctime uses static buffer: use separate calls */
                        printf(" %-20.16s", shmseg.shm_atime
-                                         ? ctime(&shmseg.shm_atime) + 4 : "Not set");
+                                       ? ctime(&shmseg.shm_atime) + 4 : "Not set");
                        printf(" %-20.16s", shmseg.shm_dtime
-                                         ? ctime(&shmseg.shm_dtime) + 4 : "Not set");
+                                       ? ctime(&shmseg.shm_dtime) + 4 : "Not set");
                        printf(" %-20.16s\n", shmseg.shm_ctime
-                                         ? ctime(&shmseg.shm_ctime) + 4 : "Not set");
+                                       ? ctime(&shmseg.shm_ctime) + 4 : "Not set");
                        break;
                case PID:
                        if (pw)
@@ -225,17 +241,17 @@ static NOINLINE void do_shm(void)
                        else
                                printf("%-10d %-10d", shmid, ipcp->uid);
                        printf(" %-10o %-10lu %-10ld %-6s %-6s\n", ipcp->mode & 0777,
-                                         /*
-                                          * earlier: int, Austin has size_t
-                                          */
-                                         (unsigned long) shmseg.shm_segsz,
-                                         /*
-                                          * glibc-2.1.3 and earlier has unsigned short;
-                                          * Austin has shmatt_t
-                                          */
-                                         (long) shmseg.shm_nattch,
-                                         ipcp->mode & SHM_DEST ? "dest" : " ",
-                                         ipcp->mode & SHM_LOCKED ? "locked" : " ");
+                                       /*
+                                        * earlier: int, Austin has size_t
+                                        */
+                                       (unsigned long) shmseg.shm_segsz,
+                                       /*
+                                        * glibc-2.1.3 and earlier has unsigned short;
+                                        * Austin has shmatt_t
+                                        */
+                                       (long) shmseg.shm_nattch,
+                                       ipcp->mode & SHM_DEST ? "dest" : " ",
+                                       ipcp->mode & SHM_LOCKED ? "locked" : " ");
                        break;
                }
        }
@@ -251,7 +267,7 @@ static NOINLINE void do_sem(void)
        struct passwd *pw;
        union semun arg;
 
-       arg.array = (ushort *) (void *) &seminfo;
+       arg.array = (unsigned short *) (void *) &seminfo;
        maxid = semctl(0, 0, SEM_INFO, arg);
        if (maxid < 0) {
                printf("kernel not configured for %s\n", "semaphores");
@@ -261,36 +277,36 @@ static NOINLINE void do_sem(void)
        switch (format) {
        case LIMITS:
                printf("------ Semaphore %s --------\n", "Limits");
-               arg.array = (ushort *) (void *) &seminfo;       /* damn union */
+               arg.array = (unsigned short *) (void *) &seminfo;       /* damn union */
                if ((semctl(0, 0, IPC_INFO, arg)) < 0)
                        return;
                printf("max number of arrays = %d\n"
-                                 "max semaphores per array = %d\n"
-                                 "max semaphores system wide = %d\n"
-                                 "max ops per semop call = %d\n"
-                                 "semaphore max value = %d\n",
-                                 seminfo.semmni,
-                                 seminfo.semmsl,
-                                 seminfo.semmns, seminfo.semopm, seminfo.semvmx);
+                               "max semaphores per array = %d\n"
+                               "max semaphores system wide = %d\n"
+                               "max ops per semop call = %d\n"
+                               "semaphore max value = %d\n",
+                               seminfo.semmni,
+                               seminfo.semmsl,
+                               seminfo.semmns, seminfo.semopm, seminfo.semvmx);
                return;
 
        case STATUS:
                printf("------ Semaphore %s --------\n", "Status");
-               printf(   "used arrays = %d\n"
-                                 "allocated semaphores = %d\n",
-                                 seminfo.semusz, seminfo.semaem);
+               printf("used arrays = %d\n"
+                               "allocated semaphores = %d\n",
+                               seminfo.semusz, seminfo.semaem);
                return;
 
        case CREATOR:
                printf("------ Semaphore %s --------\n", "Arrays Creators/Owners");
-               printf(   "%-10s %-10s %-10s %-10s %-10s %-10s\n",
-                                 "semid", "perms", "cuid", "cgid", "uid", "gid");
+               printf("%-10s %-10s %-10s %-10s %-10s %-10s\n",
+                               "semid", "perms", "cuid", "cgid", "uid", "gid");
                break;
 
        case TIME:
                printf("------ Shared Memory %s --------\n", "Operation/Change Times");
-               printf(   "%-8s %-10s %-26.24s %-26.24s\n",
-                                 "shmid", "owner", "last-op", "last-changed");
+               printf("%-8s %-10s %-26.24s %-26.24s\n",
+                               "shmid", "owner", "last-op", "last-changed");
                break;
 
        case PID:
@@ -298,8 +314,8 @@ static NOINLINE void do_sem(void)
 
        default:
                printf("------ Semaphore %s --------\n", "Arrays");
-               printf(   "%-10s %-10s %-10s %-10s %-10s\n",
-                                 "key", "semid", "owner", "perms", "nsems");
+               printf("%-10s %-10s %-10s %-10s %-10s\n",
+                               "key", "semid", "owner", "perms", "nsems");
                break;
        }
 
@@ -321,9 +337,9 @@ static NOINLINE void do_sem(void)
                                printf("%-8d %-10d", semid, ipcp->uid);
                        /* ctime uses static buffer: use separate calls */
                        printf("  %-26.24s", semary.sem_otime
-                                         ? ctime(&semary.sem_otime) : "Not set");
+                                       ? ctime(&semary.sem_otime) : "Not set");
                        printf(" %-26.24s\n", semary.sem_ctime
-                                         ? ctime(&semary.sem_ctime) : "Not set");
+                                       ? ctime(&semary.sem_ctime) : "Not set");
                        break;
                case PID:
                        break;
@@ -335,13 +351,13 @@ static NOINLINE void do_sem(void)
                        else
                                printf("%-10d %-9d", semid, ipcp->uid);
                        printf(" %-10o %-10ld\n", ipcp->mode & 0777,
-                                         /*
-                                          * glibc-2.1.3 and earlier has unsigned short;
-                                          * glibc-2.1.91 has variation between
-                                          * unsigned short and unsigned long
-                                          * Austin prescribes unsigned short.
-                                          */
-                                         (long) semary.sem_nsems);
+                                       /*
+                                        * glibc-2.1.3 and earlier has unsigned short;
+                                        * glibc-2.1.91 has variation between
+                                        * unsigned short and unsigned long
+                                        * Austin prescribes unsigned short.
+                                        */
+                                       (long) semary.sem_nsems);
                        break;
                }
        }
@@ -367,42 +383,42 @@ static NOINLINE void do_msg(void)
                if ((msgctl(0, IPC_INFO, (struct msqid_ds *) (void *) &msginfo)) < 0)
                        return;
                printf("------ Message%s --------\n", "s: Limits");
-               printf(   "max queues system wide = %d\n"
-                                 "max size of message (bytes) = %d\n"
-                                 "default max size of queue (bytes) = %d\n",
-                                 msginfo.msgmni, msginfo.msgmax, msginfo.msgmnb);
+               printf("max queues system wide = %d\n"
+                               "max size of message (bytes) = %d\n"
+                               "default max size of queue (bytes) = %d\n",
+                               msginfo.msgmni, msginfo.msgmax, msginfo.msgmnb);
                return;
 
        case STATUS:
                printf("------ Message%s --------\n", "s: Status");
-               printf(   "allocated queues = %d\n"
-                                 "used headers = %d\n"
-                                 "used space = %d bytes\n",
-                                 msginfo.msgpool, msginfo.msgmap, msginfo.msgtql);
+               printf("allocated queues = %d\n"
+                               "used headers = %d\n"
+                               "used space = %d bytes\n",
+                               msginfo.msgpool, msginfo.msgmap, msginfo.msgtql);
                return;
 
        case CREATOR:
                printf("------ Message%s --------\n", " Queues: Creators/Owners");
-               printf(   "%-10s %-10s %-10s %-10s %-10s %-10s\n",
-                                 "msqid", "perms", "cuid", "cgid", "uid", "gid");
+               printf("%-10s %-10s %-10s %-10s %-10s %-10s\n",
+                               "msqid", "perms", "cuid", "cgid", "uid", "gid");
                break;
 
        case TIME:
                printf("------ Message%s --------\n", " Queues Send/Recv/Change Times");
-               printf(   "%-8s %-10s %-20s %-20s %-20s\n",
-                                 "msqid", "owner", "send", "recv", "change");
+               printf("%-8s %-10s %-20s %-20s %-20s\n",
+                               "msqid", "owner", "send", "recv", "change");
                break;
 
        case PID:
                printf("------ Message%s --------\n", " Queues PIDs");
-               printf(   "%-10s %-10s %-10s %-10s\n",
-                                 "msqid", "owner", "lspid", "lrpid");
+               printf("%-10s %-10s %-10s %-10s\n",
+                               "msqid", "owner", "lspid", "lrpid");
                break;
 
        default:
                printf("------ Message%s --------\n", " Queues");
-               printf(   "%-10s %-10s %-10s %-10s %-12s %-12s\n",
-                                 "key", "msqid", "owner", "perms", "used-bytes", "messages");
+               printf("%-10s %-10s %-10s %-10s %-12s %-12s\n",
+                               "key", "msqid", "owner", "perms", "used-bytes", "messages");
                break;
        }
 
@@ -422,11 +438,11 @@ static NOINLINE void do_msg(void)
                        else
                                printf("%-8d %-10d", msqid, ipcp->uid);
                        printf(" %-20.16s", msgque.msg_stime
-                                         ? ctime(&msgque.msg_stime) + 4 : "Not set");
+                                       ? ctime(&msgque.msg_stime) + 4 : "Not set");
                        printf(" %-20.16s", msgque.msg_rtime
-                                         ? ctime(&msgque.msg_rtime) + 4 : "Not set");
+                                       ? ctime(&msgque.msg_rtime) + 4 : "Not set");
                        printf(" %-20.16s\n", msgque.msg_ctime
-                                         ? ctime(&msgque.msg_ctime) + 4 : "Not set");
+                                       ? ctime(&msgque.msg_ctime) + 4 : "Not set");
                        break;
                case PID:
                        if (pw)
@@ -443,13 +459,13 @@ static NOINLINE void do_msg(void)
                        else
                                printf("%-10d %-10d", msqid, ipcp->uid);
                        printf(" %-10o %-12ld %-12ld\n", ipcp->mode & 0777,
-                                         /*
-                                          * glibc-2.1.3 and earlier has unsigned short;
-                                          * glibc-2.1.91 has variation between
-                                          * unsigned short, unsigned long
-                                          * Austin has msgqnum_t
-                                          */
-                                         (long) msgque.msg_cbytes, (long) msgque.msg_qnum);
+                                       /*
+                                        * glibc-2.1.3 and earlier has unsigned short;
+                                        * glibc-2.1.91 has variation between
+                                        * unsigned short, unsigned long
+                                        * Austin has msgqnum_t
+                                        */
+                                       (long) msgque.msg_cbytes, (long) msgque.msg_qnum);
                        break;
                }
        }
@@ -467,18 +483,18 @@ static void print_shm(int shmid)
        }
 
        printf("\nShared memory Segment shmid=%d\n"
-                         "uid=%d\tgid=%d\tcuid=%d\tcgid=%d\n"
-                         "mode=%#o\taccess_perms=%#o\n"
-                         "bytes=%ld\tlpid=%d\tcpid=%d\tnattch=%ld\n",
-                         shmid,
-                         ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid,
-                         ipcp->mode, ipcp->mode & 0777,
-                         (long) shmds.shm_segsz, shmds.shm_lpid, shmds.shm_cpid,
-                         (long) shmds.shm_nattch);
+                       "uid=%d\tgid=%d\tcuid=%d\tcgid=%d\n"
+                       "mode=%#o\taccess_perms=%#o\n"
+                       "bytes=%ld\tlpid=%d\tcpid=%d\tnattch=%ld\n",
+                       shmid,
+                       ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid,
+                       ipcp->mode, ipcp->mode & 0777,
+                       (long) shmds.shm_segsz, shmds.shm_lpid, shmds.shm_cpid,
+                       (long) shmds.shm_nattch);
        printf("att_time=%-26.24s\n",
-                         shmds.shm_atime ? ctime(&shmds.shm_atime) : "Not set");
+                       shmds.shm_atime ? ctime(&shmds.shm_atime) : "Not set");
        printf("det_time=%-26.24s\n",
-                         shmds.shm_dtime ? ctime(&shmds.shm_dtime) : "Not set");
+                       shmds.shm_dtime ? ctime(&shmds.shm_dtime) : "Not set");
        printf("change_time=%-26.24s\n\n", ctime(&shmds.shm_ctime));
 }
 
@@ -494,24 +510,24 @@ static void print_msg(int msqid)
        }
 
        printf("\nMessage Queue msqid=%d\n"
-                         "uid=%d\tgid=%d\tcuid=%d\tcgid=%d\tmode=%#o\n"
-                         "cbytes=%ld\tqbytes=%ld\tqnum=%ld\tlspid=%d\tlrpid=%d\n",
-                         msqid, ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid, ipcp->mode,
-                         /*
-                          * glibc-2.1.3 and earlier has unsigned short;
-                          * glibc-2.1.91 has variation between
-                          * unsigned short, unsigned long
-                          * Austin has msgqnum_t (for msg_qbytes)
-                          */
-                         (long) buf.msg_cbytes, (long) buf.msg_qbytes,
-                         (long) buf.msg_qnum, buf.msg_lspid, buf.msg_lrpid);
+                       "uid=%d\tgid=%d\tcuid=%d\tcgid=%d\tmode=%#o\n"
+                       "cbytes=%ld\tqbytes=%ld\tqnum=%ld\tlspid=%d\tlrpid=%d\n",
+                       msqid, ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid, ipcp->mode,
+                       /*
+                        * glibc-2.1.3 and earlier has unsigned short;
+                        * glibc-2.1.91 has variation between
+                        * unsigned short, unsigned long
+                        * Austin has msgqnum_t (for msg_qbytes)
+                        */
+                       (long) buf.msg_cbytes, (long) buf.msg_qbytes,
+                       (long) buf.msg_qnum, buf.msg_lspid, buf.msg_lrpid);
 
        printf("send_time=%-26.24s\n",
-                         buf.msg_stime ? ctime(&buf.msg_stime) : "Not set");
+                       buf.msg_stime ? ctime(&buf.msg_stime) : "Not set");
        printf("rcv_time=%-26.24s\n",
-                         buf.msg_rtime ? ctime(&buf.msg_rtime) : "Not set");
+                       buf.msg_rtime ? ctime(&buf.msg_rtime) : "Not set");
        printf("change_time=%-26.24s\n\n",
-                         buf.msg_ctime ? ctime(&buf.msg_ctime) : "Not set");
+                       buf.msg_ctime ? ctime(&buf.msg_ctime) : "Not set");
 }
 
 static void print_sem(int semid)
@@ -528,19 +544,19 @@ static void print_sem(int semid)
        }
 
        printf("\nSemaphore Array semid=%d\n"
-                         "uid=%d\t gid=%d\t cuid=%d\t cgid=%d\n"
-                         "mode=%#o, access_perms=%#o\n"
-                         "nsems = %ld\n"
-                         "otime = %-26.24s\n",
-                         semid,
-                         ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid,
-                         ipcp->mode, ipcp->mode & 0777,
-                         (long) semds.sem_nsems,
-                         semds.sem_otime ? ctime(&semds.sem_otime) : "Not set");
+                       "uid=%d\t gid=%d\t cuid=%d\t cgid=%d\n"
+                       "mode=%#o, access_perms=%#o\n"
+                       "nsems = %ld\n"
+                       "otime = %-26.24s\n",
+                       semid,
+                       ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid,
+                       ipcp->mode, ipcp->mode & 0777,
+                       (long) semds.sem_nsems,
+                       semds.sem_otime ? ctime(&semds.sem_otime) : "Not set");
        printf("ctime = %-26.24s\n"
-                         "%-10s %-10s %-10s %-10s %-10s\n",
-                         ctime(&semds.sem_ctime),
-                         "semnum", "value", "ncount", "zcount", "pid");
+                       "%-10s %-10s %-10s %-10s %-10s\n",
+                       ctime(&semds.sem_ctime),
+                       "semnum", "value", "ncount", "zcount", "pid");
 
        arg.val = 0;
        for (i = 0; i < semds.sem_nsems; i++) {
@@ -553,7 +569,7 @@ static void print_sem(int semid)
                if (val < 0 || ncnt < 0 || zcnt < 0 || pid < 0) {
                        bb_perror_msg_and_die("semctl");
                }
-               printf("%-10d %-10d %-10d %-10d %-10d\n", i, val, ncnt, zcnt, pid);
+               printf("%-10u %-10d %-10d %-10d %-10d\n", i, val, ncnt, zcnt, pid);
        }
        bb_putchar('\n');
 }
index 3873be3..d450b5a 100644 (file)
@@ -4,54 +4,55 @@
  *
  * Copyright (C) 2002  Matt Kraai.
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define losetup_trivial_usage
+//usage:       "[-r] [-o OFS] {-f|LOOPDEV} FILE - associate loop devices\n"
+//usage:       "       losetup -d LOOPDEV - disassociate\n"
+//usage:       "       losetup -a - show status\n"
+//usage:       "       losetup -f - show next free loop device"
+//usage:#define losetup_full_usage "\n\n"
+//usage:       "       -o OFS  Start OFS bytes into FILE"
+//usage:     "\n       -r      Read-only"
+//usage:     "\n       -f      Show/use next free loop device"
+//usage:
+//usage:#define losetup_notes_usage
+//usage:       "One argument (losetup /dev/loop1) will display the current association\n"
+//usage:       "(if any), or disassociate it (with -d). The display shows the offset\n"
+//usage:       "and filename of the file the loop device is currently bound to.\n\n"
+//usage:       "Two arguments (losetup /dev/loop1 file.img) create a new association,\n"
+//usage:       "with an optional offset (-o 12345). Encryption is not yet supported.\n"
+//usage:       "losetup -f will show the first loop free loop device\n\n"
+
 #include "libbb.h"
 
+/* 1048575 is a max possible minor number in Linux circa 2010 */
+/* for now use something less extreme */
+#define MAX_LOOP_NUM 1023
+
 int losetup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int losetup_main(int argc UNUSED_PARAM, char **argv)
 {
        unsigned opt;
-       int n;
        char *opt_o;
-       unsigned long long offset = 0;
+       char dev[LOOP_NAMESIZE];
        enum {
                OPT_d = (1 << 0),
                OPT_o = (1 << 1),
                OPT_f = (1 << 2),
+               OPT_a = (1 << 3),
+               OPT_r = (1 << 4), /* must be last */
        };
 
-       /* max 2 args, all opts are mutually exclusive */
-       opt_complementary = "?2:d--of:o--df:f--do";
-       opt = getopt32(argv, "do:f", &opt_o);
+       opt_complementary = "?2:d--ofar:a--ofr";
+       opt = getopt32(argv, "do:far", &opt_o);
        argv += optind;
 
-       if (opt == OPT_o)
-               offset = xatoull(opt_o);
-
-       if (opt == OPT_d) {
-               /* -d BLOCKDEV */
-               if (!argv[0] || argv[1])
-                       bb_show_usage();
-               if (del_loop(argv[0]))
-                       bb_simple_perror_msg_and_die(argv[0]);
-               return EXIT_SUCCESS;
-       }
-
-       if (argv[0]) {
+       /* LOOPDEV */
+       if (!opt && argv[0] && !argv[1]) {
                char *s;
 
-               if (opt == OPT_f) /* -f should not have arguments */
-                       bb_show_usage();
-
-               if (argv[1]) {
-                       /* [-o OFS] BLOCKDEV FILE */
-                       if (set_loop(&argv[0], argv[1], offset) < 0)
-                               bb_simple_perror_msg_and_die(argv[0]);
-                       return EXIT_SUCCESS;
-               }
-               /* [-o OFS] BLOCKDEV */
                s = query_loop(argv[0]);
                if (!s)
                        bb_simple_perror_msg_and_die(argv[0]);
@@ -61,28 +62,65 @@ int losetup_main(int argc UNUSED_PARAM, char **argv)
                return EXIT_SUCCESS;
        }
 
-       /* [-o OFS|-f] with no params */
-       n = 0;
-       while (1) {
-               char *s;
-               char dev[LOOP_NAMESIZE];
-
-               sprintf(dev, LOOP_FORMAT, n);
-               s = query_loop(dev);
-               n++;
-               if (!s) {
-                       if (n > 9 && errno && errno != ENXIO)
-                               return EXIT_SUCCESS;
-                       if (opt == OPT_f) {
-                               puts(dev);
-                               return EXIT_SUCCESS;
-                       }
-               } else {
-                       if (opt != OPT_f)
+       /* -d LOOPDEV */
+       if (opt == OPT_d && argv[0]) {
+               if (del_loop(argv[0]))
+                       bb_simple_perror_msg_and_die(argv[0]);
+               return EXIT_SUCCESS;
+       }
+
+       /* -a */
+       if (opt == OPT_a) {
+               int n;
+               for (n = 0; n < MAX_LOOP_NUM; n++) {
+                       char *s;
+
+                       sprintf(dev, LOOP_FORMAT, n);
+                       s = query_loop(dev);
+                       if (s) {
                                printf("%s: %s\n", dev, s);
-                       if (ENABLE_FEATURE_CLEAN_UP)
                                free(s);
+                       }
+               }
+               return EXIT_SUCCESS;
+       }
+
+       /* contains -f */
+       if (opt & OPT_f) {
+               char *s;
+               int n = 0;
+
+               do {
+                       if (n > MAX_LOOP_NUM)
+                               bb_error_msg_and_die("no free loop devices");
+                       sprintf(dev, LOOP_FORMAT, n++);
+                       s = query_loop(dev);
+                       free(s);
+               } while (s);
+               /* now: dev is next free "/dev/loopN" */
+               if ((opt == OPT_f) && !argv[0]) {
+                       puts(dev);
+                       return EXIT_SUCCESS;
                }
        }
-       return EXIT_SUCCESS;
+
+       /* [-r] [-o OFS] {-f|LOOPDEV} FILE */
+       if (argv[0] && ((opt & OPT_f) || argv[1])) {
+               unsigned long long offset = 0;
+               char *d = dev;
+
+               if (opt & OPT_o)
+                       offset = xatoull(opt_o);
+               if (!(opt & OPT_f))
+                       d = *argv++;
+
+               if (argv[0]) {
+                       if (set_loop(&d, argv[0], offset, (opt & OPT_r)) < 0)
+                               bb_simple_perror_msg_and_die(argv[0]);
+                       return EXIT_SUCCESS;
+               }
+       }
+
+       bb_show_usage(); /* does not return */
+       /*return EXIT_FAILURE;*/
 }
index 514c3a4..514678a 100644 (file)
@@ -4,8 +4,17 @@
  *
  * Copyright (C) 2009  Malek Degachi <malek-degachi@laposte.net>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define lspci_trivial_usage
+//usage:       "[-mk]"
+//usage:#define lspci_full_usage "\n\n"
+//usage:       "List all PCI devices"
+//usage:     "\n"
+//usage:     "\n       -m      Parsable output"
+//usage:     "\n       -k      Show driver"
+
 #include "libbb.h"
 
 enum {
@@ -65,11 +74,11 @@ static int FAST_FUNC fileAction(
 
        if (option_mask32 & OPT_m) {
                printf("%s \"Class %04x\" \"%04x\" \"%04x\" \"%04x\" \"%04x\"",
-                      pci_slot_name, pci_class, pci_vid, pci_did,
-                      pci_subsys_vid, pci_subsys_did);
+                       pci_slot_name, pci_class, pci_vid, pci_did,
+                       pci_subsys_vid, pci_subsys_did);
        } else {
                printf("%s Class %04x: %04x:%04x",
-                      pci_slot_name, pci_class, pci_vid, pci_did);
+                       pci_slot_name, pci_class, pci_vid, pci_did);
        }
 
        if ((option_mask32 & OPT_k) && driver) {
index 23b0349..540f21e 100644 (file)
@@ -4,8 +4,12 @@
  *
  * Copyright (C) 2009  Malek Degachi <malek-degachi@laposte.net>
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define lsusb_trivial_usage NOUSAGE_STR
+//usage:#define lsusb_full_usage ""
+
 #include "libbb.h"
 
 static int FAST_FUNC fileAction(
index b4042c0..e80b58f 100644 (file)
@@ -5,8 +5,97 @@
  * Copyright 2005 Rob Landley <rob@landley.net>
  * Copyright 2005 Frank Sorenson <frank@tuxrocks.com>
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//config:config MDEV
+//config:      bool "mdev"
+//config:      default y
+//config:      select PLATFORM_LINUX
+//config:      help
+//config:        mdev is a mini-udev implementation for dynamically creating device
+//config:        nodes in the /dev directory.
+//config:
+//config:        For more information, please see docs/mdev.txt
+//config:
+//config:config FEATURE_MDEV_CONF
+//config:      bool "Support /etc/mdev.conf"
+//config:      default y
+//config:      depends on MDEV
+//config:      help
+//config:        Add support for the mdev config file to control ownership and
+//config:        permissions of the device nodes.
+//config:
+//config:        For more information, please see docs/mdev.txt
+//config:
+//config:config FEATURE_MDEV_RENAME
+//config:      bool "Support subdirs/symlinks"
+//config:      default y
+//config:      depends on FEATURE_MDEV_CONF
+//config:      help
+//config:        Add support for renaming devices and creating symlinks.
+//config:
+//config:        For more information, please see docs/mdev.txt
+//config:
+//config:config FEATURE_MDEV_RENAME_REGEXP
+//config:      bool "Support regular expressions substitutions when renaming device"
+//config:      default y
+//config:      depends on FEATURE_MDEV_RENAME
+//config:      help
+//config:        Add support for regular expressions substitutions when renaming
+//config:        device.
+//config:
+//config:config FEATURE_MDEV_EXEC
+//config:      bool "Support command execution at device addition/removal"
+//config:      default y
+//config:      depends on FEATURE_MDEV_CONF
+//config:      help
+//config:        This adds support for an optional field to /etc/mdev.conf for
+//config:        executing commands when devices are created/removed.
+//config:
+//config:        For more information, please see docs/mdev.txt
+//config:
+//config:config FEATURE_MDEV_LOAD_FIRMWARE
+//config:      bool "Support loading of firmwares"
+//config:      default y
+//config:      depends on MDEV
+//config:      help
+//config:        Some devices need to load firmware before they can be usable.
+//config:
+//config:        These devices will request userspace look up the files in
+//config:        /lib/firmware/ and if it exists, send it to the kernel for
+//config:        loading into the hardware.
+
+//applet:IF_MDEV(APPLET(mdev, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MDEV) += mdev.o
+
+//usage:#define mdev_trivial_usage
+//usage:       "[-s]"
+//usage:#define mdev_full_usage "\n\n"
+//usage:       "mdev -s is to be run during boot to scan /sys and populate /dev.\n"
+//usage:       "\n"
+//usage:       "Bare mdev is a kernel hotplug helper. To activate it:\n"
+//usage:       "       echo /sbin/mdev >/proc/sys/kernel/hotplug\n"
+//usage:       IF_FEATURE_MDEV_CONF(
+//usage:       "\n"
+//usage:       "It uses /etc/mdev.conf with lines\n"
+//usage:       "       [-][ENV=regex;]...DEVNAME UID:GID PERM"
+//usage:                       IF_FEATURE_MDEV_RENAME(" [>|=PATH]|[!]")
+//usage:                       IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]")
+//usage:       "\n"
+//usage:       "where DEVNAME is device name regex, @major,minor[-minor2], or\n"
+//usage:       "environment variable regex. A common use of the latter is\n"
+//usage:       "to load modules for hotplugged devices:\n"
+//usage:       "       $MODALIAS=.* 0:0 660 @modprobe \"$MODALIAS\"\n"
+//usage:       )
+//usage:       "\n"
+//usage:       "If /dev/mdev.seq file exists, mdev will wait for its value\n"
+//usage:       "to match $SEQNUM variable. This prevents plug/unplug races.\n"
+//usage:       "To activate this feature, create empty /dev/mdev.seq at boot.\n"
+//usage:       "\n"
+//usage:       "If /dev/mdev.log file exists, debug log will be appended to it."
+
 #include "libbb.h"
 #include "xregex.h"
 
  * (todo: explain "delete" and $FIRMWARE)
  *
  * If /etc/mdev.conf exists, it may modify /dev/device_name's properties.
- * /etc/mdev.conf file format:
- *
- * [-][subsystem/]device  user:grp  mode  [>|=path] [@|$|*command args...]
- * [-]@maj,min[-min2]     user:grp  mode  [>|=path] [@|$|*command args...]
- * [-]$envvar=val         user:grp  mode  [>|=path] [@|$|*command args...]
  *
  * Leading minus in 1st field means "don't stop on this line", otherwise
  * search is stopped after the matching line is encountered.
  *
- * The device name or "subsystem/device" combo is matched against 1st field
- * (which is a regex), or maj,min is matched against 1st field,
- * or specified environment variable (as regex) is matched against 1st field.
- *
- * $envvar=val format is useful for loading modules for hot-plugged devices
+ * $envvar=regex format is useful for loading modules for hot-plugged devices
  * which do not have driver loaded yet. In this case /sys/class/.../dev
  * does not exist, but $MODALIAS is set to needed module's name
  * (actually, an alias to it) by kernel. This rule instructs mdev
  * This happens regardless of /sys/class/.../dev existence.
  */
 
+/* Kernel's hotplug environment constantly changes.
+ * Here are new cases I observed on 3.1.0:
+ *
+ * Case with $DEVNAME and $DEVICE, not just $DEVPATH:
+ * ACTION=add
+ * BUSNUM=001
+ * DEVICE=/proc/bus/usb/001/003
+ * DEVNAME=bus/usb/001/003
+ * DEVNUM=003
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5
+ * DEVTYPE=usb_device
+ * MAJOR=189
+ * MINOR=2
+ * PRODUCT=18d1/4e12/227
+ * SUBSYSTEM=usb
+ * TYPE=0/0/0
+ *
+ * Case with $DEVICE, but no $DEVNAME - apparenty, usb iface notification?
+ * "Please load me a module" thing?
+ * ACTION=add
+ * DEVICE=/proc/bus/usb/001/003
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0
+ * DEVTYPE=usb_interface
+ * INTERFACE=8/6/80
+ * MODALIAS=usb:v18D1p4E12d0227dc00dsc00dp00ic08isc06ip50
+ * PRODUCT=18d1/4e12/227
+ * SUBSYSTEM=usb
+ * TYPE=0/0/0
+ *
+ * ACTION=add
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5
+ * DEVTYPE=scsi_host
+ * SUBSYSTEM=scsi
+ *
+ * ACTION=add
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/scsi_host/host5
+ * SUBSYSTEM=scsi_host
+ *
+ * ACTION=add
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0
+ * DEVTYPE=scsi_target
+ * SUBSYSTEM=scsi
+ *
+ * Case with strange $MODALIAS:
+ * ACTION=add
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0
+ * DEVTYPE=scsi_device
+ * MODALIAS=scsi:t-0x00
+ * SUBSYSTEM=scsi
+ *
+ * ACTION=add
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/scsi_disk/5:0:0:0
+ * SUBSYSTEM=scsi_disk
+ *
+ * ACTION=add
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/scsi_device/5:0:0:0
+ * SUBSYSTEM=scsi_device
+ *
+ * Case with explicit $MAJOR/$MINOR (no need to read /sys/$DEVPATH/dev?):
+ * ACTION=add
+ * DEVNAME=bsg/5:0:0:0
+ * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/bsg/5:0:0:0
+ * MAJOR=253
+ * MINOR=1
+ * SUBSYSTEM=bsg
+ *
+ * ACTION=add
+ * DEVPATH=/devices/virtual/bdi/8:16
+ * SUBSYSTEM=bdi
+ *
+ * ACTION=add
+ * DEVNAME=sdb
+ * DEVPATH=/block/sdb
+ * DEVTYPE=disk
+ * MAJOR=8
+ * MINOR=16
+ * SUBSYSTEM=block
+ *
+ * Case with ACTION=change:
+ * ACTION=change
+ * DEVNAME=sdb
+ * DEVPATH=/block/sdb
+ * DEVTYPE=disk
+ * DISK_MEDIA_CHANGE=1
+ * MAJOR=8
+ * MINOR=16
+ * SUBSYSTEM=block
+ */
+
+#define DEBUG_LVL 2
+
+#if DEBUG_LVL >= 1
+# define dbg1(...) do { if (G.verbose) bb_error_msg(__VA_ARGS__); } while(0)
+#else
+# define dbg1(...) ((void)0)
+#endif
+#if DEBUG_LVL >= 2
+# define dbg2(...) do { if (G.verbose >= 2) bb_error_msg(__VA_ARGS__); } while(0)
+#else
+# define dbg2(...) ((void)0)
+#endif
+#if DEBUG_LVL >= 3
+# define dbg3(...) do { if (G.verbose >= 3) bb_error_msg(__VA_ARGS__); } while(0)
+#else
+# define dbg3(...) ((void)0)
+#endif
+
+
+static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0"
+enum { OP_add, OP_remove };
+
+struct envmatch {
+       struct envmatch *next;
+       char *envname;
+       regex_t match;
+};
+
+struct rule {
+       bool keep_matching;
+       bool regex_compiled;
+       mode_t mode;
+       int maj, min0, min1;
+       struct bb_uidgid_t ugid;
+       char *envvar;
+       char *ren_mov;
+       IF_FEATURE_MDEV_EXEC(char *r_cmd;)
+       regex_t match;
+       struct envmatch *envmatch;
+};
+
 struct globals {
        int root_major, root_minor;
+       smallint verbose;
        char *subsystem;
+       char *subsys_env; /* for putenv("SUBSYSTEM=subsystem") */
+#if ENABLE_FEATURE_MDEV_CONF
+       const char *filename;
+       parser_t *parser;
+       struct rule **rule_vec;
+       unsigned rule_idx;
+#endif
+       struct rule cur_rule;
+       char timestr[sizeof("60.123456")];
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { \
+       IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.maj = -1;) \
+       IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.mode = 0660;) \
+} while (0)
+
 
 /* Prevent infinite loops in /sys symlinks */
 #define MAX_SYSFS_DEPTH 3
 
-/* We use additional 64+ bytes in make_device() */
-#define SCRATCH_SIZE 80
+/* We use additional bytes in make_device() */
+#define SCRATCH_SIZE 128
+
+#if ENABLE_FEATURE_MDEV_CONF
+
+static void make_default_cur_rule(void)
+{
+       memset(&G.cur_rule, 0, sizeof(G.cur_rule));
+       G.cur_rule.maj = -1; /* "not a @major,minor rule" */
+       G.cur_rule.mode = 0660;
+}
+
+static void clean_up_cur_rule(void)
+{
+       struct envmatch *e;
+
+       free(G.cur_rule.envvar);
+       free(G.cur_rule.ren_mov);
+       if (G.cur_rule.regex_compiled)
+               regfree(&G.cur_rule.match);
+       IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);)
+       e = G.cur_rule.envmatch;
+       while (e) {
+               free(e->envname);
+               regfree(&e->match);
+               e = e->next;
+       }
+       make_default_cur_rule();
+}
+
+static char *parse_envmatch_pfx(char *val)
+{
+       struct envmatch **nextp = &G.cur_rule.envmatch;
+
+       for (;;) {
+               struct envmatch *e;
+               char *semicolon;
+               char *eq = strchr(val, '=');
+               if (!eq /* || eq == val? */)
+                       return val;
+               if (endofname(val) != eq)
+                       return val;
+               semicolon = strchr(eq, ';');
+               if (!semicolon)
+                       return val;
+               /* ENVVAR=regex;... */
+               *nextp = e = xzalloc(sizeof(*e));
+               nextp = &e->next;
+               e->envname = xstrndup(val, eq - val);
+               *semicolon = '\0';
+               xregcomp(&e->match, eq + 1, REG_EXTENDED);
+               *semicolon = ';';
+               val = semicolon + 1;
+       }
+}
+
+static void parse_next_rule(void)
+{
+       /* Note: on entry, G.cur_rule is set to default */
+       while (1) {
+               char *tokens[4];
+               char *val;
+
+               /* No PARSE_EOL_COMMENTS, because command may contain '#' chars */
+               if (!config_read(G.parser, tokens, 4, 3, "# \t", PARSE_NORMAL & ~PARSE_EOL_COMMENTS))
+                       break;
+
+               /* Fields: [-]regex uid:gid mode [alias] [cmd] */
+               dbg3("token1:'%s'", tokens[1]);
+
+               /* 1st field */
+               val = tokens[0];
+               G.cur_rule.keep_matching = ('-' == val[0]);
+               val += G.cur_rule.keep_matching; /* swallow leading dash */
+               val = parse_envmatch_pfx(val);
+               if (val[0] == '@') {
+                       /* @major,minor[-minor2] */
+                       /* (useful when name is ambiguous:
+                        * "/sys/class/usb/lp0" and
+                        * "/sys/class/printer/lp0")
+                        */
+                       int sc = sscanf(val, "@%u,%u-%u", &G.cur_rule.maj, &G.cur_rule.min0, &G.cur_rule.min1);
+                       if (sc < 2 || G.cur_rule.maj < 0) {
+                               bb_error_msg("bad @maj,min on line %d", G.parser->lineno);
+                               goto next_rule;
+                       }
+                       if (sc == 2)
+                               G.cur_rule.min1 = G.cur_rule.min0;
+               } else {
+                       char *eq = strchr(val, '=');
+                       if (val[0] == '$') {
+                               /* $ENVVAR=regex ... */
+                               val++;
+                               if (!eq) {
+                                       bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno);
+                                       goto next_rule;
+                               }
+                               G.cur_rule.envvar = xstrndup(val, eq - val);
+                               val = eq + 1;
+                       }
+                       xregcomp(&G.cur_rule.match, val, REG_EXTENDED);
+                       G.cur_rule.regex_compiled = 1;
+               }
+
+               /* 2nd field: uid:gid - device ownership */
+               if (get_uidgid(&G.cur_rule.ugid, tokens[1], /*allow_numeric:*/ 1) == 0) {
+                       bb_error_msg("unknown user/group '%s' on line %d", tokens[1], G.parser->lineno);
+                       goto next_rule;
+               }
+
+               /* 3rd field: mode - device permissions */
+               bb_parse_mode(tokens[2], &G.cur_rule.mode);
+
+               /* 4th field (opt): ">|=alias" or "!" to not create the node */
+               val = tokens[3];
+               if (ENABLE_FEATURE_MDEV_RENAME && val && strchr(">=!", val[0])) {
+                       char *s = skip_non_whitespace(val);
+                       G.cur_rule.ren_mov = xstrndup(val, s - val);
+                       val = skip_whitespace(s);
+               }
+
+               if (ENABLE_FEATURE_MDEV_EXEC && val && val[0]) {
+                       const char *s = "$@*";
+                       const char *s2 = strchr(s, val[0]);
+                       if (!s2) {
+                               bb_error_msg("bad line %u", G.parser->lineno);
+                               goto next_rule;
+                       }
+                       IF_FEATURE_MDEV_EXEC(G.cur_rule.r_cmd = xstrdup(val);)
+               }
+
+               return;
+ next_rule:
+               clean_up_cur_rule();
+       } /* while (config_read) */
+
+       dbg3("config_close(G.parser)");
+       config_close(G.parser);
+       G.parser = NULL;
+
+       return;
+}
+
+/* If mdev -s, we remember rules in G.rule_vec[].
+ * Otherwise, there is no point in doing it, and we just
+ * save only one parsed rule in G.cur_rule.
+ */
+static const struct rule *next_rule(void)
+{
+       struct rule *rule;
+
+       /* Open conf file if we didn't do it yet */
+       if (!G.parser && G.filename) {
+               dbg3("config_open('%s')", G.filename);
+               G.parser = config_open2(G.filename, fopen_for_read);
+               G.filename = NULL;
+       }
+
+       if (G.rule_vec) {
+               /* mdev -s */
+               /* Do we have rule parsed already? */
+               if (G.rule_vec[G.rule_idx]) {
+                       dbg3("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
+                       return G.rule_vec[G.rule_idx++];
+               }
+               make_default_cur_rule();
+       } else {
+               /* not mdev -s */
+               clean_up_cur_rule();
+       }
+
+       /* Parse one more rule if file isn't fully read */
+       rule = &G.cur_rule;
+       if (G.parser) {
+               parse_next_rule();
+               if (G.rule_vec) { /* mdev -s */
+                       rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule));
+                       G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx);
+                       G.rule_vec[G.rule_idx++] = rule;
+                       dbg3("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
+               }
+       }
+
+       return rule;
+}
+
+static int env_matches(struct envmatch *e)
+{
+       while (e) {
+               int r;
+               char *val = getenv(e->envname);
+               if (!val)
+                       return 0;
+               r = regexec(&e->match, val, /*size*/ 0, /*range[]*/ NULL, /*eflags*/ 0);
+               if (r != 0) /* no match */
+                       return 0;
+               e = e->next;
+       }
+       return 1;
+}
+
+#else
+
+# define next_rule() (&G.cur_rule)
+
+#endif
+
+static void mkdir_recursive(char *name)
+{
+       /* if name has many levels ("dir1/dir2"),
+        * bb_make_directory() will create dir1 according to umask,
+        * not according to its "mode" parameter.
+        * Since we run with umask=0, need to temporarily switch it.
+        */
+       umask(022); /* "dir1" (if any) will be 0755 too */
+       bb_make_directory(name, 0755, FILEUTILS_RECUR);
+       umask(0);
+}
 
 /* Builds an alias path.
  * This function potentionally reallocates the alias parameter.
@@ -86,7 +527,7 @@ static char *build_alias(char *alias, const char *device_name)
        dest = strrchr(alias, '/');
        if (dest) { /* ">bar/[baz]" ? */
                *dest = '\0'; /* mkdir bar */
-               bb_make_directory(alias, 0755, FILEUTILS_RECUR);
+               mkdir_recursive(alias);
                *dest = '/';
                if (dest[1] == '\0') { /* ">bar/" => ">bar/device_name" */
                        dest = alias;
@@ -100,16 +541,17 @@ static char *build_alias(char *alias, const char *device_name)
 
 /* mknod in /dev based on a path like "/sys/block/hda/hda1"
  * NB1: path parameter needs to have SCRATCH_SIZE scratch bytes
- * after NUL, but we promise to not mangle (IOW: to restore if needed)
+ * after NUL, but we promise to not mangle (IOW: to restore NUL if needed)
  * path string.
  * NB2: "mdev -s" may call us many times, do not leak memory/fds!
+ *
+ * device_name = $DEVNAME (may be NULL)
+ * path        = /sys/$DEVPATH
  */
-static void make_device(char *path, int delete)
+static void make_device(char *device_name, char *path, int operation)
 {
-       char *device_name, *subsystem_slash_devname;
        int major, minor, type, len;
-       mode_t mode;
-       parser_t *parser;
+       char *path_end = path + strlen(path);
 
        /* Try to read major/minor string.  Note that the kernel puts \n after
         * the data, so we don't need to worry about null terminating the string
@@ -117,291 +559,253 @@ static void make_device(char *path, int delete)
         * We also depend on path having writeable space after it.
         */
        major = -1;
-       if (!delete) {
-               char *dev_maj_min = path + strlen(path);
-
-               strcpy(dev_maj_min, "/dev");
-               len = open_read_close(path, dev_maj_min + 1, 64);
-               *dev_maj_min = '\0';
+       if (operation == OP_add) {
+               strcpy(path_end, "/dev");
+               len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1);
+               *path_end = '\0';
                if (len < 1) {
                        if (!ENABLE_FEATURE_MDEV_EXEC)
                                return;
                        /* no "dev" file, but we can still run scripts
                         * based on device name */
-               } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) != 2) {
+               } else if (sscanf(path_end + 1, "%u:%u", &major, &minor) == 2) {
+                       dbg1("dev %u,%u", major, minor);
+               } else {
                        major = -1;
                }
        }
-
-       /* Determine device name, type, major and minor */
-       device_name = (char*) bb_basename(path);
-       /* http://kernel.org/doc/pending/hotplug.txt says that only
+       /* else: for delete, -1 still deletes the node, but < -1 suppresses that */
+
+       /* Determine device name */
+       if (!device_name) {
+               /*
+                * There was no $DEVNAME envvar (for example, mdev -s never has).
+                * But it is very useful: it contains the *path*, not only basename,
+                * Thankfully, uevent file has it.
+                * Example of .../sound/card0/controlC0/uevent file on Linux-3.7.7:
+                * MAJOR=116
+                * MINOR=7
+                * DEVNAME=snd/controlC0
+                */
+               strcpy(path_end, "/uevent");
+               len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1);
+               if (len < 0)
+                       len = 0;
+               *path_end = '\0';
+               path_end[1 + len] = '\0';
+               device_name = strstr(path_end + 1, "\nDEVNAME=");
+               if (device_name) {
+                       device_name += sizeof("\nDEVNAME=")-1;
+                       strchrnul(device_name, '\n')[0] = '\0';
+               } else {
+                       /* Fall back to just basename */
+                       device_name = (char*) bb_basename(path);
+               }
+       }
+       /* Determine device type */
+       /*
+        * http://kernel.org/doc/pending/hotplug.txt says that only
         * "/sys/block/..." is for block devices. "/sys/bus" etc is not.
         * But since 2.6.25 block devices are also in /sys/class/block.
-        * We use strstr("/block/") to forestall future surprises. */
+        * We use strstr("/block/") to forestall future surprises.
+        */
        type = S_IFCHR;
        if (strstr(path, "/block/") || (G.subsystem && strncmp(G.subsystem, "block", 5) == 0))
                type = S_IFBLK;
 
-       /* Make path point to "subsystem/device_name" */
-       subsystem_slash_devname = NULL;
-       /* Check for coldplug invocations first */
-       if (strncmp(path, "/sys/block/", 11) == 0) /* legacy case */
-               path += sizeof("/sys/") - 1;
-       else if (strncmp(path, "/sys/class/", 11) == 0)
-               path += sizeof("/sys/class/") - 1;
-       else {
-               /* Example of a hotplug invocation:
-                * SUBSYSTEM="block"
-                * DEVPATH="/sys" + "/devices/virtual/mtd/mtd3/mtdblock3"
-                * ("/sys" is added by mdev_main)
-                * - path does not contain subsystem
-                */
-               subsystem_slash_devname = concat_path_file(G.subsystem, device_name);
-               path = subsystem_slash_devname;
-       }
-
-       /* If we have config file, look up user settings */
-       if (ENABLE_FEATURE_MDEV_CONF)
-               parser = config_open2("/etc/mdev.conf", fopen_for_read);
-
-       do {
-               int keep_matching;
-               struct bb_uidgid_t ugid;
-               char *tokens[4];
-               char *command = NULL;
-               char *alias = NULL;
+#if ENABLE_FEATURE_MDEV_CONF
+       G.rule_idx = 0; /* restart from the beginning (think mdev -s) */
+#endif
+       for (;;) {
+               const char *str_to_match;
+               regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP];
+               char *command;
+               char *alias;
                char aliaslink = aliaslink; /* for compiler */
-
-               /* Defaults in case we won't match any line */
-               ugid.uid = ugid.gid = 0;
-               keep_matching = 0;
-               mode = 0660;
-
-               if (ENABLE_FEATURE_MDEV_CONF
-                && config_read(parser, tokens, 4, 3, "# \t", PARSE_NORMAL)
-               ) {
-                       char *val;
-                       char *str_to_match;
-                       regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP];
-
-                       val = tokens[0];
-                       keep_matching = ('-' == val[0]);
-                       val += keep_matching; /* swallow leading dash */
-
-                       /* Match against either "subsystem/device_name"
-                        * or "device_name" alone */
-                       str_to_match = strchr(val, '/') ? path : device_name;
-
-                       /* Fields: regex uid:gid mode [alias] [cmd] */
-
-                       if (val[0] == '@') {
-                               /* @major,minor[-minor2] */
-                               /* (useful when name is ambiguous:
-                                * "/sys/class/usb/lp0" and
-                                * "/sys/class/printer/lp0") */
-                               int cmaj, cmin0, cmin1, sc;
-                               if (major < 0)
-                                       continue; /* no dev, no match */
-                               sc = sscanf(val, "@%u,%u-%u", &cmaj, &cmin0, &cmin1);
-                               if (sc < 1
-                                || major != cmaj
-                                || (sc == 2 && minor != cmin0)
-                                || (sc == 3 && (minor < cmin0 || minor > cmin1))
-                               ) {
-                                       continue; /* this line doesn't match */
-                               }
-                               goto line_matches;
-                       }
-                       if (val[0] == '$') {
-                               /* regex to match an environment variable */
-                               char *eq = strchr(++val, '=');
-                               if (!eq)
-                                       continue;
-                               *eq = '\0';
-                               str_to_match = getenv(val);
-                               if (!str_to_match)
-                                       continue;
-                               str_to_match -= strlen(val) + 1;
-                               *eq = '=';
+               char *node_name;
+               const struct rule *rule;
+
+               str_to_match = device_name;
+
+               rule = next_rule();
+
+#if ENABLE_FEATURE_MDEV_CONF
+               if (!env_matches(rule->envmatch))
+                       continue;
+               if (rule->maj >= 0) {  /* @maj,min rule */
+                       if (major != rule->maj)
+                               continue;
+                       if (minor < rule->min0 || minor > rule->min1)
+                               continue;
+                       memset(off, 0, sizeof(off));
+                       goto rule_matches;
+               }
+               if (rule->envvar) { /* $envvar=regex rule */
+                       str_to_match = getenv(rule->envvar);
+                       dbg3("getenv('%s'):'%s'", rule->envvar, str_to_match);
+                       if (!str_to_match)
+                               continue;
+               }
+               /* else: str_to_match = device_name */
+
+               if (rule->regex_compiled) {
+                       int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0);
+                       dbg3("regex_match for '%s':%d", str_to_match, regex_match);
+                       //bb_error_msg("matches:");
+                       //for (int i = 0; i < ARRAY_SIZE(off); i++) {
+                       //      if (off[i].rm_so < 0) continue;
+                       //      bb_error_msg("match %d: '%.*s'\n", i,
+                       //              (int)(off[i].rm_eo - off[i].rm_so),
+                       //              device_name + off[i].rm_so);
+                       //}
+
+                       if (regex_match != 0
+                       /* regexec returns whole pattern as "range" 0 */
+                        || off[0].rm_so != 0
+                        || (int)off[0].rm_eo != (int)strlen(str_to_match)
+                       ) {
+                               continue; /* this rule doesn't match */
                        }
-                       /* else: regex to match [subsystem/]device_name */
-
-                       {
-                               regex_t match;
-                               int result;
-
-                               xregcomp(&match, val, REG_EXTENDED);
-                               result = regexec(&match, str_to_match, ARRAY_SIZE(off), off, 0);
-                               regfree(&match);
-                               //bb_error_msg("matches:");
-                               //for (int i = 0; i < ARRAY_SIZE(off); i++) {
-                               //      if (off[i].rm_so < 0) continue;
-                               //      bb_error_msg("match %d: '%.*s'\n", i,
-                               //              (int)(off[i].rm_eo - off[i].rm_so),
-                               //              device_name + off[i].rm_so);
-                               //}
-
-                               /* If no match, skip rest of line */
-                               /* (regexec returns whole pattern as "range" 0) */
-                               if (result
-                                || off[0].rm_so
-                                || ((int)off[0].rm_eo != (int)strlen(str_to_match))
-                               ) {
-                                       continue; /* this line doesn't match */
-                               }
+               }
+               /* else: it's final implicit "match-all" rule */
+ rule_matches:
+               dbg2("rule matched, line %d", G.parser ? G.parser->lineno : -1);
+#endif
+               /* Build alias name */
+               alias = NULL;
+               if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) {
+                       aliaslink = rule->ren_mov[0];
+                       if (aliaslink == '!') {
+                               /* "!": suppress node creation/deletion */
+                               major = -2;
                        }
- line_matches:
-                       /* This line matches. Stop parsing after parsing
-                        * the rest the line unless keep_matching == 1 */
-
-                       /* 2nd field: uid:gid - device ownership */
-                       if (get_uidgid(&ugid, tokens[1], 1) == 0)
-                               bb_error_msg("unknown user/group %s on line %d", tokens[1], parser->lineno);
-
-                       /* 3rd field: mode - device permissions */
-                       bb_parse_mode(tokens[2], &mode);
-
-                       val = tokens[3];
-                       /* 4th field (opt): ">|=alias" or "!" to not create the node */
-
-                       if (ENABLE_FEATURE_MDEV_RENAME && val) {
-                               char *a, *s, *st;
-
-                               a = val;
-                               s = strchrnul(val, ' ');
-                               st = strchrnul(val, '\t');
-                               if (st < s)
-                                       s = st;
-                               st = (s[0] && s[1]) ? s+1 : NULL;
-
-                               aliaslink = a[0];
-                               if (aliaslink == '!' && s == a+1) {
-                                       val = st;
-                                       /* "!": suppress node creation/deletion */
-                                       major = -1;
-                               }
-                               else if (aliaslink == '>' || aliaslink == '=') {
-                                       val = st;
-                                       s[0] = '\0';
-                                       if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) {
-                                               char *p;
-                                               unsigned i, n;
-
-                                               /* substitute %1..9 with off[1..9], if any */
-                                               n = 0;
-                                               s = a;
-                                               while (*s)
-                                                       if (*s++ == '%')
-                                                               n++;
-
-                                               p = alias = xzalloc(strlen(a) + n * strlen(str_to_match));
-                                               s = a + 1;
-                                               while (*s) {
-                                                       *p = *s;
-                                                       if ('%' == *s) {
-                                                               i = (s[1] - '0');
-                                                               if (i <= 9 && off[i].rm_so >= 0) {
-                                                                       n = off[i].rm_eo - off[i].rm_so;
-                                                                       strncpy(p, str_to_match + off[i].rm_so, n);
-                                                                       p += n - 1;
-                                                                       s++;
-                                                               }
+                       else if (aliaslink == '>' || aliaslink == '=') {
+                               if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) {
+                                       char *s;
+                                       char *p;
+                                       unsigned n;
+
+                                       /* substitute %1..9 with off[1..9], if any */
+                                       n = 0;
+                                       s = rule->ren_mov;
+                                       while (*s)
+                                               if (*s++ == '%')
+                                                       n++;
+
+                                       p = alias = xzalloc(strlen(rule->ren_mov) + n * strlen(str_to_match));
+                                       s = rule->ren_mov + 1;
+                                       while (*s) {
+                                               *p = *s;
+                                               if ('%' == *s) {
+                                                       unsigned i = (s[1] - '0');
+                                                       if (i <= 9 && off[i].rm_so >= 0) {
+                                                               n = off[i].rm_eo - off[i].rm_so;
+                                                               strncpy(p, str_to_match + off[i].rm_so, n);
+                                                               p += n - 1;
+                                                               s++;
                                                        }
-                                                       p++;
-                                                       s++;
                                                }
-                                       } else {
-                                               alias = xstrdup(a + 1);
+                                               p++;
+                                               s++;
                                        }
+                               } else {
+                                       alias = xstrdup(rule->ren_mov + 1);
                                }
                        }
+               }
+               dbg3("alias:'%s'", alias);
 
-                       if (ENABLE_FEATURE_MDEV_EXEC && val) {
-                               const char *s = "$@*";
-                               const char *s2 = strchr(s, val[0]);
-
-                               if (!s2) {
-                                       bb_error_msg("bad line %u", parser->lineno);
-                                       if (ENABLE_FEATURE_MDEV_RENAME)
-                                               free(alias);
-                                       continue;
-                               }
-
-                               /* Are we running this command now?
-                                * Run $cmd on delete, @cmd on create, *cmd on both
-                                */
-                               if (s2 - s != delete) {
-                                       /* We are here if: '*',
-                                        * or: '@' and delete = 0,
-                                        * or: '$' and delete = 1
-                                        */
-                                       command = xstrdup(val + 1);
-                               }
+               command = NULL;
+               IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;)
+               if (command) {
+                       /* Are we running this command now?
+                        * Run @cmd on create, $cmd on delete, *cmd on any
+                        */
+                       if ((command[0] == '@' && operation == OP_add)
+                        || (command[0] == '$' && operation == OP_remove)
+                        || (command[0] == '*')
+                       ) {
+                               command++;
+                       } else {
+                               command = NULL;
                        }
                }
-
-               /* End of field parsing */
+               dbg3("command:'%s'", command);
 
                /* "Execute" the line we found */
-               {
-                       const char *node_name;
-
-                       node_name = device_name;
-                       if (ENABLE_FEATURE_MDEV_RENAME && alias)
-                               node_name = alias = build_alias(alias, device_name);
-
-                       if (!delete && major >= 0) {
-                               if (mknod(node_name, mode | type, makedev(major, minor)) && errno != EEXIST)
-                                       bb_perror_msg("can't create '%s'", node_name);
-                               if (major == G.root_major && minor == G.root_minor)
-                                       symlink(node_name, "root");
-                               if (ENABLE_FEATURE_MDEV_CONF) {
-                                       chmod(node_name, mode);
-                                       chown(node_name, ugid.uid, ugid.gid);
-                               }
-                               if (ENABLE_FEATURE_MDEV_RENAME && alias) {
-                                       if (aliaslink == '>')
-                                               symlink(node_name, device_name);
+               node_name = device_name;
+               if (ENABLE_FEATURE_MDEV_RENAME && alias) {
+                       node_name = alias = build_alias(alias, device_name);
+                       dbg3("alias2:'%s'", alias);
+               }
+
+               if (operation == OP_add && major >= 0) {
+                       char *slash = strrchr(node_name, '/');
+                       if (slash) {
+                               *slash = '\0';
+                               mkdir_recursive(node_name);
+                               *slash = '/';
+                       }
+                       if (ENABLE_FEATURE_MDEV_CONF) {
+                               dbg1("mknod %s (%d,%d) %o"
+                                       " %u:%u",
+                                       node_name, major, minor, rule->mode | type,
+                                       rule->ugid.uid, rule->ugid.gid
+                               );
+                       } else {
+                               dbg1("mknod %s (%d,%d) %o",
+                                       node_name, major, minor, rule->mode | type
+                               );
+                       }
+                       if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
+                               bb_perror_msg("can't create '%s'", node_name);
+                       if (ENABLE_FEATURE_MDEV_CONF) {
+                               chmod(node_name, rule->mode);
+                               chown(node_name, rule->ugid.uid, rule->ugid.gid);
+                       }
+                       if (major == G.root_major && minor == G.root_minor)
+                               symlink(node_name, "root");
+                       if (ENABLE_FEATURE_MDEV_RENAME && alias) {
+                               if (aliaslink == '>') {
+//TODO: on devtmpfs, device_name already exists and symlink() fails.
+//End result is that instead of symlink, we have two nodes.
+//What should be done?
+                                       dbg1("symlink: %s", device_name);
+                                       symlink(node_name, device_name);
                                }
                        }
+               }
 
-                       if (ENABLE_FEATURE_MDEV_EXEC && command) {
-                               /* setenv will leak memory, use putenv/unsetenv/free */
-                               char *s = xasprintf("%s=%s", "MDEV", node_name);
-                               char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
-                               putenv(s);
-                               putenv(s1);
-                               if (system(command) == -1)
-                                       bb_perror_msg("can't run '%s'", command);
-                               bb_unsetenv_and_free(s1);
-                               bb_unsetenv_and_free(s);
-                               free(command);
-                       }
+               if (ENABLE_FEATURE_MDEV_EXEC && command) {
+                       /* setenv will leak memory, use putenv/unsetenv/free */
+                       char *s = xasprintf("%s=%s", "MDEV", node_name);
+                       putenv(s);
+                       dbg1("running: %s", command);
+                       if (system(command) == -1)
+                               bb_perror_msg("can't run '%s'", command);
+                       bb_unsetenv_and_free(s);
+               }
 
-                       if (delete && major >= 0) {
-                               if (ENABLE_FEATURE_MDEV_RENAME && alias) {
-                                       if (aliaslink == '>')
-                                               unlink(device_name);
+               if (operation == OP_remove && major >= -1) {
+                       if (ENABLE_FEATURE_MDEV_RENAME && alias) {
+                               if (aliaslink == '>') {
+                                       dbg1("unlink: %s", device_name);
+                                       unlink(device_name);
                                }
-                               unlink(node_name);
                        }
-
-                       if (ENABLE_FEATURE_MDEV_RENAME)
-                               free(alias);
+                       dbg1("unlink: %s", node_name);
+                       unlink(node_name);
                }
 
+               if (ENABLE_FEATURE_MDEV_RENAME)
+                       free(alias);
+
                /* We found matching line.
-                * Stop unless it was prefixed with '-' */
-               if (ENABLE_FEATURE_MDEV_CONF && !keep_matching)
+                * Stop unless it was prefixed with '-'
+                */
+               if (!ENABLE_FEATURE_MDEV_CONF || !rule->keep_matching)
                        break;
-
-       /* end of "while line is read from /etc/mdev.conf" */
-       } while (ENABLE_FEATURE_MDEV_CONF);
-
-       if (ENABLE_FEATURE_MDEV_CONF)
-               config_close(parser);
-       free(subsystem_slash_devname);
+       } /* for (;;) */
 }
 
 /* File callback for /sys/ traversal */
@@ -419,7 +823,7 @@ static int FAST_FUNC fileAction(const char *fileName,
 
        strcpy(scratch, fileName);
        scratch[len] = '\0';
-       make_device(scratch, /*delete:*/ 0);
+       make_device(/*DEVNAME:*/ NULL, scratch, OP_add);
 
        return TRUE;
 }
@@ -434,9 +838,16 @@ static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
         * under /sys/class/ */
        if (1 == depth) {
                free(G.subsystem);
+               if (G.subsys_env) {
+                       bb_unsetenv_and_free(G.subsys_env);
+                       G.subsys_env = NULL;
+               }
                G.subsystem = strrchr(fileName, '/');
-               if (G.subsystem)
+               if (G.subsystem) {
                        G.subsystem = xstrdup(G.subsystem + 1);
+                       G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
+                       putenv(G.subsys_env);
+               }
        }
 
        return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
@@ -456,47 +867,149 @@ static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
 static void load_firmware(const char *firmware, const char *sysfs_path)
 {
        int cnt;
-       int firmware_fd, loading_fd, data_fd;
+       int firmware_fd, loading_fd;
 
        /* check for /lib/firmware/$FIRMWARE */
        xchdir("/lib/firmware");
-       firmware_fd = xopen(firmware, O_RDONLY);
-
-       /* in case we goto out ... */
-       data_fd = -1;
+       firmware_fd = open(firmware, O_RDONLY); /* can fail */
 
        /* check for /sys/$DEVPATH/loading ... give 30 seconds to appear */
        xchdir(sysfs_path);
        for (cnt = 0; cnt < 30; ++cnt) {
                loading_fd = open("loading", O_WRONLY);
-               if (loading_fd != -1)
+               if (loading_fd >= 0)
                        goto loading;
                sleep(1);
        }
        goto out;
 
  loading:
-       /* tell kernel we're loading by "echo 1 > /sys/$DEVPATH/loading" */
-       if (full_write(loading_fd, "1", 1) != 1)
-               goto out;
-
-       /* load firmware into /sys/$DEVPATH/data */
-       data_fd = open("data", O_WRONLY);
-       if (data_fd == -1)
-               goto out;
-       cnt = bb_copyfd_eof(firmware_fd, data_fd);
+       cnt = 0;
+       if (firmware_fd >= 0) {
+               int data_fd;
+
+               /* tell kernel we're loading by "echo 1 > /sys/$DEVPATH/loading" */
+               if (full_write(loading_fd, "1", 1) != 1)
+                       goto out;
+
+               /* load firmware into /sys/$DEVPATH/data */
+               data_fd = open("data", O_WRONLY);
+               if (data_fd < 0)
+                       goto out;
+               cnt = bb_copyfd_eof(firmware_fd, data_fd);
+               if (ENABLE_FEATURE_CLEAN_UP)
+                       close(data_fd);
+       }
 
-       /* tell kernel result by "echo [0|-1] > /sys/$DEVPATH/loading" */
+       /* Tell kernel result by "echo [0|-1] > /sys/$DEVPATH/loading"
+        * Note: we emit -1 also if firmware file wasn't found.
+        * There are cases when otherwise kernel would wait for minutes
+        * before timing out.
+        */
        if (cnt > 0)
                full_write(loading_fd, "0", 1);
        else
                full_write(loading_fd, "-1", 2);
 
  out:
+       xchdir("/dev");
        if (ENABLE_FEATURE_CLEAN_UP) {
                close(firmware_fd);
                close(loading_fd);
-               close(data_fd);
+       }
+}
+
+static char *curtime(void)
+{
+       struct timeval tv;
+       gettimeofday(&tv, NULL);
+       sprintf(G.timestr, "%u.%06u", (unsigned)tv.tv_sec % 60, (unsigned)tv.tv_usec);
+       return G.timestr;
+}
+
+static void open_mdev_log(const char *seq, unsigned my_pid)
+{
+       int logfd = open("mdev.log", O_WRONLY | O_APPEND);
+       if (logfd >= 0) {
+               xmove_fd(logfd, STDERR_FILENO);
+               G.verbose = 2;
+               applet_name = xasprintf("%s[%s]", applet_name, seq ? seq : utoa(my_pid));
+       }
+}
+
+/* If it exists, does /dev/mdev.seq match $SEQNUM?
+ * If it does not match, earlier mdev is running
+ * in parallel, and we need to wait.
+ * Active mdev pokes us with SIGCHLD to check the new file.
+ */
+static int
+wait_for_seqfile(const char *seq)
+{
+       /* We time out after 2 sec */
+       static const struct timespec ts = { 0, 32*1000*1000 };
+       int timeout = 2000 / 32;
+       int seq_fd = -1;
+       int do_once = 1;
+       sigset_t set_CHLD;
+
+       sigemptyset(&set_CHLD);
+       sigaddset(&set_CHLD, SIGCHLD);
+       sigprocmask(SIG_BLOCK, &set_CHLD, NULL);
+
+       for (;;) {
+               int seqlen;
+               char seqbuf[sizeof(int)*3 + 2];
+
+               if (seq_fd < 0) {
+                       seq_fd = open("mdev.seq", O_RDWR);
+                       if (seq_fd < 0)
+                               break;
+               }
+               seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0);
+               if (seqlen < 0) {
+                       close(seq_fd);
+                       seq_fd = -1;
+                       break;
+               }
+               seqbuf[seqlen] = '\0';
+               if (seqbuf[0] == '\n') {
+                       /* seed file: write out seq ASAP */
+                       xwrite_str(seq_fd, seq);
+                       xlseek(seq_fd, 0, SEEK_SET);
+                       dbg2("first seq written");
+                       break;
+               }
+               if (strcmp(seq, seqbuf) == 0) {
+                       /* correct idx */
+                       break;
+               }
+               if (do_once) {
+                       dbg2("%s waiting for '%s'", curtime(), seqbuf);
+                       do_once = 0;
+               }
+               if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) {
+                       dbg3("woken up");
+                       continue; /* don't decrement timeout! */
+               }
+               if (--timeout == 0) {
+                       dbg1("%s waiting for '%s'", "timed out", seqbuf);
+                       break;
+               }
+       }
+       sigprocmask(SIG_UNBLOCK, &set_CHLD, NULL);
+       return seq_fd;
+}
+
+static void signal_mdevs(unsigned my_pid)
+{
+       procps_status_t* p = NULL;
+       while ((p = procps_scan(p, PSSCAN_ARGV0)) != NULL) {
+               if (p->pid != my_pid
+                && p->argv0
+                && strcmp(bb_basename(p->argv0), "mdev") == 0
+               ) {
+                       kill(p->pid, SIGCHLD);
+               }
        }
 }
 
@@ -505,6 +1018,12 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
 {
        RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);
 
+       INIT_G();
+
+#if ENABLE_FEATURE_MDEV_CONF
+       G.filename = "/etc/mdev.conf";
+#endif
+
        /* We can be called as hotplug helper */
        /* Kernel cannot provide suitable stdio fds for us, do it ourself */
        bb_sanitize_stdio();
@@ -515,15 +1034,21 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
        xchdir("/dev");
 
        if (argv[1] && strcmp(argv[1], "-s") == 0) {
-               /* Scan:
-                * mdev -s
+               /*
+                * Scan: mdev -s
                 */
                struct stat st;
 
+#if ENABLE_FEATURE_MDEV_CONF
+               /* Same as xrealloc_vector(NULL, 4, 0): */
+               G.rule_vec = xzalloc((1 << 4) * sizeof(*G.rule_vec));
+#endif
                xstat("/", &st);
                G.root_major = major(st.st_dev);
                G.root_minor = minor(st.st_dev);
 
+               putenv((char*)"ACTION=add");
+
                /* ACTION_FOLLOWLINKS is needed since in newer kernels
                 * /sys/block/loop* (for example) are symlinks to dirs,
                 * not real directories.
@@ -547,66 +1072,60 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
                char *fw;
                char *seq;
                char *action;
-               char *env_path;
-               static const char keywords[] ALIGN1 = "remove\0add\0";
-               enum { OP_remove = 0, OP_add };
+               char *env_devname;
+               char *env_devpath;
+               unsigned my_pid;
+               int seq_fd;
                smalluint op;
 
                /* Hotplug:
                 * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
-                * ACTION can be "add" or "remove"
+                * ACTION can be "add", "remove", "change"
                 * DEVPATH is like "/block/sda" or "/class/input/mice"
                 */
-               action = getenv("ACTION");
-               env_path = getenv("DEVPATH");
+               env_devname = getenv("DEVNAME"); /* can be NULL */
                G.subsystem = getenv("SUBSYSTEM");
-               if (!action || !env_path /*|| !G.subsystem*/)
+               action = getenv("ACTION");
+               env_devpath = getenv("DEVPATH");
+               if (!action || !env_devpath /*|| !G.subsystem*/)
                        bb_show_usage();
                fw = getenv("FIRMWARE");
-               op = index_in_strings(keywords, action);
-               /* If it exists, does /dev/mdev.seq match $SEQNUM?
-                * If it does not match, earlier mdev is running
-                * in parallel, and we need to wait */
                seq = getenv("SEQNUM");
-               if (seq) {
-                       int timeout = 2000 / 32; /* 2000 msec */
-                       do {
-                               int seqlen;
-                               char seqbuf[sizeof(int)*3 + 2];
-
-                               seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf-1));
-                               if (seqlen < 0) {
-                                       seq = NULL;
-                                       break;
-                               }
-                               seqbuf[seqlen] = '\0';
-                               if (seqbuf[0] == '\n' /* seed file? */
-                                || strcmp(seq, seqbuf) == 0 /* correct idx? */
-                               ) {
-                                       break;
-                               }
-                               usleep(32*1000);
-                       } while (--timeout);
-               }
+               op = index_in_strings(keywords, action);
+
+               my_pid = getpid();
+               open_mdev_log(seq, my_pid);
+
+               seq_fd = seq ? wait_for_seqfile(seq) : -1;
+
+               dbg1("%s "
+                       "ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s"
+                       "%s%s",
+                       curtime(),
+                       action, G.subsystem, env_devname, env_devpath,
+                       fw ? " FW:" : "", fw ? fw : ""
+               );
 
-               snprintf(temp, PATH_MAX, "/sys%s", env_path);
+               snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
                if (op == OP_remove) {
                        /* Ignoring "remove firmware". It was reported
                         * to happen and to cause erroneous deletion
                         * of device nodes. */
                        if (!fw)
-                               make_device(temp, /*delete:*/ 1);
+                               make_device(env_devname, temp, op);
                }
-               else if (op == OP_add) {
-                       make_device(temp, /*delete:*/ 0);
+               else {
+                       make_device(env_devname, temp, op);
                        if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
-                               if (fw)
+                               if (op == OP_add && fw)
                                        load_firmware(fw, temp);
                        }
                }
 
-               if (seq) {
-                       xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1));
+               dbg1("%s exiting", curtime());
+               if (seq_fd >= 0) {
+                       xwrite_str(seq_fd, utoa(xatou(seq) + 1));
+                       signal_mdevs(my_pid);
                }
        }
 
index 14feb92..3258d7e 100644 (file)
@@ -5,19 +5,53 @@
  *
  * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define mkfs_ext2_trivial_usage
+//usage:       "[-Fn] "
+/* //usage:    "[-c|-l filename] " */
+//usage:       "[-b BLK_SIZE] "
+/* //usage:    "[-f fragment-size] [-g blocks-per-group] " */
+//usage:       "[-i INODE_RATIO] [-I INODE_SIZE] "
+/* //usage:    "[-j] [-J journal-options] [-N number-of-inodes] " */
+//usage:       "[-m RESERVED_PERCENT] "
+/* //usage:    "[-o creator-os] [-O feature[,...]] [-q] " */
+/* //usage:    "[r fs-revision-level] [-E extended-options] [-v] [-F] " */
+//usage:       "[-L LABEL] "
+/* //usage:    "[-M last-mounted-directory] [-S] [-T filesystem-type] " */
+//usage:       "BLOCKDEV [KBYTES]"
+//usage:#define mkfs_ext2_full_usage "\n\n"
+//usage:       "       -b BLK_SIZE     Block size, bytes"
+/* //usage:  "\n       -c              Check device for bad blocks" */
+/* //usage:  "\n       -E opts         Set extended options" */
+/* //usage:  "\n       -f size         Fragment size in bytes" */
+//usage:     "\n       -F              Force"
+/* //usage:  "\n       -g N            Number of blocks in a block group" */
+//usage:     "\n       -i RATIO        Max number of files is filesystem_size / RATIO"
+//usage:     "\n       -I BYTES        Inode size (min 128)"
+/* //usage:  "\n       -j              Create a journal (ext3)" */
+/* //usage:  "\n       -J opts         Set journal options (size/device)" */
+/* //usage:  "\n       -l file         Read bad blocks list from file" */
+//usage:     "\n       -L LBL          Volume label"
+//usage:     "\n       -m PERCENT      Percent of blocks to reserve for admin"
+/* //usage:  "\n       -M dir          Set last mounted directory" */
+//usage:     "\n       -n              Dry run"
+/* //usage:  "\n       -N N            Number of inodes to create" */
+/* //usage:  "\n       -o os           Set the 'creator os' field" */
+/* //usage:  "\n       -O features     Dir_index/filetype/has_journal/journal_dev/sparse_super" */
+/* //usage:  "\n       -q              Quiet" */
+/* //usage:  "\n       -r rev          Set filesystem revision" */
+/* //usage:  "\n       -S              Write superblock and group descriptors only" */
+/* //usage:  "\n       -T fs-type      Set usage type (news/largefile/largefile4)" */
+/* //usage:  "\n       -v              Verbose" */
+
 #include "libbb.h"
 #include <linux/fs.h>
-#include <linux/ext2_fs.h>
-
-#define        ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT 0
-#define        ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX    1
+#include "bb_e2fs_defs.h"
 
-// from e2fsprogs
-#define s_reserved_gdt_blocks s_padding1
-#define s_mkfs_time           s_reserved[0]
-#define s_flags               s_reserved[22]
+#define ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT 0
+#define ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX    1
 
 #define EXT2_HASH_HALF_MD4       1
 #define EXT2_FLAGS_SIGNED_HASH   0x0001
@@ -221,7 +255,7 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
 
        // open the device, check the device is a block device
        xmove_fd(xopen(argv[0], O_WRONLY), fd);
-       fstat(fd, &st);
+       xfstat(fd, &st, argv[0]);
        if (!S_ISBLK(st.st_mode) && !(option_mask32 & OPT_F))
                bb_error_msg_and_die("%s: not a block device", argv[0]);
 
@@ -443,8 +477,10 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
        STORE_LE(sb->s_magic, EXT2_SUPER_MAGIC);
        STORE_LE(sb->s_inode_size, inodesize);
        // set "Required extra isize" and "Desired extra isize" fields to 28
-       if (inodesize != sizeof(*inode))
-               STORE_LE(sb->s_reserved[21], 0x001C001C);
+       if (inodesize != sizeof(*inode)) {
+               STORE_LE(sb->s_min_extra_isize, 0x001c);
+               STORE_LE(sb->s_want_extra_isize, 0x001c);
+       }
        STORE_LE(sb->s_first_ino, EXT2_GOOD_OLD_FIRST_INO);
        STORE_LE(sb->s_log_block_size, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE);
        STORE_LE(sb->s_log_frag_size, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE);
@@ -576,7 +612,11 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
 
        // zero boot sectors
        memset(buf, 0, blocksize);
-       PUT(0, buf, 1024); // N.B. 1024 <= blocksize, so buf[0..1023] contains zeros
+       // Disabled: standard mke2fs doesn't do this, and
+       // on SPARC this destroys Sun disklabel.
+       // Users who need/want zeroing can easily do it with dd.
+       //PUT(0, buf, 1024); // N.B. 1024 <= blocksize, so buf[0..1023] contains zeros
+
        // zero inode tables
        for (i = 0; i < ngroups; ++i)
                for (n = 0; n < inode_table_blocks; ++n)
index abfb942..d65a516 100644 (file)
@@ -4,7 +4,7 @@
  *
  * (C) 1991 Linus Torvalds.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /*
  *     removed getopt based parser and added a hand rolled one.
  */
 
+//usage:#define mkfs_minix_trivial_usage
+//usage:       "[-c | -l FILE] [-nXX] [-iXX] BLOCKDEV [KBYTES]"
+//usage:#define mkfs_minix_full_usage "\n\n"
+//usage:       "Make a MINIX filesystem\n"
+//usage:     "\n       -c              Check device for bad blocks"
+//usage:     "\n       -n [14|30]      Maximum length of filenames"
+//usage:     "\n       -i INODES       Number of inodes for the filesystem"
+//usage:     "\n       -l FILE         Read bad blocks list from FILE"
+//usage:     "\n       -v              Make version 2 filesystem"
+
 #include "libbb.h"
 #include <mntent.h>
 
@@ -186,54 +196,6 @@ static void minix_clrbit(char *a, unsigned i)
 # define BLKGETSIZE     _IO(0x12,96)    /* return device size */
 #endif
 
-
-static long valid_offset(int fd, int offset)
-{
-       char ch;
-
-       if (lseek(fd, offset, SEEK_SET) < 0)
-               return 0;
-       if (read(fd, &ch, 1) < 1)
-               return 0;
-       return 1;
-}
-
-static int count_blocks(int fd)
-{
-       int high, low;
-
-       low = 0;
-       for (high = 1; valid_offset(fd, high); high *= 2)
-               low = high;
-
-       while (low < high - 1) {
-               const int mid = (low + high) / 2;
-
-               if (valid_offset(fd, mid))
-                       low = mid;
-               else
-                       high = mid;
-       }
-       valid_offset(fd, 0);
-       return (low + 1);
-}
-
-static int get_size(const char *file)
-{
-       int fd;
-       long size;
-
-       fd = xopen(file, O_RDWR);
-       if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
-               close(fd);
-               return (size * 512);
-       }
-
-       size = count_blocks(fd);
-       close(fd);
-       return size;
-}
-
 static void write_tables(void)
 {
        /* Mark the superblock valid. */
@@ -543,7 +505,7 @@ static void get_list_blocks(char *filename)
 
        listfile = xfopen_for_read(filename);
        while (!feof(listfile)) {
-               fscanf(listfile, "%ld\n", &blockno);
+               fscanf(listfile, "%lu\n", &blockno);
                mark_zone(blockno);
                G.badblocks++;
        }
@@ -626,7 +588,6 @@ int mkfs_minix_main(int argc UNUSED_PARAM, char **argv)
 {
        unsigned opt;
        char *tmp;
-       struct stat statbuf;
        char *str_i;
        char *listfile = NULL;
 
@@ -663,13 +624,17 @@ int mkfs_minix_main(int argc UNUSED_PARAM, char **argv)
 #endif
        }
 
-       G.device_name = *argv++;
+       G.device_name = argv[0];
        if (!G.device_name)
                bb_show_usage();
-       if (*argv)
-               G.total_blocks = xatou32(*argv);
-       else
-               G.total_blocks = get_size(G.device_name) / 1024;
+
+       /* Check if it is mounted */
+       if (find_mount_point(G.device_name, 0))
+               bb_error_msg_and_die("can't format mounted filesystem");
+
+       xmove_fd(xopen(G.device_name, O_RDWR), dev_fd);
+
+       G.total_blocks = get_volume_size_in_bytes(dev_fd, argv[1], 1024, /*extend:*/ 1) / 1024;
 
        if (G.total_blocks < 10)
                bb_error_msg_and_die("must have at least 10 blocks");
@@ -680,26 +645,21 @@ int mkfs_minix_main(int argc UNUSED_PARAM, char **argv)
                        G.magic = MINIX2_SUPER_MAGIC;
        } else if (G.total_blocks > 65535)
                G.total_blocks = 65535;
-
-       /* Check if it is mounted */
-       if (find_mount_point(G.device_name, 0))
-               bb_error_msg_and_die("can't format mounted filesystem");
-
-       xmove_fd(xopen(G.device_name, O_RDWR), dev_fd);
-       if (fstat(dev_fd, &statbuf) < 0)
-               bb_error_msg_and_die("can't stat '%s'", G.device_name);
+#if 0
+       struct stat statbuf;
+       xfstat(dev_fd, &statbuf, G.device_name);
+/* why? */
        if (!S_ISBLK(statbuf.st_mode))
                opt &= ~1; // clear -c (check)
-
+#if 0
 /* I don't know why someone has special code to prevent mkfs.minix
  * on IDE devices. Why IDE but not SCSI, etc?... */
-#if 0
        else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
                /* what is this? */
                bb_error_msg_and_die("will not try "
                        "to make filesystem on '%s'", G.device_name);
 #endif
-
+#endif
        tmp = G.root_block;
        *(short *) tmp = 1;
        strcpy(tmp + 2, ".");
index f9a0ca8..b4efb9e 100644 (file)
@@ -4,8 +4,16 @@
  *
  * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define mkfs_reiser_trivial_usage
+//usage:       "[-f] [-l LABEL] BLOCKDEV [4K-BLOCKS]"
+//usage:#define mkfs_reiser_full_usage "\n\n"
+//usage:       "Make a ReiserFS V3 filesystem\n"
+//usage:     "\n       -f      Force"
+//usage:     "\n       -l LBL  Volume label"
+
 #include "libbb.h"
 #include <linux/fs.h>
 
@@ -168,9 +176,9 @@ int mkfs_reiser_main(int argc UNUSED_PARAM, char **argv)
 
        // check the device is a block device
        fd = xopen(argv[0], O_WRONLY | O_EXCL);
-       fstat(fd, &st);
+       xfstat(fd, &st, argv[0]);
        if (!S_ISBLK(st.st_mode) && !(option_mask32 & OPT_f))
-               bb_error_msg_and_die("not a block device");
+               bb_error_msg_and_die("%s: not a block device", argv[0]);
 
        // check if it is mounted
        // N.B. what if we format a file? find_mount_point will return false negative since
index c57cd9d..7d81ed0 100644 (file)
@@ -5,8 +5,24 @@
  *
  * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define mkfs_vfat_trivial_usage
+//usage:       "[-v] [-n LABEL] BLOCKDEV [KBYTES]"
+/* Accepted but ignored:
+       "[-c] [-C] [-I] [-l bad-block-file] [-b backup-boot-sector] "
+       "[-m boot-msg-file] [-i volume-id] "
+       "[-s sectors-per-cluster] [-S logical-sector-size] [-f number-of-FATs] "
+       "[-h hidden-sectors] [-F fat-size] [-r root-dir-entries] [-R reserved-sectors] "
+*/
+//usage:#define mkfs_vfat_full_usage "\n\n"
+//usage:       "Make a FAT32 filesystem\n"
+/* //usage:  "\n       -c      Check device for bad blocks" */
+//usage:     "\n       -v      Verbose"
+/* //usage:  "\n       -I      Allow to use entire disk device (e.g. /dev/hda)" */
+//usage:     "\n       -n LBL  Volume label"
+
 #include "libbb.h"
 
 #include <linux/hdreg.h> /* HDIO_GETGEO */
@@ -28,7 +44,7 @@
 
 #define ATTR_VOLUME     8
 
-#define        NUM_FATS        2
+#define NUM_FATS        2
 
 /* FAT32 filesystem looks like this:
  * sector -nn...-1: "hidden" sectors, all sectors before this partition
@@ -245,8 +261,7 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
        volume_id = time(NULL);
 
        dev = xopen(device_name, O_RDWR);
-       if (fstat(dev, &st) < 0)
-               bb_simple_perror_msg_and_die(device_name);
+       xfstat(dev, &st, device_name);
 
        //
        // Get image size and sector size
index 7e32d91..b5d2c49 100644 (file)
@@ -3,8 +3,15 @@
  *
  * Copyright 2006 Rob Landley <rob@landley.net>
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define mkswap_trivial_usage
+//usage:       "[-L LBL] BLOCKDEV [KBYTES]"
+//usage:#define mkswap_full_usage "\n\n"
+//usage:       "Prepare BLOCKDEV to be used as swap partition\n"
+//usage:     "\n       -L LBL  Label"
+
 #include "libbb.h"
 
 #if ENABLE_SELINUX
@@ -15,8 +22,7 @@ static void mkswap_selinux_setcontext(int fd, const char *path)
        if (!is_selinux_enabled())
                return;
 
-       if (fstat(fd, &stbuf) < 0)
-               bb_perror_msg_and_die("fstat failed");
+       xfstat(fd, &stbuf, path);
        if (S_ISREG(stbuf.st_mode)) {
                security_context_t newcon;
                security_context_t oldcon = NULL;
@@ -102,7 +108,15 @@ int mkswap_main(int argc UNUSED_PARAM, char **argv)
        printf("Setting up swapspace version 1, size = %"OFF_FMT"u bytes\n", len);
        mkswap_selinux_setcontext(fd, argv[0]);
 
-       /* Make a header. hdr is zero-filled so far... */
+       /* hdr is zero-filled so far. Clear the first kbyte, or else
+        * mkswap-ing former FAT partition does NOT erase its signature.
+        *
+        * util-linux-ng 2.17.2 claims to erase it only if it does not see
+        * a partition table and is not run on whole disk. -f forces it.
+        */
+       xwrite(fd, hdr, 1024);
+
+       /* Fill the header. */
        hdr->version = 1;
        hdr->last_page = (uoff_t)len / pagesize;
 
@@ -123,7 +137,6 @@ int mkswap_main(int argc UNUSED_PARAM, char **argv)
 
        /* Write the header.  Sync to disk because some kernel versions check
         * signature on disk (not in cache) during swapon. */
-       xlseek(fd, 1024, SEEK_SET);
        xwrite(fd, hdr, NWORDS * 4);
        xlseek(fd, pagesize - 10, SEEK_SET);
        xwrite(fd, SWAPSPACE2, 10);
index 9216b61..3595713 100644 (file)
  *
  * Termios corrects by Vladimir Oleynik <dzo@simtreas.ru>
  *
- * Licensed under GPLv2 or later, see file License in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define more_trivial_usage
+//usage:       "[FILE]..."
+//usage:#define more_full_usage "\n\n"
+//usage:       "View FILE (or stdin) one screenful at a time"
+//usage:
+//usage:#define more_example_usage
+//usage:       "$ dmesg | more\n"
+
 #include "libbb.h"
 
 /* Support for FEATURE_USE_TERMIOS */
@@ -29,16 +37,20 @@ struct globals {
 #define new_settings     (G.new_settings    )
 #define cin_fileno       (G.cin_fileno      )
 
-#define setTermSettings(fd, argp) do { \
-               if (ENABLE_FEATURE_USE_TERMIOS) tcsetattr(fd, TCSANOW, argp); \
-       } while (0)
+#define setTermSettings(fd, argp) \
+do { \
+       if (ENABLE_FEATURE_USE_TERMIOS) \
+               tcsetattr(fd, TCSANOW, argp); \
+} while (0)
 #define getTermSettings(fd, argp) tcgetattr(fd, argp)
 
 static void gotsig(int sig UNUSED_PARAM)
 {
-       bb_putchar('\n');
+       /* bb_putchar_stderr doesn't use stdio buffering,
+        * therefore it is safe in signal handler */
+       bb_putchar_stderr('\n');
        setTermSettings(cin_fileno, &initial_settings);
-       exit(EXIT_FAILURE);
+       _exit(EXIT_FAILURE);
 }
 
 #define CONVERTED_TAB_SIZE 8
@@ -73,8 +85,7 @@ int more_main(int argc UNUSED_PARAM, char **argv)
                cin_fileno = fileno(cin);
                getTermSettings(cin_fileno, &initial_settings);
                new_settings = initial_settings;
-               new_settings.c_lflag &= ~ICANON;
-               new_settings.c_lflag &= ~ECHO;
+               new_settings.c_lflag &= ~(ICANON | ECHO);
                new_settings.c_cc[VMIN] = 1;
                new_settings.c_cc[VTIME] = 0;
                setTermSettings(cin_fileno, &new_settings);
@@ -109,9 +120,12 @@ int more_main(int argc UNUSED_PARAM, char **argv)
  loop_top:
                        if (input != 'r' && please_display_more_prompt) {
                                len = printf("--More-- ");
-                               if (st.st_size > 0) {
+                               if (st.st_size != 0) {
+                                       uoff_t d = (uoff_t)st.st_size / 100;
+                                       if (d == 0)
+                                               d = 1;
                                        len += printf("(%u%% of %"OFF_FMT"u bytes)",
-                                               (int) (ftello(file)*100 / st.st_size),
+                                               (int) ((uoff_t)ftello(file) / d),
                                                st.st_size);
                                }
                                fflush_all();
@@ -155,7 +169,7 @@ int more_main(int argc UNUSED_PARAM, char **argv)
                        /* Crudely convert tabs into spaces, which are
                         * a bajillion times easier to deal with. */
                        if (c == '\t') {
-                               spaces = CONVERTED_TAB_SIZE - 1;
+                               spaces = ((unsigned)~len) % CONVERTED_TAB_SIZE;
                                c = ' ';
                        }
 
@@ -187,6 +201,7 @@ int more_main(int argc UNUSED_PARAM, char **argv)
                        }
                        /* My small mind cannot fathom backspaces and UTF-8 */
                        putchar(c);
+                       die_if_ferror_stdout(); /* if tty was destroyed (closed xterm, etc) */
                }
                fclose(file);
                fflush_all();
index 929dbd8..62fd41f 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net>
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 // Design notes: There is no spec for mount.  Remind me to write one.
 //
 // singlemount() can loop through /etc/filesystems for fstype detection.
 // mount_it_now() does the actual mount.
 //
+
+//usage:#define mount_trivial_usage
+//usage:       "[OPTIONS] [-o OPTS] DEVICE NODE"
+//usage:#define mount_full_usage "\n\n"
+//usage:       "Mount a filesystem. Filesystem autodetection requires /proc.\n"
+//usage:     "\n       -a              Mount all filesystems in fstab"
+//usage:       IF_FEATURE_MOUNT_FAKE(
+//usage:       IF_FEATURE_MTAB_SUPPORT(
+//usage:     "\n       -f              Update /etc/mtab, but don't mount"
+//usage:       )
+//usage:       IF_NOT_FEATURE_MTAB_SUPPORT(
+//usage:     "\n       -f              Dry run"
+//usage:       )
+//usage:       )
+//usage:       IF_FEATURE_MOUNT_HELPERS(
+//usage:     "\n       -i              Don't run mount helper"
+//usage:       )
+//usage:       IF_FEATURE_MTAB_SUPPORT(
+//usage:     "\n       -n              Don't update /etc/mtab"
+//usage:       )
+//usage:       IF_FEATURE_MOUNT_VERBOSE(
+//usage:     "\n       -v              Verbose"
+//usage:       )
+////usage:   "\n       -s              Sloppy (ignored)"
+//usage:     "\n       -r              Read-only mount"
+//usage:     "\n       -w              Read-write mount (default)"
+//usage:     "\n       -t FSTYPE[,...] Filesystem type(s)"
+//usage:     "\n       -O OPT          Mount only filesystems with option OPT (-a only)"
+//usage:     "\n-o OPT:"
+//usage:       IF_FEATURE_MOUNT_LOOP(
+//usage:     "\n       loop            Ignored (loop devices are autodetected)"
+//usage:       )
+//usage:       IF_FEATURE_MOUNT_FLAGS(
+//usage:     "\n       [a]sync         Writes are [a]synchronous"
+//usage:     "\n       [no]atime       Disable/enable updates to inode access times"
+//usage:     "\n       [no]diratime    Disable/enable atime updates to directories"
+//usage:     "\n       [no]relatime    Disable/enable atime updates relative to modification time"
+//usage:     "\n       [no]dev         (Dis)allow use of special device files"
+//usage:     "\n       [no]exec        (Dis)allow use of executable files"
+//usage:     "\n       [no]suid        (Dis)allow set-user-id-root programs"
+//usage:     "\n       [r]shared       Convert [recursively] to a shared subtree"
+//usage:     "\n       [r]slave        Convert [recursively] to a slave subtree"
+//usage:     "\n       [r]private      Convert [recursively] to a private subtree"
+//usage:     "\n       [un]bindable    Make mount point [un]able to be bind mounted"
+//usage:     "\n       [r]bind         Bind a file or directory [recursively] to another location"
+//usage:     "\n       move            Relocate an existing mount point"
+//usage:       )
+//usage:     "\n       remount         Remount a mounted filesystem, changing flags"
+//usage:     "\n       ro/rw           Same as -r/-w"
+//usage:     "\n"
+//usage:     "\nThere are filesystem-specific -o flags."
+//usage:
+//usage:#define mount_example_usage
+//usage:       "$ mount\n"
+//usage:       "/dev/hda3 on / type minix (rw)\n"
+//usage:       "proc on /proc type proc (rw)\n"
+//usage:       "devpts on /dev/pts type devpts (rw)\n"
+//usage:       "$ mount /dev/fd0 /mnt -t msdos -o ro\n"
+//usage:       "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
+//usage:       "$ mount cd_image.iso mydir\n"
+//usage:#define mount_notes_usage
+//usage:       "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
+
 #include <mntent.h>
 #include <syslog.h>
 #include <sys/mount.h>
 #ifndef MS_RELATIME
 # define MS_RELATIME    (1 << 21)
 #endif
+#ifndef MS_STRICTATIME
+# define MS_STRICTATIME (1 << 24)
+#endif
+
+/* Any ~MS_FOO value has this bit set: */
+#define BB_MS_INVERTED_VALUE (1u << 31)
 
 #include "libbb.h"
 #if ENABLE_FEATURE_MOUNT_LABEL
@@ -159,6 +228,7 @@ static const int32_t mount_options[] = {
                IF_DESKTOP(/* "user"  */ MOUNT_USERS,)
                IF_DESKTOP(/* "users" */ MOUNT_USERS,)
                /* "_netdev" */ 0,
+               IF_DESKTOP(/* "comment=" */ 0,) /* systemd uses this in fstab */
        )
 
        IF_FEATURE_MOUNT_FLAGS(
@@ -180,7 +250,9 @@ static const int32_t mount_options[] = {
                /* "nomand"      */ ~MS_MANDLOCK,
                /* "relatime"    */ MS_RELATIME,
                /* "norelatime"  */ ~MS_RELATIME,
+               /* "strictatime" */ MS_STRICTATIME,
                /* "loud"        */ ~MS_SILENT,
+               /* "rbind"       */ MS_BIND|MS_RECURSIVE,
 
                // action flags
                /* "union"       */ MS_UNION,
@@ -192,7 +264,7 @@ static const int32_t mount_options[] = {
                /* "unbindable"  */ MS_UNBINDABLE,
                /* "rshared"     */ MS_SHARED|MS_RECURSIVE,
                /* "rslave"      */ MS_SLAVE|MS_RECURSIVE,
-               /* "rprivate"    */ MS_SLAVE|MS_RECURSIVE,
+               /* "rprivate"    */ MS_PRIVATE|MS_RECURSIVE,
                /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
        )
 
@@ -215,6 +287,7 @@ static const char mount_option_str[] =
                IF_DESKTOP("user\0")
                IF_DESKTOP("users\0")
                "_netdev\0"
+               IF_DESKTOP("comment=\0") /* systemd uses this in fstab */
        )
        IF_FEATURE_MOUNT_FLAGS(
                // vfs flags
@@ -235,20 +308,22 @@ static const char mount_option_str[] =
                "nomand\0"
                "relatime\0"
                "norelatime\0"
+               "strictatime\0"
                "loud\0"
+               "rbind\0"
 
                // action flags
                "union\0"
                "bind\0"
                "move\0"
-               "shared\0"
-               "slave\0"
-               "private\0"
-               "unbindable\0"
-               "rshared\0"
-               "rslave\0"
-               "rprivate\0"
-               "runbindable\0"
+               "make-shared\0"
+               "make-slave\0"
+               "make-private\0"
+               "make-unbindable\0"
+               "make-rshared\0"
+               "make-rslave\0"
+               "make-rprivate\0"
+               "make-runbindable\0"
        )
 
        // Always understood.
@@ -278,7 +353,63 @@ enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_b
 #endif
 #define fslist            (G.fslist           )
 #define getmntent_buf     (G.getmntent_buf    )
+#define INIT_G() do { } while (0)
+
+#if ENABLE_FEATURE_MTAB_SUPPORT
+/*
+ * update_mtab_entry_on_move() is used to update entry in case of mount --move.
+ * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
+ * input mntent and replace it by new one.
+ */
+static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
+{
+       struct mntent *entries, *m;
+       int i, count;
+       FILE *mountTable;
+
+       mountTable = setmntent(bb_path_mtab_file, "r");
+       if (!mountTable) {
+               bb_perror_msg(bb_path_mtab_file);
+               return;
+       }
+
+       entries = NULL;
+       count = 0;
+       while ((m = getmntent(mountTable)) != NULL) {
+               entries = xrealloc_vector(entries, 3, count);
+               entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
+               entries[count].mnt_dir = xstrdup(m->mnt_dir);
+               entries[count].mnt_type = xstrdup(m->mnt_type);
+               entries[count].mnt_opts = xstrdup(m->mnt_opts);
+               entries[count].mnt_freq = m->mnt_freq;
+               entries[count].mnt_passno = m->mnt_passno;
+               count++;
+       }
+       endmntent(mountTable);
+
+       mountTable = setmntent(bb_path_mtab_file, "w");
+       if (mountTable) {
+               for (i = 0; i < count; i++) {
+                       if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
+                               addmntent(mountTable, &entries[i]);
+                       else
+                               addmntent(mountTable, mp);
+               }
+               endmntent(mountTable);
+       } else if (errno != EROFS)
+               bb_perror_msg(bb_path_mtab_file);
 
+       if (ENABLE_FEATURE_CLEAN_UP) {
+               for (i = 0; i < count; i++) {
+                       free(entries[i].mnt_fsname);
+                       free(entries[i].mnt_dir);
+                       free(entries[i].mnt_type);
+                       free(entries[i].mnt_opts);
+               }
+               free(entries);
+       }
+}
+#endif
 
 #if ENABLE_FEATURE_MOUNT_VERBOSE
 static int verbose_mount(const char *source, const char *target,
@@ -332,10 +463,10 @@ static void append_mount_options(char **oldopts, const char *newopts)
 }
 
 // Use the mount_options list to parse options into flags.
-// Also return list of unrecognized options if unrecognized != NULL
-static long parse_mount_options(char *options, char **unrecognized)
+// Also update list of unrecognized options if unrecognized != NULL
+static unsigned long parse_mount_options(char *options, char **unrecognized)
 {
-       long flags = MS_SILENT;
+       unsigned long flags = MS_SILENT;
 
        // Loop through options
        for (;;) {
@@ -348,25 +479,42 @@ static long parse_mount_options(char *options, char **unrecognized)
 // FIXME: use hasmntopt()
                // Find this option in mount_options
                for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
-                       if (!strcasecmp(option_str, options)) {
-                               long fl = mount_options[i];
-                               if (fl < 0) flags &= fl;
-                               else flags |= fl;
-                               break;
+                       unsigned opt_len = strlen(option_str);
+
+                       if (strncasecmp(option_str, options, opt_len) == 0
+                        && (options[opt_len] == '\0'
+                           /* or is it "comment=" thingy in fstab? */
+                           IF_FEATURE_MOUNT_FSTAB(IF_DESKTOP( || option_str[opt_len-1] == '=' ))
+                           )
+                       ) {
+                               unsigned long fl = mount_options[i];
+                               if (fl & BB_MS_INVERTED_VALUE)
+                                       flags &= fl;
+                               else
+                                       flags |= fl;
+                               goto found;
                        }
-                       option_str += strlen(option_str) + 1;
+                       option_str += opt_len + 1;
                }
-               // If unrecognized not NULL, append unrecognized mount options
-               if (unrecognized && i == ARRAY_SIZE(mount_options)) {
+               // We did not recognize this option.
+               // If "unrecognized" is not NULL, append option there.
+               // Note that we should not append *empty* option -
+               // in this case we want to pass NULL, not "", to "data"
+               // parameter of mount(2) syscall.
+               // This is crucial for filesystems that don't accept
+               // any arbitrary mount options, like cgroup fs:
+               // "mount -t cgroup none /mnt"
+               if (options[0] && unrecognized) {
                        // Add it to strflags, to pass on to kernel
-                       i = *unrecognized ? strlen(*unrecognized) : 0;
-                       *unrecognized = xrealloc(*unrecognized, i + strlen(options) + 2);
+                       char *p = *unrecognized;
+                       unsigned len = p ? strlen(p) : 0;
+                       *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
 
                        // Comma separated if it's not the first one
-                       if (i) (*unrecognized)[i++] = ',';
-                       strcpy((*unrecognized)+i, options);
+                       if (len) p[len++] = ',';
+                       strcpy(p + len, options);
                }
-
+ found:
                if (!comma)
                        break;
                // Advance to next option
@@ -395,12 +543,13 @@ static llist_t *get_block_backed_filesystems(void)
 
                while ((buf = xmalloc_fgetline(f)) != NULL) {
                        if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
-                               continue;
+                               goto next;
                        fs = skip_whitespace(buf);
                        if (*fs == '#' || *fs == '*' || !*fs)
-                               continue;
+                               goto next;
 
                        llist_add_to_end(&list, xstrdup(fs));
+ next:
                        free(buf);
                }
                if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
@@ -420,7 +569,7 @@ void delete_block_backed_filesystems(void);
 
 // Perform actual mount of specific filesystem at specific location.
 // NB: mp->xxx fields may be trashed on exit
-static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
+static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts)
 {
        int rc = 0;
 
@@ -486,12 +635,11 @@ static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
                int i;
 
                if (!mountTable) {
-                       bb_error_msg("no %s", bb_path_mtab_file);
+                       bb_perror_msg(bb_path_mtab_file);
                        goto ret;
                }
 
                // Add vfs string flags
-
                for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
                        if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
                                append_mount_options(&(mp->mnt_opts), option_str);
@@ -499,24 +647,28 @@ static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
                }
 
                // Remove trailing / (if any) from directory we mounted on
-
                i = strlen(mp->mnt_dir) - 1;
-               if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = '\0';
+               while (i > 0 && mp->mnt_dir[i] == '/')
+                       mp->mnt_dir[i--] = '\0';
 
                // Convert to canonical pathnames as needed
-
                mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
-               fsname = 0;
+               fsname = NULL;
                if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
                        mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
                        mp->mnt_type = (char*)"bind";
                }
                mp->mnt_freq = mp->mnt_passno = 0;
 
-               // Write and close.
-
-               addmntent(mountTable, mp);
+               // Write and close
+#if ENABLE_FEATURE_MTAB_SUPPORT
+               if (vfsflags & MS_MOVE)
+                       update_mtab_entry_on_move(mp);
+               else
+#endif
+                       addmntent(mountTable, mp);
                endmntent(mountTable);
+
                if (ENABLE_FEATURE_CLEAN_UP) {
                        free(mp->mnt_dir);
                        free(fsname);
@@ -532,7 +684,7 @@ static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts)
  * Linux NFS mount
  * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  *
  * Wed Feb  8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
  * numbers to be specified on the command line.
@@ -689,31 +841,31 @@ enum {
  */
 
 struct nfs2_fh {
-       char                    data[32];
+       char            data[32];
 };
 struct nfs3_fh {
-       unsigned short          size;
-       unsigned char           data[64];
+       unsigned short  size;
+       unsigned char   data[64];
 };
 
 struct nfs_mount_data {
-       int             version;                /* 1 */
-       int             fd;                     /* 1 */
-       struct nfs2_fh  old_root;               /* 1 */
-       int             flags;                  /* 1 */
-       int             rsize;                  /* 1 */
-       int             wsize;                  /* 1 */
-       int             timeo;                  /* 1 */
-       int             retrans;                /* 1 */
-       int             acregmin;               /* 1 */
-       int             acregmax;               /* 1 */
-       int             acdirmin;               /* 1 */
-       int             acdirmax;               /* 1 */
-       struct sockaddr_in addr;                /* 1 */
-       char            hostname[256];          /* 1 */
-       int             namlen;                 /* 2 */
-       unsigned int    bsize;                  /* 3 */
-       struct nfs3_fh  root;                   /* 4 */
+       int             version;        /* 1 */
+       int             fd;             /* 1 */
+       struct nfs2_fh  old_root;       /* 1 */
+       int             flags;          /* 1 */
+       int             rsize;          /* 1 */
+       int             wsize;          /* 1 */
+       int             timeo;          /* 1 */
+       int             retrans;        /* 1 */
+       int             acregmin;       /* 1 */
+       int             acregmax;       /* 1 */
+       int             acdirmin;       /* 1 */
+       int             acdirmax;       /* 1 */
+       struct sockaddr_in addr;        /* 1 */
+       char            hostname[256];  /* 1 */
+       int             namlen;         /* 2 */
+       unsigned int    bsize;          /* 3 */
+       struct nfs3_fh  root;           /* 4 */
 };
 
 /* bits in the flags field */
@@ -728,6 +880,7 @@ enum {
        NFS_MOUNT_VER3 = 0x0080,        /* 3 */
        NFS_MOUNT_KERBEROS = 0x0100,    /* 3 */
        NFS_MOUNT_NONLM = 0x0200,       /* 3 */
+       NFS_MOUNT_NOACL = 0x0800,       /* 4 */
        NFS_MOUNT_NORDIRPLUS = 0x4000
 };
 
@@ -775,78 +928,52 @@ static char *nfs_strerror(int status)
 
 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
 {
-       if (!xdr_opaque(xdrs, objp, FHSIZE))
-                return FALSE;
-       return TRUE;
+       return xdr_opaque(xdrs, objp, FHSIZE);
 }
 
 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
 {
        if (!xdr_u_int(xdrs, &objp->fhs_status))
-                return FALSE;
-       switch (objp->fhs_status) {
-       case 0:
-               if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
-                        return FALSE;
-               break;
-       default:
-               break;
-       }
+               return FALSE;
+       if (objp->fhs_status == 0)
+               return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
        return TRUE;
 }
 
 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
 {
-       if (!xdr_string(xdrs, objp, MNTPATHLEN))
-                return FALSE;
-       return TRUE;
+       return xdr_string(xdrs, objp, MNTPATHLEN);
 }
 
 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
 {
-       if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
-                               (unsigned int *) &objp->fhandle3_len,
-                               FHSIZE3)
-       ) {
-                return FALSE;
-       }
-       return TRUE;
+       return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
+                       (unsigned int *) &objp->fhandle3_len,
+                       FHSIZE3);
 }
 
 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
 {
        if (!xdr_fhandle3(xdrs, &objp->fhandle))
                return FALSE;
-       if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
-                               &(objp->auth_flavours.auth_flavours_len),
-                               ~0,
-                               sizeof(int),
-                               (xdrproc_t) xdr_int)
-       ) {
-               return FALSE;
-       }
-       return TRUE;
+       return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
+                       &(objp->auth_flavours.auth_flavours_len),
+                       ~0,
+                       sizeof(int),
+                       (xdrproc_t) xdr_int);
 }
 
 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
 {
-       if (!xdr_enum(xdrs, (enum_t *) objp))
-                return FALSE;
-       return TRUE;
+       return xdr_enum(xdrs, (enum_t *) objp);
 }
 
 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
 {
        if (!xdr_mountstat3(xdrs, &objp->fhs_status))
                return FALSE;
-       switch (objp->fhs_status) {
-       case MNT_OK:
-               if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
-                        return FALSE;
-               break;
-       default:
-               break;
-       }
+       if (objp->fhs_status == MNT_OK)
+               return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
        return TRUE;
 }
 
@@ -974,7 +1101,7 @@ static void error_msg_rpc(const char *msg)
 }
 
 /* NB: mp->xxx fields may be trashed on exit */
-static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
+static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
 {
        CLIENT *mclient;
        char *hostname;
@@ -1018,6 +1145,7 @@ static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
        int noac;
        int nordirplus;
        int nolock;
+       int noacl;
 
        find_kernel_nfs_mount_version();
 
@@ -1037,7 +1165,7 @@ static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
        pathname = s + 1;
        *s = '\0';
        /* Ignore all but first hostname in replicated mounts
-          until they can be fully supported. (mack@sgi.com) */
+        * until they can be fully supported. (mack@sgi.com) */
        s = strchr(hostname, ',');
        if (s) {
                *s = '\0';
@@ -1090,8 +1218,9 @@ static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
        nolock = 0;
        noac = 0;
        nordirplus = 0;
+       noacl = 0;
        retry = 10000;          /* 10000 minutes ~ 1 week */
-       tcp = 0;
+       tcp = 1;                        /* nfs-utils uses tcp per default */
 
        mountprog = MOUNTPROG;
        mountvers = 0;
@@ -1145,9 +1274,12 @@ static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
                                continue;
                        case 20: // "addr" - ignore
                                continue;
+                       case -1: // unknown
+                               if (vfsflags & MS_REMOUNT)
+                                       continue;
                        }
 
-                       val = xatoi_u(opteq);
+                       val = xatoi_positive(opteq);
                        switch (idx) {
                        case 0: // "rsize"
                                data.rsize = val;
@@ -1225,7 +1357,8 @@ static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
                                "tcp\0"
                                "udp\0"
                                "lock\0"
-                               "rdirplus\0";
+                               "rdirplus\0"
+                               "acl\0";
                        int val = 1;
                        if (!strncmp(opt, "no", 2)) {
                                val = 0;
@@ -1275,6 +1408,9 @@ static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
                        case 11: //rdirplus
                                nordirplus = !val;
                                break;
+                       case 12: // acl
+                               noacl = !val;
+                               break;
                        default:
                                bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
                                goto fail;
@@ -1288,7 +1424,8 @@ static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
                | (posix ? NFS_MOUNT_POSIX : 0)
                | (nocto ? NFS_MOUNT_NOCTO : 0)
                | (noac ? NFS_MOUNT_NOAC : 0)
-               | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0);
+               | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
+               | (noacl ? NFS_MOUNT_NOACL : 0);
        if (nfs_mount_version >= 2)
                data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
        if (nfs_mount_version >= 3)
@@ -1392,19 +1529,19 @@ static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
                switch (pm_mnt.pm_prot) {
                case IPPROTO_UDP:
                        mclient = clntudp_create(&mount_server_addr,
-                                                pm_mnt.pm_prog,
-                                                pm_mnt.pm_vers,
-                                                retry_timeout,
-                                                &msock);
+                                               pm_mnt.pm_prog,
+                                               pm_mnt.pm_vers,
+                                               retry_timeout,
+                                               &msock);
                        if (mclient)
                                break;
                        mount_server_addr.sin_port = htons(pm_mnt.pm_port);
                        msock = RPC_ANYSOCK;
                case IPPROTO_TCP:
                        mclient = clnttcp_create(&mount_server_addr,
-                                                pm_mnt.pm_prog,
-                                                pm_mnt.pm_vers,
-                                                &msock, 0, 0);
+                                               pm_mnt.pm_prog,
+                                               pm_mnt.pm_vers,
+                                               &msock, 0, 0);
                        break;
                default:
                        mclient = NULL;
@@ -1425,18 +1562,18 @@ static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
 
                        if (pm_mnt.pm_vers == 3)
                                clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
-                                             (xdrproc_t) xdr_dirpath,
-                                             (caddr_t) &pathname,
-                                             (xdrproc_t) xdr_mountres3,
-                                             (caddr_t) &status,
-                                             total_timeout);
+                                               (xdrproc_t) xdr_dirpath,
+                                               (caddr_t) &pathname,
+                                               (xdrproc_t) xdr_mountres3,
+                                               (caddr_t) &status,
+                                               total_timeout);
                        else
                                clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
-                                             (xdrproc_t) xdr_dirpath,
-                                             (caddr_t) &pathname,
-                                             (xdrproc_t) xdr_fhstatus,
-                                             (caddr_t) &status,
-                                             total_timeout);
+                                               (xdrproc_t) xdr_dirpath,
+                                               (caddr_t) &pathname,
+                                               (xdrproc_t) xdr_fhstatus,
+                                               (caddr_t) &status,
+                                               total_timeout);
 
                        if (clnt_stat == RPC_SUCCESS)
                                goto prepare_kernel_data; /* we're done */
@@ -1567,7 +1704,6 @@ static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
 
        /* Perform actual mount */
  do_mount:
-       mp->mnt_type = (char*)"nfs";
        retval = mount_it_now(mp, vfsflags, (char*)&data);
        goto ret;
 
@@ -1592,8 +1728,43 @@ static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
 
 #else // !ENABLE_FEATURE_MOUNT_NFS
 
-// Never called. Call should be optimized out.
-int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
+/* Linux 2.6.23+ supports nfs mounts with options passed as a string.
+ * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
+ * (However, note that then you lose any chances that NFS over IPv6 would work).
+ */
+static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
+{
+       len_and_sockaddr *lsa;
+       char *opts;
+       char *end;
+       char *dotted;
+       int ret;
+
+# if ENABLE_FEATURE_IPV6
+       end = strchr(mp->mnt_fsname, ']');
+       if (end && end[1] == ':')
+               end++;
+       else
+# endif
+               /* mount_main() guarantees that ':' is there */
+               end = strchr(mp->mnt_fsname, ':');
+
+       *end = '\0';
+       lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
+       *end = ':';
+       dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
+       if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
+       opts = xasprintf("%s%saddr=%s",
+               filteropts ? filteropts : "",
+               filteropts ? "," : "",
+               dotted
+       );
+       if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
+       ret = mount_it_now(mp, vfsflags, opts);
+       if (ENABLE_FEATURE_CLEAN_UP) free(opts);
+
+       return ret;
+}
 
 #endif // !ENABLE_FEATURE_MOUNT_NFS
 
@@ -1603,7 +1774,7 @@ int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
 static int singlemount(struct mntent *mp, int ignore_busy)
 {
        int rc = -1;
-       long vfsflags;
+       unsigned long vfsflags;
        char *loopFile = NULL, *filteropts = NULL;
        llist_t *fl = NULL;
        struct stat st;
@@ -1653,17 +1824,44 @@ static int singlemount(struct mntent *mp, int ignore_busy)
        ) {
                int len;
                char c;
+               char *hostname, *share;
+               char *dotted, *ip;
                len_and_sockaddr *lsa;
-               char *hostname, *dotted, *ip;
+
+               // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
 
                hostname = mp->mnt_fsname + 2;
                len = strcspn(hostname, "/\\");
-               if (len == 0 || hostname[len] == '\0')
+               share = hostname + len + 1;
+               if (len == 0          // 3rd char is a [back]slash (IOW: empty hostname)
+                || share[-1] == '\0' // no [back]slash after hostname
+                || share[0] == '\0'  // empty share name
+               ) {
                        goto report_error;
-               c = hostname[len];
-               hostname[len] = '\0';
+               }
+               c = share[-1];
+               share[-1] = '\0';
+               len = strcspn(share, "/\\");
+
+               // "unc=\\hostname\share" option is mandatory
+               // after CIFS option parsing was rewritten in Linux 3.4.
+               // Must use backslashes.
+               // If /dir1/dir2 is present, also add "prefixpath=dir1/dir2"
+               {
+                       char *unc = xasprintf(
+                               share[len] != '\0'  /* "/dir1/dir2" exists? */
+                                       ? "unc=\\\\%s\\%.*s,prefixpath=%s"
+                                       : "unc=\\\\%s\\%.*s",
+                               hostname,
+                               len, share,
+                               share + len + 1  /* "dir1/dir2" */
+                       );
+                       parse_mount_options(unc, &filteropts);
+                       if (ENABLE_FEATURE_CLEAN_UP) free(unc);
+               }
+
                lsa = host2sockaddr(hostname, 0);
-               hostname[len] = c;
+               share[-1] = c;
                if (!lsa)
                        goto report_error;
 
@@ -1675,8 +1873,6 @@ static int singlemount(struct mntent *mp, int ignore_busy)
                parse_mount_options(ip, &filteropts);
                if (ENABLE_FEATURE_CLEAN_UP) free(ip);
 
-               // "-o mand" is required [why?]
-               vfsflags |= MS_MANDLOCK;
                mp->mnt_type = (char*)"cifs";
                rc = mount_it_now(mp, vfsflags, filteropts);
 
@@ -1684,10 +1880,11 @@ static int singlemount(struct mntent *mp, int ignore_busy)
        }
 
        // Might this be an NFS filesystem?
-       if (ENABLE_FEATURE_MOUNT_NFS
-        && (!mp->mnt_type || strcmp(mp->mnt_type, "nfs") == 0)
+       if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0)
         && strchr(mp->mnt_fsname, ':') != NULL
        ) {
+               if (!mp->mnt_type)
+                       mp->mnt_type = (char*)"nfs";
                rc = nfsmount(mp, vfsflags, filteropts);
                goto report_error;
        }
@@ -1703,7 +1900,7 @@ static int singlemount(struct mntent *mp, int ignore_busy)
                if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
                        loopFile = bb_simplify_path(mp->mnt_fsname);
                        mp->mnt_fsname = NULL; // will receive malloced loop dev name
-                       if (set_loop(&mp->mnt_fsname, loopFile, 0) < 0) {
+                       if (set_loop(&mp->mnt_fsname, loopFile, 0, /*ro:*/ (vfsflags & MS_RDONLY)) < 0) {
                                if (errno == EPERM || errno == EACCES)
                                        bb_error_msg(bb_msg_perm_denied_are_you_root);
                                else
@@ -1716,26 +1913,19 @@ static int singlemount(struct mntent *mp, int ignore_busy)
                        vfsflags |= MS_BIND;
        }
 
-       // Mount filesystem if we know fstype (or list of fstypes)
-       if (mp->mnt_type) {
-               char *fstype, *next;
-
-               mp->mnt_type = fstype = xstrdup(mp->mnt_type);
-
-               do {
-                       next = strchr(fstype, ',');
-                       if (next != NULL)
+       // If we know the fstype (or don't need to), jump straight
+       // to the actual mount.
+       if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
+               char *next;
+               for (;;) {
+                       next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
+                       if (next)
                                *next = '\0';
                        rc = mount_it_now(mp, vfsflags, filteropts);
-                       if (!rc)
+                       if (rc == 0 || !next)
                                break;
                        mp->mnt_type = next + 1;
-               } while (next != NULL);
-
-               free(fstype);
-       } else if (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)) {
-               // Jump straight to the actual mount if possible
-               rc = mount_it_now(mp, vfsflags, filteropts);
+               }
        } else {
                // Loop through filesystem types until mount succeeds
                // or we run out
@@ -1752,7 +1942,7 @@ static int singlemount(struct mntent *mp, int ignore_busy)
                for (fl = fslist; fl; fl = fl->link) {
                        mp->mnt_type = fl->data;
                        rc = mount_it_now(mp, vfsflags, filteropts);
-                       if (!rc)
+                       if (rc == 0)
                                break;
                }
        }
@@ -1848,12 +2038,15 @@ int mount_main(int argc UNUSED_PARAM, char **argv)
        FILE *fstab;
        int i, j;
        int rc = EXIT_SUCCESS;
+       unsigned long cmdopt_flags;
        unsigned opt;
        struct mntent mtpair[2], *mtcur = mtpair;
        IF_NOT_DESKTOP(const int nonroot = 0;)
 
        IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
 
+       INIT_G();
+
        // Parse long options, like --bind and --move.  Note that -o option
        // and --option are synonymous.  Yes, this means --remount,rw works.
        for (i = j = 1; argv[i]; i++) {
@@ -1920,16 +2113,16 @@ int mount_main(int argc UNUSED_PARAM, char **argv)
        // Past this point, we are handling either "mount -a [opts]"
        // or "mount [opts] single_param"
 
-       i = parse_mount_options(cmdopts, NULL); // FIXME: should be "long", not "int"
-       if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
+       cmdopt_flags = parse_mount_options(cmdopts, NULL);
+       if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
                bb_error_msg_and_die(bb_msg_you_must_be_root);
 
        // If we have a shared subtree flag, don't worry about fstab or mtab.
        if (ENABLE_FEATURE_MOUNT_FLAGS
-        && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
+        && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
        ) {
                // verbose_mount(source, target, type, flags, data)
-               rc = verbose_mount("", argv[0], "", i, "");
+               rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
                if (rc)
                        bb_simple_perror_msg_and_die(argv[0]);
                return rc;
@@ -1937,7 +2130,7 @@ int mount_main(int argc UNUSED_PARAM, char **argv)
 
        // Open either fstab or mtab
        fstabname = "/etc/fstab";
-       if (i & MS_REMOUNT) {
+       if (cmdopt_flags & MS_REMOUNT) {
                // WARNING. I am not sure this matches util-linux's
                // behavior. It's possible util-linux does not
                // take -o opts from mtab (takes only mount source).
@@ -2036,7 +2229,7 @@ int mount_main(int argc UNUSED_PARAM, char **argv)
        // End of fstab/mtab is reached.
        // Were we looking for something specific?
        if (argv[0]) { // yes
-               long l;
+               unsigned long l;
 
                // If we didn't find anything, complain
                if (!mtcur->mnt_fsname)
index 28af00c..83f01fa 100644 (file)
@@ -6,8 +6,15 @@
  * pivot_root syscall stubbed by Erik Andersen, so it will compile
  *     regardless of the kernel being used.
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define pivot_root_trivial_usage
+//usage:       "NEW_ROOT PUT_OLD"
+//usage:#define pivot_root_full_usage "\n\n"
+//usage:       "Move the current root file system to PUT_OLD and make NEW_ROOT\n"
+//usage:       "the new root file system"
+
 #include "libbb.h"
 
 extern int pivot_root(const char * new_root,const char * put_old);
index f257d54..6e35cd5 100644 (file)
@@ -1,13 +1,20 @@
 /* vi: set sw=4 ts=4: */
 /*
  * The Rdate command will ask a time server for the RFC 868 time
- *  and optionally set the system time.
+ * and optionally set the system time.
  *
  * by Sterling Huxley <sterling@europa.com>
  *
- * Licensed under GPL v2 or later, see file License for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 */
 
+//usage:#define rdate_trivial_usage
+//usage:       "[-sp] HOST"
+//usage:#define rdate_full_usage "\n\n"
+//usage:       "Get and possibly set the system date/time from a remote HOST\n"
+//usage:     "\n       -s      Set the system date/time (default)"
+//usage:     "\n       -p      Print the date/time"
+
 #include "libbb.h"
 
 enum { RFC_868_BIAS = 2208988800UL };
@@ -28,15 +35,16 @@ static time_t askremotedate(const char *host)
 
        fd = create_and_connect_stream_or_die(host, bb_lookup_port("time", "tcp", 37));
 
-       if (safe_read(fd, (void *)&nett, 4) != 4)    /* read time from server */
+       if (safe_read(fd, &nett, 4) != 4)    /* read time from server */
                bb_error_msg_and_die("%s did not send the complete time", host);
-       close(fd);
+       if (ENABLE_FEATURE_CLEAN_UP)
+               close(fd);
 
-       /* convert from network byte order to local byte order.
+       /* 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
-        * Subtract the RFC 868 time to get Linux epoch
+        * Subtract the RFC 868 time to get Linux epoch.
         */
 
        return ntohl(nett) - RFC_868_BIAS;
@@ -46,14 +54,14 @@ int rdate_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int rdate_main(int argc UNUSED_PARAM, char **argv)
 {
        time_t remote_time;
-       unsigned long flags;
+       unsigned flags;
 
        opt_complementary = "-1";
        flags = getopt32(argv, "sp");
 
        remote_time = askremotedate(argv[optind]);
 
-       if ((flags & 2) == 0) {
+       if (!(flags & 2)) { /* no -p (-s may be present) */
                time_t current_time;
 
                time(&current_time);
@@ -64,7 +72,7 @@ int rdate_main(int argc UNUSED_PARAM, char **argv)
                                bb_perror_msg_and_die("can't set time of day");
        }
 
-       if ((flags & 1) == 0)
+       if (flags != 1) /* not lone -s */
                printf("%s", ctime(&remote_time));
 
        return EXIT_SUCCESS;
index 33abd39..4652817 100644 (file)
@@ -5,18 +5,27 @@
  * Copyright (c) 2008 Nuovation System Designs, LLC
  *   Grant Erickson <gerickson@nuovations.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  *
  */
 
+//usage:#define rdev_trivial_usage
+//usage:       ""
+//usage:#define rdev_full_usage "\n\n"
+//usage:       "Print the device node associated with the filesystem mounted at '/'"
+//usage:
+//usage:#define rdev_example_usage
+//usage:       "$ rdev\n"
+//usage:       "/dev/mtdblock9 /\n"
+
 #include "libbb.h"
 
 int rdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int rdev_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 {
-       char const * const root_device = find_block_device("/");
+       const char *root_device = find_block_device("/");
 
-       if (root_device != NULL) {
+       if (root_device) {
                printf("%s /\n", root_device);
                return EXIT_SUCCESS;
        }
index 2196544..a645404 100644 (file)
@@ -4,7 +4,7 @@
  *
  *  Copyright (C) 1994,1996 Alessandro Rubini (rubini@ipvvis.unipv.it)
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*
  * Paul Mundt <lethal@linux-sh.org>.
  */
 
+//usage:#define readprofile_trivial_usage
+//usage:       "[OPTIONS]"
+//usage:#define readprofile_full_usage "\n\n"
+//usage:       "       -m mapfile      (Default: /boot/System.map)"
+//usage:     "\n       -p profile      (Default: /proc/profile)"
+//usage:     "\n       -M NUM          Set the profiling multiplier to NUM"
+//usage:     "\n       -i              Print only info about the sampling step"
+//usage:     "\n       -v              Verbose"
+//usage:     "\n       -a              Print all symbols, even if count is 0"
+//usage:     "\n       -b              Print individual histogram-bin counts"
+//usage:     "\n       -s              Print individual counters within functions"
+//usage:     "\n       -r              Reset all the counters (root only)"
+//usage:     "\n       -n              Disable byte order auto-detection"
+
 #include "libbb.h"
 #include <sys/utsname.h>
 
@@ -97,7 +111,7 @@ int readprofile_main(int argc UNUSED_PARAM, char **argv)
                 */
                to_write = sizeof(int);
                if (!optMult)
-                       to_write = 1;   /* sth different from sizeof(int) */
+                       to_write = 1;  /* sth different from sizeof(int) */
 
                fd = xopen(defaultpro, O_WRONLY);
                xwrite(fd, &multiplier, to_write);
@@ -138,7 +152,7 @@ int readprofile_main(int argc UNUSED_PARAM, char **argv)
 
        step = buf[0];
        if (optInfo) {
-               printf("Sampling_step: %i\n", step);
+               printf("Sampling_step: %u\n", step);
                return EXIT_SUCCESS;
        }
 
@@ -149,7 +163,7 @@ int readprofile_main(int argc UNUSED_PARAM, char **argv)
        while (fgets(mapline, S_LEN, map)) {
                if (sscanf(mapline, "%llx %s %s", &fn_add, mode, fn_name) != 3)
                        bb_error_msg_and_die("%s(%i): wrong map line",
-                                            mapFile, maplineno);
+                                       mapFile, maplineno);
 
                if (!strcmp(fn_name, "_stext")) /* only elf works like this */ {
                        add0 = fn_add;
@@ -179,12 +193,12 @@ int readprofile_main(int argc UNUSED_PARAM, char **argv)
                if (*mode != 'T' && *mode != 't'
                 && *mode != 'W' && *mode != 'w'
                ) {
-                       break;  /* only text is profiled */
+                       break;  /* only text is profiled */
                }
 
                if (indx >= len / sizeof(*buf))
                        bb_error_msg_and_die("profile address out of range. "
-                                            "Wrong map file?");
+                                       "Wrong map file?");
 
                while (indx < (next_add-add0)/step) {
                        if (optBins && (buf[indx] || optAll)) {
@@ -205,11 +219,11 @@ int readprofile_main(int argc UNUSED_PARAM, char **argv)
                        && (fn_len = next_add-fn_add) != 0
                ) {
                        if (optVerbose)
-                               printf("%016llx %-40s %6i %8.4f\n", fn_add,
-                                      fn_name, this, this/(double)fn_len);
+                               printf("%016llx %-40s %6u %8.4f\n", fn_add,
+                                       fn_name, this, this/(double)fn_len);
                        else
-                               printf("%6i %-40s %8.4f\n",
-                                      this, fn_name, this/(double)fn_len);
+                               printf("%6u %-40s %8.4f\n",
+                                       this, fn_name, this/(double)fn_len);
                        if (optSub) {
                                unsigned long long scan;
 
@@ -219,8 +233,8 @@ int readprofile_main(int argc UNUSED_PARAM, char **argv)
 
                                        addr = (scan - 1)*step + add0;
                                        printf("\t%#llx\t%s+%#llx\t%u\n",
-                                              addr, fn_name, addr - fn_add,
-                                              buf[scan]);
+                                               addr, fn_name, addr - fn_add,
+                                               buf[scan]);
                                }
                        }
                }
@@ -232,15 +246,15 @@ int readprofile_main(int argc UNUSED_PARAM, char **argv)
        }
 
        /* clock ticks, out of kernel text - probably modules */
-       printf("%6i %s\n", buf[len/sizeof(*buf)-1], "*unknown*");
+       printf("%6u %s\n", buf[len/sizeof(*buf)-1], "*unknown*");
 
        /* trailer */
        if (optVerbose)
-               printf("%016x %-40s %6i %8.4f\n",
-                      0, "total", total, total/(double)(fn_add-add0));
+               printf("%016x %-40s %6u %8.4f\n",
+                       0, "total", total, total/(double)(fn_add-add0));
        else
-               printf("%6i %-40s %8.4f\n",
-                      total, "total", total/(double)(fn_add-add0));
+               printf("%6u %-40s %8.4f\n",
+                       total, "total", total/(double)(fn_add-add0));
 
        fclose(map);
        free(buf);
index fa3a453..3c1b22f 100644 (file)
@@ -3,10 +3,10 @@
  *
  * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com>
  *
- * Licensed under GPLv2, see file License in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
-//applet:IF_REV(APPLET(rev, _BB_DIR_BIN, _BB_SUID_DROP))
+//applet:IF_REV(APPLET(rev, BB_DIR_BIN, BB_SUID_DROP))
 
 //kbuild:lib-$(CONFIG_REV) += rev.o
 
index b163766..735a298 100644 (file)
@@ -3,7 +3,7 @@
  *
  * This version was taken from util-linux and scrubbed down for busybox.
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  *
  * This uses cross-platform Linux interfaces to enter a system sleep state,
  * and leave it no later than a specified time.  It uses any RTC framework
  * That flag should not be needed on systems with adjtime support.
  */
 
+//usage:#define rtcwake_trivial_usage
+//usage:       "[-a | -l | -u] [-d DEV] [-m MODE] [-s SEC | -t TIME]"
+//usage:#define rtcwake_full_usage "\n\n"
+//usage:       "Enter a system sleep state until specified wakeup time\n"
+//usage:       IF_LONG_OPTS(
+//usage:     "\n       -a,--auto       Read clock mode from adjtime"
+//usage:     "\n       -l,--local      Clock is set to local time"
+//usage:     "\n       -u,--utc        Clock is set to UTC time"
+//usage:     "\n       -d,--device=DEV Specify the RTC device"
+//usage:     "\n       -m,--mode=MODE  Set the sleep state (default: standby)"
+//usage:     "\n       -s,--seconds=SEC Set the timeout in SEC seconds from now"
+//usage:     "\n       -t,--time=TIME  Set the timeout to TIME seconds from epoch"
+//usage:       )
+//usage:       IF_NOT_LONG_OPTS(
+//usage:     "\n       -a      Read clock mode from adjtime"
+//usage:     "\n       -l      Clock is set to local time"
+//usage:     "\n       -u      Clock is set to UTC time"
+//usage:     "\n       -d DEV  Specify the RTC device"
+//usage:     "\n       -m MODE Set the sleep state (default: standby)"
+//usage:     "\n       -s SEC  Set the timeout in SEC seconds from now"
+//usage:     "\n       -t TIME Set the timeout to TIME seconds from epoch"
+//usage:       )
+
 #include "libbb.h"
 #include "rtc_.h"
 
@@ -50,7 +73,7 @@ static NOINLINE bool may_wakeup(const char *rtcname)
 static NOINLINE void setup_alarm(int fd, time_t *wakeup, time_t rtc_time)
 {
        struct tm *ptm;
-       struct linux_rtc_wkalrm wake;
+       struct linux_rtc_wkalrm wake;
 
        /* The wakeup time is in POSIX time (more or less UTC).
         * Ideally RTCs use that same time; but PCs can't do that
index c23117c..8fb991d 100644 (file)
@@ -6,10 +6,22 @@
  *
  * Based on code from util-linux v 2.12r
  * Copyright (c) 1980
- *     The Regents of the University of California.  All rights reserved.
+ * The Regents of the University of California.  All rights reserved.
  *
- * Licensed under GPLv2 or later, see file License in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//usage:#define script_trivial_usage
+//usage:       "[-afq" IF_SCRIPTREPLAY("t") "] [-c PROG] [OUTFILE]"
+//usage:#define script_full_usage "\n\n"
+//usage:       "       -a      Append output"
+//usage:     "\n       -c PROG Run PROG, not shell"
+//usage:     "\n       -f      Flush output after each write"
+//usage:     "\n       -q      Quiet"
+//usage:       IF_SCRIPTREPLAY(
+//usage:     "\n       -t      Send timing to stderr"
+//usage:       )
+
 #include "libbb.h"
 
 int script_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -65,10 +77,7 @@ int script_main(int argc UNUSED_PARAM, char **argv)
        if (!(opt & OPT_q)) {
                printf("Script started, file is %s\n", fname);
        }
-       shell = getenv("SHELL");
-       if (shell == NULL) {
-               shell = DEFAULT_SHELL;
-       }
+       shell = get_shell_name();
 
        pty = xgetpty(pty_line);
 
index d7e1933..382f56d 100644 (file)
@@ -4,9 +4,15 @@
  *
  * pascal.bellard@ads-lu.com
  *
- * Licensed under GPLv2 or later, see file License in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  *
  */
+
+//usage:#define scriptreplay_trivial_usage
+//usage:       "timingfile [typescript [divisor]]"
+//usage:#define scriptreplay_full_usage "\n\n"
+//usage:       "Play back typescripts, using timing information"
+
 #include "libbb.h"
 
 int scriptreplay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
index 8213382..7b9421a 100644 (file)
@@ -4,8 +4,21 @@
  *
  * Copyright 2002 Andi Kleen, SuSE Labs.
  *
- * Licensed under GPL v2 or later, see file License for details.
-*/
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define setarch_trivial_usage
+//usage:       "personality PROG ARGS"
+//usage:#define setarch_full_usage "\n\n"
+//usage:       "Personality may be:\n"
+//usage:       "       linux32         Set 32bit uname emulation\n"
+//usage:       "       linux64         Set 64bit uname emulation"
+//usage:
+//usage:#define linux32_trivial_usage NOUSAGE_STR
+//usage:#define linux32_full_usage ""
+//usage:
+//usage:#define linux64_trivial_usage NOUSAGE_STR
+//usage:#define linux64_full_usage ""
 
 #include <sys/personality.h>
 
index f2f52fb..3f22334 100644 (file)
@@ -4,12 +4,29 @@
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
+//usage:#define swapon_trivial_usage
+//usage:       "[-a]" IF_FEATURE_SWAPON_PRI(" [-p PRI]") " [DEVICE]"
+//usage:#define swapon_full_usage "\n\n"
+//usage:       "Start swapping on DEVICE\n"
+//usage:     "\n       -a      Start swapping on all swap devices"
+//usage:       IF_FEATURE_SWAPON_PRI(
+//usage:     "\n       -p PRI  Set swap device priority"
+//usage:       )
+//usage:
+//usage:#define swapoff_trivial_usage
+//usage:       "[-a] [DEVICE]"
+//usage:#define swapoff_full_usage "\n\n"
+//usage:       "Stop swapping on DEVICE\n"
+//usage:     "\n       -a      Stop swapping on all swap devices"
+
 #include "libbb.h"
 #include <mntent.h>
-#include <sys/swap.h>
+#ifndef __BIONIC__
+# include <sys/swap.h>
+#endif
 
 #if ENABLE_FEATURE_MOUNT_LABEL
 # include "volume_id.h"
 # define resolve_mount_spec(fsname) ((void)0)
 #endif
 
+#ifndef MNTTYPE_SWAP
+# define MNTTYPE_SWAP "swap"
+#endif
+
 #if ENABLE_FEATURE_SWAPON_PRI
 struct globals {
        int flags;
@@ -26,6 +47,7 @@ struct globals {
 #else
 #define g_flags 0
 #endif
+#define INIT_G() do { } while (0)
 
 static int swap_enable_disable(char *device)
 {
@@ -73,6 +95,20 @@ static int do_em_all(void)
                        if (applet_name[5] != 'n'
                         || hasmntopt(m, MNTOPT_NOAUTO) == NULL
                        ) {
+#if ENABLE_FEATURE_SWAPON_PRI
+                               char *p;
+                               g_flags = 0; /* each swap space might have different flags */
+                               p = hasmntopt(m, "pri");
+                               if (p) {
+                                       /* Max allowed 32767 (==SWAP_FLAG_PRIO_MASK) */
+                                       unsigned int swap_prio = MIN(bb_strtou(p + 4 , NULL, 10), SWAP_FLAG_PRIO_MASK);
+                                       /* We want to allow "NNNN,foo", thus errno == EINVAL is allowed too */
+                                       if (errno != ERANGE) {
+                                               g_flags = SWAP_FLAG_PREFER |
+                                                       (swap_prio << SWAP_FLAG_PRIO_SHIFT);
+                                       }
+                               }
+#endif
                                err += swap_enable_disable(m->mnt_fsname);
                        }
                }
@@ -89,10 +125,13 @@ int swap_on_off_main(int argc UNUSED_PARAM, char **argv)
 {
        int ret;
 
+       INIT_G();
+
 #if !ENABLE_FEATURE_SWAPON_PRI
        ret = getopt32(argv, "a");
 #else
-       opt_complementary = "p+";
+       if (applet_name[5] == 'n')
+               opt_complementary = "p+";
        ret = getopt32(argv, (applet_name[5] == 'n') ? "ap:" : "a", &g_flags);
 
        if (ret & 2) { // -p
index e2da119..a301b36 100644 (file)
@@ -3,8 +3,17 @@
  *
  * Switch from rootfs to another filesystem as the root of the mount tree.
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define switch_root_trivial_usage
+//usage:       "[-c /dev/console] NEW_ROOT NEW_INIT [ARGS]"
+//usage:#define switch_root_full_usage "\n\n"
+//usage:       "Free initramfs and switch to another root fs:\n"
+//usage:       "chroot to NEW_ROOT, delete all in /, move NEW_ROOT to /,\n"
+//usage:       "execute NEW_INIT. PID must be 1. NEW_ROOT must be a mountpoint.\n"
+//usage:     "\n       -c DEV  Reopen stdio to DEV after switch"
+
 #include <sys/vfs.h>
 #include <sys/mount.h>
 #include "libbb.h"
@@ -105,7 +114,7 @@ int switch_root_main(int argc UNUSED_PARAM, char **argv)
        }
        xchroot(".");
        // The chdir is needed to recalculate "." and ".." links
-       xchdir("/");
+       /*xchdir("/"); - done in xchroot */
 
        // If a new console specified, redirect stdin/stdout/stderr to it
        if (console) {
index a19f86c..4c2e882 100644 (file)
@@ -5,46 +5,35 @@
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  * Copyright (C) 2005 by Rob Landley <rob@landley.net>
  *
- * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+
+//usage:#define umount_trivial_usage
+//usage:       "[OPTIONS] FILESYSTEM|DIRECTORY"
+//usage:#define umount_full_usage "\n\n"
+//usage:       "Unmount file systems\n"
+//usage:       IF_FEATURE_UMOUNT_ALL(
+//usage:     "\n       -a      Unmount all file systems" IF_FEATURE_MTAB_SUPPORT(" in /etc/mtab")
+//usage:       )
+//usage:       IF_FEATURE_MTAB_SUPPORT(
+//usage:     "\n       -n      Don't erase /etc/mtab entries"
+//usage:       )
+//usage:     "\n       -r      Try to remount devices as read-only if mount is busy"
+//usage:     "\n       -l      Lazy umount (detach filesystem)"
+//usage:     "\n       -f      Force umount (i.e., unreachable NFS server)"
+//usage:       IF_FEATURE_MOUNT_LOOP(
+//usage:     "\n       -D      Don't free loop device even if it has been used"
+//usage:       )
+//usage:
+//usage:#define umount_example_usage
+//usage:       "$ umount /dev/hdc1\n"
+
 #include <mntent.h>
 #include <sys/mount.h>
-/* Make sure we have all the new mount flags we actually try to use. */
-#ifndef MS_BIND
-# define MS_BIND        (1 << 12)
-#endif
-#ifndef MS_MOVE
-# define MS_MOVE        (1 << 13)
-#endif
-#ifndef MS_RECURSIVE
-# define MS_RECURSIVE   (1 << 14)
-#endif
-#ifndef MS_SILENT
-# define MS_SILENT      (1 << 15)
-#endif
-/* The shared subtree stuff, which went in around 2.6.15. */
-#ifndef MS_UNBINDABLE
-# define MS_UNBINDABLE  (1 << 17)
-#endif
-#ifndef MS_PRIVATE
-# define MS_PRIVATE     (1 << 18)
-#endif
-#ifndef MS_SLAVE
-# define MS_SLAVE       (1 << 19)
-#endif
-#ifndef MS_SHARED
-# define MS_SHARED      (1 << 20)
-#endif
-#ifndef MS_RELATIME
-# define MS_RELATIME    (1 << 21)
-#endif
 #include "libbb.h"
-#ifndef PATH_MAX
-# define PATH_MAX (4*1024)
-#endif
-
 
 #if defined(__dietlibc__)
+// TODO: This does not belong here.
 /* 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
  * dietlibc-0.30 does not have implementation of getmntent_r() */
 static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
@@ -55,25 +44,27 @@ static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
 }
 #endif
 
-/* ignored: -v -d -t -i */
-#define OPTION_STRING           "fldnra" "vdt:i"
-#define OPT_FORCE               (1 << 0)
-#define OPT_LAZY                (1 << 1)
-#define OPT_FREELOOP            (1 << 2)
-#define OPT_NO_MTAB             (1 << 3)
-#define OPT_REMOUNT             (1 << 4)
-#define OPT_ALL                 (ENABLE_FEATURE_UMOUNT_ALL ? (1 << 5) : 0)
-
-// These constants from linux/fs.h must match OPT_FORCE and OPT_LAZY,
-// otherwise "doForce" trick below won't work!
-//#define MNT_FORCE  0x00000001 /* Attempt to forcibly umount */
-//#define MNT_DETACH 0x00000002 /* Just detach from the tree */
+/* Ignored: -v -t -i
+ * bbox always acts as if -d is present.
+ * -D can be used to suppress it (bbox extension).
+ * Rationale:
+ * (1) util-linux's umount does it if "loop=..." is seen in /etc/mtab:
+ * thus, on many systems, bare umount _does_ drop loop devices.
+ * (2) many users request this feature.
+ */
+#define OPTION_STRING           "fldDnra" "vt:i"
+#define OPT_FORCE               (1 << 0) // Same as MNT_FORCE
+#define OPT_LAZY                (1 << 1) // Same as MNT_DETACH
+//#define OPT_FREE_LOOP           (1 << 2) // -d is assumed always present
+#define OPT_DONT_FREE_LOOP      (1 << 3)
+#define OPT_NO_MTAB             (1 << 4)
+#define OPT_REMOUNT             (1 << 5)
+#define OPT_ALL                 (ENABLE_FEATURE_UMOUNT_ALL ? (1 << 6) : 0)
 
 int umount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int umount_main(int argc UNUSED_PARAM, char **argv)
 {
        int doForce;
-       char *const buf = xmalloc(PATH_MAX * 2 + 128); /* to save stack */
        struct mntent me;
        FILE *fp;
        char *fstype = NULL;
@@ -88,6 +79,9 @@ int umount_main(int argc UNUSED_PARAM, char **argv)
        opt = getopt32(argv, OPTION_STRING, &fstype);
        //argc -= optind;
        argv += optind;
+
+       // MNT_FORCE and MNT_DETACH (from linux/fs.h) must match
+       // OPT_FORCE and OPT_LAZY, otherwise this trick won't work:
        doForce = MAX((opt & OPT_FORCE), (opt & OPT_LAZY));
 
        /* Get a list of mount points from mtab.  We read them all in now mostly
@@ -104,7 +98,7 @@ int umount_main(int argc UNUSED_PARAM, char **argv)
                if (opt & OPT_ALL)
                        bb_error_msg_and_die("can't open '%s'", bb_path_mtab_file);
        } else {
-               while (getmntent_r(fp, &me, buf, PATH_MAX * 2 + 128)) {
+               while (getmntent_r(fp, &me, bb_common_bufsiz1, sizeof(bb_common_bufsiz1))) {
                        /* Match fstype if passed */
                        if (!match_fstype(&me, fstype))
                                continue;
@@ -179,7 +173,7 @@ int umount_main(int argc UNUSED_PARAM, char **argv)
                } else {
                        // De-allocate the loop device.  This ioctl should be ignored on
                        // any non-loop block devices.
-                       if (ENABLE_FEATURE_MOUNT_LOOP && (opt & OPT_FREELOOP) && m)
+                       if (ENABLE_FEATURE_MOUNT_LOOP && !(opt & OPT_DONT_FREE_LOOP) && m)
                                del_loop(m->device);
                        if (ENABLE_FEATURE_MTAB_SUPPORT && !(opt & OPT_NO_MTAB) && m)
                                erase_mtab(m->dir);
@@ -206,7 +200,6 @@ int umount_main(int argc UNUSED_PARAM, char **argv)
                        free(mtl);
                        mtl = m;
                }
-               free(buf);
        }
 
        return status;
diff --git a/util-linux/volume_id/Config.src b/util-linux/volume_id/Config.src
new file mode 100644 (file)
index 0000000..ac208c9
--- /dev/null
@@ -0,0 +1,15 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+config VOLUMEID
+       bool #No description makes it a hidden option
+       default n
+
+menu "Filesystem/Volume identification"
+       depends on VOLUMEID
+
+INSERT
+
+endmenu
index 8af3ccd..6b4fb74 100644 (file)
@@ -2,44 +2,8 @@
 #
 # Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
 #
-# Licensed under the GPL v2, see the file LICENSE in this tarball.
+# Licensed under GPLv2, see file LICENSE in this source tree.
 
 lib-y:=
 
 INSERT
-
-lib-$(CONFIG_BLKID)                             += get_devname.o
-lib-$(CONFIG_FINDFS)                            += get_devname.o
-lib-$(CONFIG_FEATURE_MOUNT_LABEL)               += get_devname.o
-
-lib-$(CONFIG_VOLUMEID)                          += volume_id.o util.o
-lib-$(CONFIG_FEATURE_VOLUMEID_BTRFS)            += btrfs.o
-lib-$(CONFIG_FEATURE_VOLUMEID_EXT)              += ext.o
-lib-$(CONFIG_FEATURE_VOLUMEID_FAT)              += fat.o
-lib-$(CONFIG_FEATURE_VOLUMEID_HFS)              += hfs.o
-### lib-$(CONFIG_FEATURE_VOLUMEID_HIGHPOINTRAID)    += highpoint.o
-### lib-$(CONFIG_FEATURE_VOLUMEID_ISWRAID)          += isw_raid.o
-### lib-$(CONFIG_FEATURE_VOLUMEID_LSIRAID)          += lsi_raid.o
-### lib-$(CONFIG_FEATURE_VOLUMEID_VIARAID)          += via_raid.o
-### lib-$(CONFIG_FEATURE_VOLUMEID_SILICONRAID)      += silicon_raid.o
-### lib-$(CONFIG_FEATURE_VOLUMEID_NVIDIARAID)       += nvidia_raid.o
-### lib-$(CONFIG_FEATURE_VOLUMEID_PROMISERAID)      += promise_raid.o
-lib-$(CONFIG_FEATURE_VOLUMEID_ISO9660)          += iso9660.o
-lib-$(CONFIG_FEATURE_VOLUMEID_JFS)              += jfs.o
-lib-$(CONFIG_FEATURE_VOLUMEID_LINUXRAID)        += linux_raid.o
-lib-$(CONFIG_FEATURE_VOLUMEID_LINUXSWAP)        += linux_swap.o
-### lib-$(CONFIG_FEATURE_VOLUMEID_LVM)              += lvm.o
-### lib-$(CONFIG_FEATURE_VOLUMEID_MAC)              += mac.o
-### lib-$(CONFIG_FEATURE_VOLUMEID_MSDOS)            += msdos.o
-lib-$(CONFIG_FEATURE_VOLUMEID_NTFS)             += ntfs.o
-lib-$(CONFIG_FEATURE_VOLUMEID_REISERFS)         += reiserfs.o
-lib-$(CONFIG_FEATURE_VOLUMEID_UDF)              += udf.o
-### lib-$(CONFIG_FEATURE_VOLUMEID_UFS)              += ufs.o
-lib-$(CONFIG_FEATURE_VOLUMEID_XFS)              += xfs.o
-lib-$(CONFIG_FEATURE_VOLUMEID_CRAMFS)           += cramfs.o
-### lib-$(CONFIG_FEATURE_VOLUMEID_HPFS)             += hpfs.o
-lib-$(CONFIG_FEATURE_VOLUMEID_ROMFS)            += romfs.o
-lib-$(CONFIG_FEATURE_VOLUMEID_SYSV)             += sysv.o
-### lib-$(CONFIG_FEATURE_VOLUMEID_MINIX)            += minix.o
-lib-$(CONFIG_FEATURE_VOLUMEID_LUKS)             += luks.o
-lib-$(CONFIG_FEATURE_VOLUMEID_OCFS2)            += ocfs2.o
index 777b809..e4dddf2 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_BTRFS) += btrfs.o
+
+//config:
+//config:config FEATURE_VOLUMEID_BTRFS
+//config:      bool "btrfs filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 #define BTRFS_UUID_SIZE 16
@@ -102,6 +113,7 @@ int FAST_FUNC volume_id_probe_btrfs(struct volume_id *id /*,uint64_t off*/)
        // N.B.: btrfs natively supports 256 (>VOLUME_ID_LABEL_SIZE) size labels
        volume_id_set_label_string(id, sb->label, VOLUME_ID_LABEL_SIZE);
        volume_id_set_uuid(id, sb->fsid, UUID_DCE);
+       IF_FEATURE_BLKID_TYPE(id->type = "btrfs";)
 
        return 0;
 }
index b84a6f0..aeb7f20 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_CRAMFS) += cramfs.o
+
+//config:
+//config:config FEATURE_VOLUMEID_CRAMFS
+//config:      bool "cramfs filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct cramfs_super {
@@ -51,7 +62,7 @@ int FAST_FUNC volume_id_probe_cramfs(struct volume_id *id /*,uint64_t off*/)
                volume_id_set_label_string(id, cs->name, 16);
 
 //             volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
-//             id->type = "cramfs";
+               IF_FEATURE_BLKID_TYPE(id->type = "cramfs";)
                return 0;
        }
 
diff --git a/util-linux/volume_id/exfat.c b/util-linux/volume_id/exfat.c
new file mode 100644 (file)
index 0000000..c3aa368
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2012 S-G Bergh <sgb@systemasis.org>
+ *
+ *     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_EXFAT) += exfat.o
+
+//config:
+//config:config FEATURE_VOLUMEID_EXFAT
+//config:      bool "exFAT filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        exFAT (extended FAT) is a proprietary file system designed especially
+//config:        for flash drives. It has many features from NTFS, but with less
+//config:        overhead. exFAT is used on most SDXC cards for consumer electronics.
+//config:
+
+#include "volume_id_internal.h"
+
+#define EXFAT_SB_OFFSET                0
+#define EXFAT_DIR_ENTRY_SZ     32
+#define EXFAT_MAX_DIR_ENTRIES  100
+
+struct exfat_super_block {
+/* 0x00 */     uint8_t         boot_jump[3];
+/* 0x03 */     uint8_t         fs_name[8];
+/* 0x0B */     uint8_t         must_be_zero[53];
+/* 0x40 */     uint64_t        partition_offset;
+/* 0x48 */     uint64_t        volume_length;
+/* 0x50 */     uint32_t        fat_offset;             // Sector address of 1st FAT
+/* 0x54 */     uint32_t        fat_size;               // In sectors
+/* 0x58 */     uint32_t        cluster_heap_offset;    // Sector address of Data Region
+/* 0x5C */     uint32_t        cluster_count;
+/* 0x60 */     uint32_t        root_dir;               // Cluster address of Root Directory
+/* 0x64 */     uint8_t         vol_serial_nr[4];       // Volume ID
+/* 0x68 */     uint16_t        fs_revision;            // VV.MM
+/* 0x6A */     uint16_t        vol_flags;
+/* 0x6C */     uint8_t         bytes_per_sector;       // Power of 2: 9 => 512, 12 => 4096
+/* 0x6D */     uint8_t         sectors_per_cluster;    // Power of 2
+/* 0x6E */     uint8_t         nr_of_fats;             // 2 for TexFAT
+/* 0x6F */     // ...
+} PACKED;
+
+struct exfat_dir_entry {
+/* 0x00 */     uint8_t         entry_type;
+               union {
+                       struct volume_label {
+/* 0x01 */                     uint8_t         char_count;             // Length of label
+/* 0x02 */                     uint16_t        vol_label[11];          // UTF16 string without null termination
+/* 0x18 */                     uint8_t         reserved[8];
+/* 0x20 */             } PACKED label;
+                       struct volume_guid {
+/* 0x01 */                     uint8_t         sec_count;
+/* 0x02 */                     uint16_t        set_checksum;
+/* 0x04 */                     uint16_t        flags;
+/* 0x06 */                     uint8_t         vol_guid[16];
+/* 0x16 */                     uint8_t         reserved[10];
+/* 0x20 */             } PACKED guid;
+               } PACKED type;
+} PACKED;
+
+int FAST_FUNC volume_id_probe_exfat(struct volume_id *id /*,uint64_t off*/)
+{
+       struct exfat_super_block *sb;
+       struct exfat_dir_entry *de;
+       unsigned        sector_sz;
+       unsigned        cluster_sz;
+       uint64_t        root_dir_off;
+       unsigned        count;
+       unsigned        need_lbl_guid;
+
+       // Primary super block
+       dbg("exFAT: probing at offset 0x%x", EXFAT_SB_OFFSET);
+       sb = volume_id_get_buffer(id, EXFAT_SB_OFFSET, sizeof(*sb));
+
+       if (!sb)
+               return -1;
+
+       if (memcmp(sb->fs_name, "EXFAT   ", 8) != 0)
+               return -1;
+
+       sector_sz = 1 << sb->bytes_per_sector;
+       cluster_sz = sector_sz << sb->sectors_per_cluster;
+       // There are no clusters 0 and 1, so the first cluster is 2.
+       root_dir_off = (uint64_t)EXFAT_SB_OFFSET +
+               // Hmm... should we cast sector_sz/cluster_sz to uint64_t?
+               (le32_to_cpu(sb->cluster_heap_offset)) * sector_sz +
+               (le32_to_cpu(sb->root_dir) - 2) * cluster_sz;
+       dbg("exFAT: sector size 0x%x bytes", sector_sz);
+       dbg("exFAT: cluster size 0x%x bytes", cluster_sz);
+       dbg("exFAT: root dir is at 0x%llx", (long long)root_dir_off);
+
+       // Use DOS uuid as fallback, if no GUID set
+       volume_id_set_uuid(id, sb->vol_serial_nr, UUID_DOS);
+
+       // EXFAT_MAX_DIR_ENTRIES is used as a safety belt.
+       // The Root Directory may hold an unlimited number of entries,
+       // so we do not want to check all. Usually label and GUID
+       // are in the beginning, but there are no guarantees.
+       need_lbl_guid = (1 << 0) | (1 << 1);
+       for (count = 0; count < EXFAT_MAX_DIR_ENTRIES; count++) {
+               de = volume_id_get_buffer(id, root_dir_off + (count * EXFAT_DIR_ENTRY_SZ), EXFAT_DIR_ENTRY_SZ);
+               if (de == NULL)
+                       break;
+               if (de->entry_type == 0x00) {
+                       // End of Directory Marker
+                       dbg("exFAT: End of root directory reached after %u entries", count);
+                       break;
+               }
+               if (de->entry_type == 0x83) {
+                       // Volume Label Directory Entry
+                       volume_id_set_label_unicode16(id, (uint8_t *)de->type.label.vol_label,
+                                               LE, 2 * de->type.label.char_count);
+                       need_lbl_guid &= ~(1 << 0);
+               }
+               if (de->entry_type == 0xA0) {
+                       // Volume GUID Directory Entry
+                       volume_id_set_uuid(id, de->type.guid.vol_guid, UUID_DCE);
+                       need_lbl_guid &= ~(1 << 1);
+               }
+               if (!need_lbl_guid)
+                       break;
+       }
+
+       IF_FEATURE_BLKID_TYPE(id->type = "exfat";)
+       return 0;
+}
index 80c217f..df39d93 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
-#include "volume_id_internal.h"
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_EXT) += ext.o
+
+//config:
+//config:config FEATURE_VOLUMEID_EXT
+//config:      bool "Ext filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
 
-struct ext2_super_block {
-       uint32_t        inodes_count;
-       uint32_t        blocks_count;
-       uint32_t        r_blocks_count;
-       uint32_t        free_blocks_count;
-       uint32_t        free_inodes_count;
-       uint32_t        first_data_block;
-       uint32_t        log_block_size;
-       uint32_t        dummy3[7];
-       uint8_t magic[2];
-       uint16_t        state;
-       uint32_t        dummy5[8];
-       uint32_t        feature_compat;
-       uint32_t        feature_incompat;
-       uint32_t        feature_ro_compat;
-       uint8_t uuid[16];
-       uint8_t volume_name[16];
-} PACKED;
+#include "volume_id_internal.h"
+#include "bb_e2fs_defs.h"
 
-#define EXT3_FEATURE_COMPAT_HAS_JOURNAL                0x00000004
-#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV      0x00000008
 #define EXT_SUPERBLOCK_OFFSET                  0x400
 
 int FAST_FUNC volume_id_probe_ext(struct volume_id *id /*,uint64_t off*/)
@@ -54,21 +45,27 @@ int FAST_FUNC volume_id_probe_ext(struct volume_id *id /*,uint64_t off*/)
        if (es == NULL)
                return -1;
 
-       if (es->magic[0] != 0123 || es->magic[1] != 0357) {
+       if (es->s_magic != cpu_to_le16(EXT2_SUPER_MAGIC)) {
                dbg("ext: no magic found");
                return -1;
        }
 
 //     volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
 //     volume_id_set_label_raw(id, es->volume_name, 16);
-       volume_id_set_label_string(id, es->volume_name, 16);
-       volume_id_set_uuid(id, es->uuid, UUID_DCE);
+       volume_id_set_label_string(id, (void*)es->s_volume_name, 16);
+       volume_id_set_uuid(id, es->s_uuid, UUID_DCE);
        dbg("ext: label '%s' uuid '%s'", id->label, id->uuid);
 
-//     if ((le32_to_cpu(es->feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0)
-//             id->type = "ext3";
-//     else
-//             id->type = "ext2";
-
+#if ENABLE_FEATURE_BLKID_TYPE
+       if ((es->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_HUGE_FILE | EXT4_FEATURE_RO_COMPAT_DIR_NLINK))
+        || (es->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_EXTENTS | EXT4_FEATURE_INCOMPAT_64BIT))
+       ) {
+               id->type = "ext4";
+       }
+       else if (es->s_feature_compat & cpu_to_le32(EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+               id->type = "ext3";
+       else
+               id->type = "ext2";
+#endif
        return 0;
 }
diff --git a/util-linux/volume_id/f2fs.c b/util-linux/volume_id/f2fs.c
new file mode 100644 (file)
index 0000000..bf0b662
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2012 S-G Bergh <sgb@systemasis.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_F2FS) += f2fs.o
+
+//config:
+//config:config FEATURE_VOLUMEID_F2FS
+//config:      bool "f2fs filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        F2FS (aka Flash-Friendly File System) is a log-structured file system,
+//config:        which is adapted to newer forms of storage. F2FS also remedies some
+//config:        known issues of the older log structured file systems, such as high
+//config:        cleaning overhead.
+//config:
+
+#include "volume_id_internal.h"
+
+#define F2FS_MAGIC             0xF2F52010      // F2FS Magic Number
+#define F2FS_UUID_SIZE         16
+#define F2FS_LABEL_SIZE                512
+#define F2FS_LABEL_BYTES       1024
+#define F2FS_SB1_OFFSET                0x400           // offset for 1:st super block
+/*
+#define F2FS_SB2_OFFSET                0x1400          // offset for 2:nd super block
+*/
+
+struct f2fs_super_block {      // According to version 1.1
+/* 0x00 */     uint32_t        magic;                  // Magic Number
+/* 0x04 */     uint16_t        major_ver;              // Major Version
+/* 0x06 */     uint16_t        minor_ver;              // Minor Version
+/* 0x08 */     uint32_t        log_sectorsize;         // log2 sector size in bytes
+/* 0x0C */     uint32_t        log_sectors_per_block;  // log2 # of sectors per block
+/* 0x10 */     uint32_t        log_blocksize;          // log2 block size in bytes
+/* 0x14 */     uint32_t        log_blocks_per_seg;     // log2 # of blocks per segment
+/* 0x18 */     uint32_t        segs_per_sec;           // # of segments per section
+/* 0x1C */     uint32_t        secs_per_zone;          // # of sections per zone
+/* 0x20 */     uint32_t        checksum_offset;        // checksum offset inside super block
+/* 0x24 */     uint64_t        block_count;            // total # of user blocks
+/* 0x2C */     uint32_t        section_count;          // total # of sections
+/* 0x30 */     uint32_t        segment_count;          // total # of segments
+/* 0x34 */     uint32_t        segment_count_ckpt;     // # of segments for checkpoint
+/* 0x38 */     uint32_t        segment_count_sit;      // # of segments for SIT
+/* 0x3C */     uint32_t        segment_count_nat;      // # of segments for NAT
+/* 0x40 */     uint32_t        segment_count_ssa;      // # of segments for SSA
+/* 0x44 */     uint32_t        segment_count_main;     // # of segments for main area
+/* 0x48 */     uint32_t        segment0_blkaddr;       // start block address of segment 0
+/* 0x4C */     uint32_t        cp_blkaddr;             // start block address of checkpoint
+/* 0x50 */     uint32_t        sit_blkaddr;            // start block address of SIT
+/* 0x54 */     uint32_t        nat_blkaddr;            // start block address of NAT
+/* 0x58 */     uint32_t        ssa_blkaddr;            // start block address of SSA
+/* 0x5C */     uint32_t        main_blkaddr;           // start block address of main area
+/* 0x60 */     uint32_t        root_ino;               // root inode number
+/* 0x64 */     uint32_t        node_ino;               // node inode number
+/* 0x68 */     uint32_t        meta_ino;               // meta inode number
+/* 0x6C */     uint8_t         uuid[F2FS_UUID_SIZE];   // 128-bit uuid for volume
+/* 0x7C */     uint16_t        volume_name[F2FS_LABEL_SIZE];   // volume name
+// /* 0x47C */ uint32_t        extension_count;        // # of extensions below
+// /* 0x480 */ uint8_t         extension_list[64][8];  // extension array
+} PACKED;
+
+
+int FAST_FUNC volume_id_probe_f2fs(struct volume_id *id /*,uint64_t off*/)
+{
+       struct f2fs_super_block *sb;
+
+       // Go for primary super block (ignore second sb)
+       dbg("f2fs: probing at offset 0x%x", F2FS_SB1_OFFSET);
+       sb = volume_id_get_buffer(id, F2FS_SB1_OFFSET, sizeof(*sb));
+
+       if (!sb)
+               return -1;
+
+       if (sb->magic != cpu_to_le32(F2FS_MAGIC))
+               return -1;
+
+       IF_FEATURE_BLKID_TYPE(id->type = "f2fs");
+
+       // For version 1.0 we don't know sb structure and can't set label/uuid
+       if (sb->major_ver == cpu_to_le16(1) && sb->minor_ver == cpu_to_le16(0))
+               return 0;
+
+       volume_id_set_label_unicode16(id, (uint8_t *)sb->volume_name,
+                                     LE, MIN(F2FS_LABEL_BYTES, VOLUME_ID_LABEL_SIZE));
+
+       volume_id_set_uuid(id, sb->uuid, UUID_DCE);
+
+       return 0;
+}
index b0f427c..476d500 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_FAT) += fat.o
+
+//config:
+//config:config FEATURE_VOLUMEID_FAT
+//config:      bool "fat filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 /* linux/msdos_fs.h says: */
@@ -332,7 +343,7 @@ int FAST_FUNC volume_id_probe_vfat(struct volume_id *id /*,uint64_t fat_partitio
 
  ret:
 //     volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
-//     id->type = "vfat";
+       IF_FEATURE_BLKID_TYPE(id->type = "vfat";)
 
        return 0;
 }
index 4d9f9ec..0c6bdfd 100644 (file)
@@ -5,8 +5,13 @@
  * Copyright (C) 2006 by Jason Schoon <floydpink@gmail.com>
  * Some portions cribbed from e2fsprogs, util-linux, dosfstools
  *
- * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
+
+//kbuild:lib-$(CONFIG_BLKID) += get_devname.o
+//kbuild:lib-$(CONFIG_FINDFS) += get_devname.o
+//kbuild:lib-$(CONFIG_FEATURE_MOUNT_LABEL) += get_devname.o
+
 #include <sys/mount.h> /* BLKGETSIZE64 */
 #if !defined(BLKGETSIZE64)
 # define BLKGETSIZE64 _IOR(0x12,114,size_t)
@@ -19,14 +24,22 @@ static struct uuidCache_s {
        char *device;
        char *label;
        char *uc_uuid; /* prefix makes it easier to grep for */
+       IF_FEATURE_BLKID_TYPE(const char *type;)
 } *uuidCache;
 
+#if !ENABLE_FEATURE_BLKID_TYPE
+#define get_label_uuid(fd, label, uuid, type) \
+       get_label_uuid(fd, label, uuid)
+#define uuidcache_addentry(device, label, uuid, type) \
+       uuidcache_addentry(device, label, uuid)
+#endif
+
 /* Returns !0 on error.
  * Otherwise, returns malloc'ed strings for label and uuid
  * (and they can't be NULL, although they can be "").
  * NB: closes fd. */
 static int
-get_label_uuid(int fd, char **label, char **uuid)
+get_label_uuid(int fd, char **label, char **uuid, const char **type)
 {
        int rv = 1;
        uint64_t size;
@@ -41,10 +54,19 @@ get_label_uuid(int fd, char **label, char **uuid)
        if (volume_id_probe_all(vid, /*0,*/ size) != 0)
                goto ret;
 
-       if (vid->label[0] != '\0' || vid->uuid[0] != '\0') {
+       if (vid->label[0] != '\0' || vid->uuid[0] != '\0'
+#if ENABLE_FEATURE_BLKID_TYPE
+        || vid->type != NULL
+#endif
+       ) {
                *label = xstrndup(vid->label, sizeof(vid->label));
                *uuid  = xstrndup(vid->uuid, sizeof(vid->uuid));
+#if ENABLE_FEATURE_BLKID_TYPE
+               *type = vid->type;
+               dbg("found label '%s', uuid '%s', type '%s'", *label, *uuid, *type);
+#else
                dbg("found label '%s', uuid '%s'", *label, *uuid);
+#endif
                rv = 0;
        }
  ret:
@@ -54,7 +76,7 @@ get_label_uuid(int fd, char **label, char **uuid)
 
 /* NB: we take ownership of (malloc'ed) label and uuid */
 static void
-uuidcache_addentry(char *device, /*int major, int minor,*/ char *label, char *uuid)
+uuidcache_addentry(char *device, /*int major, int minor,*/ char *label, char *uuid, const char *type)
 {
        struct uuidCache_s *last;
 
@@ -72,6 +94,7 @@ uuidcache_addentry(char *device, /*int major, int minor,*/ char *label, char *uu
        last->device = device;
        last->label = label;
        last->uc_uuid = uuid;
+       IF_FEATURE_BLKID_TYPE(last->type = type;)
 }
 
 /* If get_label_uuid() on device_name returns success,
@@ -83,10 +106,6 @@ uuidcache_check_device(const char *device,
                void *userData UNUSED_PARAM,
                int depth UNUSED_PARAM)
 {
-       char *uuid = uuid; /* for compiler */
-       char *label = label;
-       int fd;
-
        /* note: this check rejects links to devices, among other nodes */
        if (!S_ISBLK(statbuf->st_mode))
                return TRUE;
@@ -99,23 +118,17 @@ uuidcache_check_device(const char *device,
        if (major(statbuf->st_rdev) == 2)
                return TRUE;
 
-       fd = open(device, O_RDONLY);
-       if (fd < 0)
-               return TRUE;
+       add_to_uuid_cache(device);
 
-       /* get_label_uuid() closes fd in all cases (success & failure) */
-       if (get_label_uuid(fd, &label, &uuid) == 0) {
-               /* uuidcache_addentry() takes ownership of all three params */
-               uuidcache_addentry(xstrdup(device), /*ma, mi,*/ label, uuid);
-       }
        return TRUE;
 }
 
-static void
-uuidcache_init(void)
+static struct uuidCache_s*
+uuidcache_init(int scan_devices)
 {
+       dbg("DBG: uuidCache=%x, uuidCache");
        if (uuidCache)
-               return;
+               return uuidCache;
 
        /* We were scanning /proc/partitions
         * and /proc/sys/dev/cdrom/info here.
@@ -127,12 +140,14 @@ uuidcache_init(void)
         * This is unacceptably complex. Let's just scan /dev.
         * (Maybe add scanning of /sys/block/XXX/dev for devices
         * somehow not having their /dev/XXX entries created?) */
-
-       recursive_action("/dev", ACTION_RECURSE,
-               uuidcache_check_device, /* file_action */
-               NULL, /* dir_action */
-               NULL, /* userData */
-               0 /* depth */);
+       if (scan_devices)
+               recursive_action("/dev", ACTION_RECURSE,
+                       uuidcache_check_device, /* file_action */
+                       NULL, /* dir_action */
+                       NULL, /* userData */
+                       0 /* depth */);
+
+       return uuidCache;
 }
 
 #define UUID   1
@@ -144,9 +159,7 @@ get_spec_by_x(int n, const char *t, int *majorPtr, int *minorPtr)
 {
        struct uuidCache_s *uc;
 
-       uuidcache_init();
-       uc = uuidCache;
-
+       uc = uuidcache_init(/*scan_devices:*/ 1);
        while (uc) {
                switch (n) {
                case UUID:
@@ -211,31 +224,56 @@ get_spec_by_volume_label(const char *s, int *major, int *minor)
 #endif // UNUSED
 
 /* Used by blkid */
-void display_uuid_cache(void)
+void display_uuid_cache(int scan_devices)
 {
-       struct uuidCache_s *u;
-
-       uuidcache_init();
-       u = uuidCache;
-       while (u) {
-               printf("%s:", u->device);
-               if (u->label[0])
-                       printf(" LABEL=\"%s\"", u->label);
-               if (u->uc_uuid[0])
-                       printf(" UUID=\"%s\"", u->uc_uuid);
+       struct uuidCache_s *uc;
+
+       uc = uuidcache_init(scan_devices);
+       while (uc) {
+               printf("%s:", uc->device);
+               if (uc->label[0])
+                       printf(" LABEL=\"%s\"", uc->label);
+               if (uc->uc_uuid[0])
+                       printf(" UUID=\"%s\"", uc->uc_uuid);
+#if ENABLE_FEATURE_BLKID_TYPE
+       if (uc->type)
+               printf(" TYPE=\"%s\"", uc->type);
+#endif
                bb_putchar('\n');
-               u = u->next;
+               uc = uc->next;
        }
 }
 
+int add_to_uuid_cache(const char *device)
+{
+       char *uuid = uuid; /* for compiler */
+       char *label = label;
+#if ENABLE_FEATURE_BLKID_TYPE
+       const char *type = type;
+#endif
+       int fd;
+
+       fd = open(device, O_RDONLY);
+       if (fd < 0)
+               return 0;
+
+       /* get_label_uuid() closes fd in all cases (success & failure) */
+       if (get_label_uuid(fd, &label, &uuid, &type) == 0) {
+               /* uuidcache_addentry() takes ownership of all four params */
+               uuidcache_addentry(xstrdup(device), /*ma, mi,*/ label, uuid, type);
+               return 1;
+       }
+       return 0;
+}
+
+
 /* Used by mount and findfs */
 
 char *get_devname_from_label(const char *spec)
 {
        struct uuidCache_s *uc;
 
-       uuidcache_init();
-       uc = uuidCache;
+       uc = uuidcache_init(/*scan_devices:*/ 1);
        while (uc) {
                if (uc->label[0] && strcmp(spec, uc->label) == 0) {
                        return xstrdup(uc->device);
@@ -249,8 +287,7 @@ char *get_devname_from_uuid(const char *spec)
 {
        struct uuidCache_s *uc;
 
-       uuidcache_init();
-       uc = uuidCache;
+       uc = uuidcache_init(/*scan_devices:*/ 1);
        while (uc) {
                /* case of hex numbers doesn't matter */
                if (strcasecmp(spec, uc->uc_uuid) == 0) {
index cf75851..8d34aaf 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_HFS) += hfs.o
+
+//config:
+//config:config FEATURE_VOLUMEID_HFS
+//config:      bool "hfs filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct hfs_finder_info{
@@ -131,6 +142,27 @@ struct hfsplus_vol_header {
 #define HFS_NODE_LEAF                  0xff
 #define HFSPLUS_POR_CNID               1
 
+static void FAST_FUNC hfs_set_uuid(struct volume_id *id, const uint8_t *hfs_id)
+{
+#define hfs_id_len 8
+       md5_ctx_t md5c;
+       uint8_t uuid[16];
+       unsigned i;
+
+       for (i = 0; i < hfs_id_len; i++)
+               if (hfs_id[i] != 0)
+                       goto do_md5;
+       return;
+ do_md5:
+       md5_begin(&md5c);
+       md5_hash(&md5c, "\263\342\17\71\362\222\21\326\227\244\0\60\145\103\354\254", 16);
+       md5_hash(&md5c, hfs_id, hfs_id_len);
+       md5_end(&md5c, uuid);
+       uuid[6] = 0x30 | (uuid[6] & 0x0f);
+       uuid[8] = 0x80 | (uuid[8] & 0x3f);
+       volume_id_set_uuid(id, uuid, UUID_DCE);
+}
+
 int FAST_FUNC volume_id_probe_hfs_hfsplus(struct volume_id *id /*,uint64_t off*/)
 {
        uint64_t off = 0;
@@ -193,9 +225,9 @@ int FAST_FUNC volume_id_probe_hfs_hfsplus(struct volume_id *id /*,uint64_t off*/
                volume_id_set_label_string(id, hfs->label, hfs->label_len) ;
        }
 
-       volume_id_set_uuid(id, hfs->finder_info.id, UUID_HFS);
+       hfs_set_uuid(id, hfs->finder_info.id);
 //     volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
-//     id->type = "hfs";
+       IF_FEATURE_BLKID_TYPE(id->type = "hfs";)
 
        return 0;
 
@@ -207,7 +239,7 @@ int FAST_FUNC volume_id_probe_hfs_hfsplus(struct volume_id *id /*,uint64_t off*/
        return -1;
 
  hfsplus:
-       volume_id_set_uuid(id, hfsplus->finder_info.id, UUID_HFS);
+       hfs_set_uuid(id, hfsplus->finder_info.id);
 
        blocksize = be32_to_cpu(hfsplus->blocksize);
        dbg("blocksize %u", blocksize);
@@ -286,7 +318,7 @@ int FAST_FUNC volume_id_probe_hfs_hfsplus(struct volume_id *id /*,uint64_t off*/
 
  found:
 //     volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
-//     id->type = "hfsplus";
+       IF_FEATURE_BLKID_TYPE(id->type = "hfsplus";)
 
        return 0;
 }
index 1519de4..3848de4 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_ISO9660) += iso9660.o
+
+//config:
+//config:config FEATURE_VOLUMEID_ISO9660
+//config:      bool "iso9660 filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 #define ISO_SUPERBLOCK_OFFSET          0x8000
@@ -114,7 +125,7 @@ int FAST_FUNC volume_id_probe_iso9660(struct volume_id *id /*,uint64_t off*/)
 
  found:
 //     volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
-//     id->type = "iso9660";
+       IF_FEATURE_BLKID_TYPE(id->type = "iso9660";)
 
        return 0;
 }
index eb7a448..a6eaff4 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_JFS) += jfs.o
+
+//config:
+//config:config FEATURE_VOLUMEID_JFS
+//config:      bool "jfs filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct jfs_super_block {
@@ -54,7 +65,7 @@ int FAST_FUNC volume_id_probe_jfs(struct volume_id *id /*,uint64_t off*/)
        volume_id_set_uuid(id, js->uuid, UUID_DCE);
 
 //     volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
-//     id->type = "jfs";
+       IF_FEATURE_BLKID_TYPE(id->type = "jfs";)
 
        return 0;
 }
index e1c8636..f20823a 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_LINUXRAID) += linux_raid.o
+
+//config:
+//config:config FEATURE_VOLUMEID_LINUXRAID
+//config:      bool "linuxraid"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct mdp_super_block {
@@ -69,13 +80,13 @@ int FAST_FUNC volume_id_probe_linux_raid(struct volume_id *id /*,uint64_t off*/,
        volume_id_set_uuid(id, uuid, UUID_DCE);
 
 //     snprintf(id->type_version, sizeof(id->type_version)-1, "%u.%u.%u",
-//              le32_to_cpu(mdp->major_version),
-//              le32_to_cpu(mdp->minor_version),
-//              le32_to_cpu(mdp->patch_version));
+//             le32_to_cpu(mdp->major_version),
+//             le32_to_cpu(mdp->minor_version),
+//             le32_to_cpu(mdp->patch_version));
 
        dbg("found raid signature");
 //     volume_id_set_usage(id, VOLUME_ID_RAID);
-//     id->type = "linux_raid_member";
+       IF_FEATURE_BLKID_TYPE(id->type = "linux_raid_member";)
 
        return 0;
 }
index 5743546..39470d4 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_LINUXSWAP) += linux_swap.o
+
+//config:
+//config:config FEATURE_VOLUMEID_LINUXSWAP
+//config:      bool "linux swap filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct swap_header_v1_2 {
@@ -72,7 +83,7 @@ int FAST_FUNC volume_id_probe_linux_swap(struct volume_id *id /*,uint64_t off*/)
 
 found:
 //     volume_id_set_usage(id, VOLUME_ID_OTHER);
-//     id->type = "swap";
+       IF_FEATURE_BLKID_TYPE(id->type = "swap";)
 
        return 0;
 }
index 8ab09e3..42bf876 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_LUKS) += luks.o
+
+//config:
+//config:config FEATURE_VOLUMEID_LUKS
+//config:      bool "luks filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 #define LUKS_MAGIC_L             6
@@ -94,7 +105,7 @@ int FAST_FUNC volume_id_probe_luks(struct volume_id *id /*,uint64_t off*/)
 
 //     volume_id_set_usage(id, VOLUME_ID_CRYPTO);
        volume_id_set_uuid(id, header->uuid, UUID_DCE_STRING);
-//     id->type = "crypto_LUKS";
+       IF_FEATURE_BLKID_TYPE(id->type = "crypto_LUKS";)
 
        return 0;
 }
diff --git a/util-linux/volume_id/nilfs.c b/util-linux/volume_id/nilfs.c
new file mode 100644 (file)
index 0000000..f3a9ef5
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2012 S-G Bergh <sgb@systemasis.org>
+ *
+ *     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_NILFS) += nilfs.o
+
+//config:
+//config:config FEATURE_VOLUMEID_NILFS
+//config:      bool "nilfs filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        NILFS is a New Implementation of a Log-Structured File System (LFS)
+//config:        that supports continuous snapshots. This provides features like
+//config:        versioning of the entire filesystem, restoration of files that
+//config:        were deleted a few minutes ago. NILFS keeps consistency like
+//config:        conventional LFS, so it provides quick recovery after system crashes.
+//config:
+//config:        The possible use of NILFS includes versioning, tamper detection,
+//config:        SOX compliance logging, and so forth. It can serve as an alternative
+//config:        filesystem for Linux desktop environment, or as a basis of advanced
+//config:        storage appliances.
+//config:
+
+#include "volume_id_internal.h"
+
+#define NILFS_UUID_SIZE 16
+#define NILFS_LABEL_SIZE 80
+#define NILFS_SB1_OFFSET 0x400
+#define NILFS_SB2_OFFSET 0x1000
+#define NILFS_MAGIC 0x3434
+
+struct nilfs2_super_block {
+/* 0x00 */     uint32_t s_rev_level;                           // Major revision level.
+/* 0x04 */     uint16_t s_minor_rev_level;                     // Minor revision level.
+/* 0x06 */     uint16_t s_magic;                               // Magic signature.
+/* 0x08 */     uint16_t s_bytes;
+/* 0x0A */     uint16_t s_flags;
+/* 0x0C */     uint32_t s_crc_seed;
+/* 0x10 */     uint32_t s_sum;
+/* 0x14 */     uint32_t s_log_block_size;
+/* 0x18 */     uint64_t s_nsegments;
+/* 0x20 */     uint64_t s_dev_size;                            // Block device size in bytes.
+/* 0x28 */     uint64_t s_first_data_block;
+/* 0x30 */     uint32_t s_blocks_per_segment;
+/* 0x34 */     uint32_t s_r_segments_percentage;
+/* 0x38 */     uint64_t s_last_cno;
+/* 0x40 */     uint64_t s_last_pseg;
+/* 0x48 */     uint64_t s_last_seq;
+/* 0x50 */     uint64_t s_free_blocks_count;
+/* 0x58 */     uint64_t s_ctime;
+/* 0x60 */     uint64_t s_mtime;
+/* 0x68 */     uint64_t s_wtime;
+/* 0x70 */     uint16_t s_mnt_count;
+/* 0x72 */     uint16_t s_max_mnt_count;
+/* 0x74 */     uint16_t s_state;
+/* 0x76 */     uint16_t s_errors;
+/* 0x78 */     uint64_t s_lastcheck;
+/* 0x80 */     uint32_t s_checkinterval;
+/* 0x84 */     uint32_t s_creator_os;
+/* 0x88 */     uint16_t s_def_resuid;
+/* 0x8A */     uint16_t s_def_resgid;
+/* 0x8C */     uint32_t s_first_ino;
+/* 0x90 */     uint16_t s_inode_size;
+/* 0x92 */     uint16_t s_dat_entry_size;
+/* 0x94 */     uint16_t s_checkpoint_size;
+/* 0x96 */     uint16_t s_segment_usage_size;
+/* 0x98 */     uint8_t s_uuid[NILFS_UUID_SIZE];                // 128-bit UUID for volume.
+/* 0xA8 */     uint8_t s_volume_name[NILFS_LABEL_SIZE];        // Volume label.
+/* 0xF8 */     // ...
+} PACKED;
+
+int FAST_FUNC volume_id_probe_nilfs(struct volume_id *id /*,uint64_t off*/)
+{
+       struct nilfs2_super_block *sb;
+
+       // Primary super block
+       dbg("nilfs: probing at offset 0x%x", NILFS_SB1_OFFSET);
+
+       sb = volume_id_get_buffer(id, NILFS_SB1_OFFSET, sizeof(*sb));
+
+       if (sb == NULL)
+               return -1;
+
+       if (sb->s_magic != NILFS_MAGIC)
+               return -1;
+
+       // The secondary superblock is not always used, so ignore it for now.
+       // When used it is at 4K from the end of the partition (sb->s_dev_size - NILFS_SB2_OFFSET).
+
+       volume_id_set_label_string(id, sb->s_volume_name, NILFS_LABEL_SIZE < VOLUME_ID_LABEL_SIZE ?
+                               NILFS_LABEL_SIZE : VOLUME_ID_LABEL_SIZE);
+       volume_id_set_uuid(id, sb->s_uuid, UUID_DCE);
+
+       if (sb->s_rev_level == 2)
+               IF_FEATURE_BLKID_TYPE(id->type = "nilfs2");
+
+       return 0;
+}
index 17b1fe8..46f687a 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_NTFS) += ntfs.o
+
+//config:
+//config:config FEATURE_VOLUMEID_NTFS
+//config:      bool "ntfs filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct ntfs_super_block {
@@ -132,7 +143,7 @@ int FAST_FUNC volume_id_probe_ntfs(struct volume_id *id /*,uint64_t off*/)
        dbg("mft record size  %i", mft_record_size);
 
        buf = volume_id_get_buffer(id, off + mft_off + (MFT_RECORD_VOLUME * mft_record_size),
-                        mft_record_size);
+                       mft_record_size);
        if (buf == NULL)
                goto found;
 
@@ -150,7 +161,7 @@ int FAST_FUNC volume_id_probe_ntfs(struct volume_id *id /*,uint64_t off*/)
 
                attr = (struct file_attribute*) &buf[attr_off];
                attr_type = le32_to_cpu(attr->type);
-               attr_len = le16_to_cpu(attr->len);
+               attr_len = le32_to_cpu(attr->len);
                val_off = le16_to_cpu(attr->value_offset);
                val_len = le32_to_cpu(attr->value_len);
                attr_off += attr_len;
@@ -165,7 +176,7 @@ int FAST_FUNC volume_id_probe_ntfs(struct volume_id *id /*,uint64_t off*/)
                        break;
 
                dbg("found attribute type 0x%x, len %i, at offset %i",
-                   attr_type, attr_len, attr_off);
+                       attr_type, attr_len, attr_off);
 
 //             if (attr_type == MFT_RECORD_ATTR_VOLUME_INFO) {
 //                     struct volume_info *info;
@@ -188,7 +199,7 @@ int FAST_FUNC volume_id_probe_ntfs(struct volume_id *id /*,uint64_t off*/)
 
  found:
 //     volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
-//     id->type = "ntfs";
+       IF_FEATURE_BLKID_TYPE(id->type = "ntfs";)
 
        return 0;
 }
index e6c4559..415e0bf 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_OCFS2) += ocfs2.o
+
+//config:
+//config:config FEATURE_VOLUMEID_OCFS2
+//config:      bool "ocfs2 filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 /* All these values are taken from ocfs2-tools's ocfs2_fs.h */
@@ -101,6 +112,6 @@ int FAST_FUNC volume_id_probe_ocfs2(struct volume_id *id /*,uint64_t off*/)
        volume_id_set_label_string(id, os->s_label, OCFS2_MAX_VOL_LABEL_LEN < VOLUME_ID_LABEL_SIZE ?
                                        OCFS2_MAX_VOL_LABEL_LEN : VOLUME_ID_LABEL_SIZE);
        volume_id_set_uuid(id, os->s_uuid, UUID_DCE);
-//     id->type = "ocfs2";
+       IF_FEATURE_BLKID_TYPE(id->type = "ocfs2";)
        return 0;
 }
index 3120b29..24979fb 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_REISERFS) += reiserfs.o
+
+//config:
+//config:config FEATURE_VOLUMEID_REISERFS
+//config:      bool "Reiser filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct reiserfs_super_block {
@@ -107,7 +118,7 @@ int FAST_FUNC volume_id_probe_reiserfs(struct volume_id *id /*,uint64_t off*/)
 
  found:
 //     volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
-//     id->type = "reiserfs";
+       IF_FEATURE_BLKID_TYPE(id->type = "reiserfs";)
 
        return 0;
 }
index 228e77a..4754fdb 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_ROMFS) += romfs.o
+
+//config:
+//config:config FEATURE_VOLUMEID_ROMFS
+//config:      bool "romfs filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct romfs_super {
@@ -47,7 +58,7 @@ int FAST_FUNC volume_id_probe_romfs(struct volume_id *id /*,uint64_t off*/)
                }
 
 //             volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
-//             id->type = "romfs";
+               IF_FEATURE_BLKID_TYPE(id->type = "romfs";)
                return 0;
        }
 
diff --git a/util-linux/volume_id/squashfs.c b/util-linux/volume_id/squashfs.c
new file mode 100644 (file)
index 0000000..079b6cc
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2012 S-G Bergh <sgb@systemasis.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_SQUASHFS) += squashfs.o
+
+//config:
+//config:config FEATURE_VOLUMEID_SQUASHFS
+//config:      bool "SquashFS filesystem"
+//config:      default y
+//config:      depends on VOLUMEID && FEATURE_BLKID_TYPE
+//config:      help
+//config:        Squashfs is a compressed read-only filesystem for Linux. Squashfs is
+//config:        intended for general read-only filesystem use and in constrained block
+//config:        device/memory systems (e.g. embedded systems) where low overhead is
+//config:        needed.
+//config:
+
+#include "volume_id_internal.h"
+
+struct squashfs_superblock {
+       uint32_t        magic;
+/*
+       uint32_t        dummy[6];
+       uint16_t        major;
+       uint16_t        minor;
+*/
+} PACKED;
+
+int FAST_FUNC volume_id_probe_squashfs(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+       struct squashfs_superblock *sb;
+
+       dbg("SquashFS: probing at offset 0x%llx", (unsigned long long) off);
+       sb = volume_id_get_buffer(id, off, 0x200);
+       if (!sb)
+               return -1;
+
+       // Old SquashFS (pre 4.0) can be both big and little endian, so test for both.
+       // Likewise, it is commonly used in firwmare with some non-standard signatures.
+#define pack(a,b,c,d) ( (uint32_t)((a * 256 + b) * 256 + c) * 256 + d )
+#define SIG1 pack('s','q','s','h')
+#define SIG2 pack('h','s','q','s')
+#define SIG3 pack('s','h','s','q')
+#define SIG4 pack('q','s','h','s')
+       if (sb->magic == SIG1
+        || sb->magic == SIG2
+        || sb->magic == SIG3
+        || sb->magic == SIG4
+       ) {
+               IF_FEATURE_BLKID_TYPE(id->type = "squashfs";)
+               return 0;
+       }
+
+       return -1;
+}
index e0fa20a..7b4b536 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_SYSV) += sysv.o
+
+//config:
+//config:config FEATURE_VOLUMEID_SYSV
+//config:      bool "sysv filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 #define SYSV_NICINOD                   100
@@ -99,7 +110,7 @@ int FAST_FUNC volume_id_probe_sysv(struct volume_id *id /*,uint64_t off*/)
                if (vs->s_magic == cpu_to_le32(SYSV_MAGIC) || vs->s_magic == cpu_to_be32(SYSV_MAGIC)) {
 //                     volume_id_set_label_raw(id, vs->s_fname, 6);
                        volume_id_set_label_string(id, vs->s_fname, 6);
-//                     id->type = "sysv";
+                       IF_FEATURE_BLKID_TYPE(id->type = "sysv");
                        goto found;
                }
        }
@@ -112,7 +123,7 @@ int FAST_FUNC volume_id_probe_sysv(struct volume_id *id /*,uint64_t off*/)
                if (xs->s_magic == cpu_to_le32(XENIX_MAGIC) || xs->s_magic == cpu_to_be32(XENIX_MAGIC)) {
 //                     volume_id_set_label_raw(id, xs->s_fname, 6);
                        volume_id_set_label_string(id, xs->s_fname, 6);
-//                     id->type = "xenix";
+                       IF_FEATURE_BLKID_TYPE(id->type = "xenix";)
                        goto found;
                }
        }
index dd25731..9214545 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_UDF) += udf.o
+
+//config:
+//config:config FEATURE_VOLUMEID_UDF
+//config:      bool "udf filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct volume_descriptor {
@@ -109,7 +120,7 @@ nsr:
                        return -1;
 
                dbg("vsd: %c%c%c%c%c",
-                   vsd->id[0], vsd->id[1], vsd->id[2], vsd->id[3], vsd->id[4]);
+                       vsd->id[0], vsd->id[1], vsd->id[2], vsd->id[3], vsd->id[4]);
 
                if (vsd->id[0] == '\0')
                        return -1;
@@ -167,7 +178,6 @@ anchor:
 
  found:
 //     volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
-//     id->type = "udf";
-
+       IF_FEATURE_BLKID_TYPE(id->type = "udf";)
        return 0;
 }
index 17b7b32..7231a1d 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_HIGHPOINTRAID) += highpoint.o
+
+//config:
+//config:### config FEATURE_VOLUMEID_HIGHPOINTRAID
+//config:###   bool "highpoint raid"
+//config:###   default y
+//config:###   depends on VOLUMEID
+//config:###   help
+//config:###     TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct hpt37x_meta {
index 4429524..a87c89f 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_HPFS) += hpfs.o
+
+//config:
+//config:### config FEATURE_VOLUMEID_HPFS
+//config:###   bool "hpfs filesystem"
+//config:###   default y
+//config:###   depends on VOLUMEID
+//config:###   help
+//config:###     TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct hpfs_super {
index 7ab47b3..851bd2f 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_ISWRAID) += isw_raid.o
+
+//config:
+//config:### config FEATURE_VOLUMEID_ISWRAID
+//config:###   bool "intel raid"
+//config:###   default y
+//config:###   depends on VOLUMEID
+//config:###   help
+//config:###     TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct isw_meta {
index e6cc8ed..52d68de 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_LSIRAID) += lsi_raid.o
+
+//config:
+//config:### config FEATURE_VOLUMEID_LSIRAID
+//config:###   bool "lsi raid"
+//config:###   default y
+//config:###   depends on VOLUMEID
+//config:###   help
+//config:###     TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct lsi_meta {
index 2206498..08fa052 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_LVM) += lvm.o
+
+//config:
+//config:### config FEATURE_VOLUMEID_LVM
+//config:###   bool "lvm"
+//config:###   default y
+//config:###   depends on VOLUMEID
+//config:###   help
+//config:###     TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct lvm1_super_block {
index e8deb97..a1a53d1 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_MAC) += mac.o
+
+//config:
+//config:### config FEATURE_VOLUMEID_MAC
+//config:###   bool "mac filesystem"
+//config:###   default y
+//config:###   depends on VOLUMEID
+//config:###   help
+//config:###     TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct mac_driver_desc {
index a3e1077..50afd5c 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_MINIX) += minix.o
+
+//config:
+//config:### config FEATURE_VOLUMEID_MINIX
+//config:###   bool "minix filesystem"
+//config:###   default y
+//config:###   depends on VOLUMEID
+//config:###   help
+//config:###     TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct minix_super_block {
index 65fb885..5ebaa3e 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_MSDOS) += msdos.o
+
+//config:
+//config:### config FEATURE_VOLUMEID_MSDOS
+//config:###   bool "msdos filesystem"
+//config:###   default y
+//config:###   depends on VOLUMEID
+//config:###   help
+//config:###     TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct msdos_partition_entry {
@@ -109,7 +120,7 @@ int FAST_FUNC volume_id_probe_msdos_part_table(struct volume_id *id, uint64_t of
                                extended = off + poff;
                } else {
                        dbg("found 0x%x data partition at 0x%llx, len 0x%llx",
-                           part[i].sys_ind, (unsigned long long) poff, (unsigned long long) plen);
+                               part[i].sys_ind, (unsigned long long) poff, (unsigned long long) plen);
 
 //                     if (is_raid(part[i].sys_ind))
 //                             volume_id_set_usage_part(p, VOLUME_ID_RAID);
@@ -165,7 +176,7 @@ int FAST_FUNC volume_id_probe_msdos_part_table(struct volume_id *id, uint64_t of
                                if (id->partition_count < 4)
                                        id->partition_count = 4;
 
-                               p = &id->partitions[id->partition_count];
+//                             p = &id->partitions[id->partition_count];
 
 //                             if (is_raid(part[i].sys_ind))
 //                                     volume_id_set_usage_part(p, VOLUME_ID_RAID);
index 9e84729..d99a108 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_NVIDIARAID) += nvidia_raid.o
+
+//config:
+//config:### config FEATURE_VOLUMEID_NVIDIARAID
+//config:###   bool "nvidia raid"
+//config:###   default y
+//config:###   depends on VOLUMEID
+//config:###   help
+//config:###     TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct nvidia_meta {
index 0b0d006..cebebe3 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_PROMISERAID) += promise_raid.o
+
+//config:
+//config:### config FEATURE_VOLUMEID_PROMISERAID
+//config:###   bool "promise raid"
+//config:###   default y
+//config:###   depends on VOLUMEID
+//config:###   help
+//config:###     TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct promise_meta {
index d1c439e..40c8faa 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_SILICONRAID) += silicon_raid.o
+
+//config:
+//config:### config FEATURE_VOLUMEID_SILICONRAID
+//config:###   bool "silicon raid"
+//config:###   default y
+//config:###   depends on VOLUMEID
+//config:###   help
+//config:###     TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct silicon_meta {
@@ -62,7 +73,7 @@ int FAST_FUNC volume_id_probe_silicon_medley_raid(struct volume_id *id, uint64_t
 
 //     volume_id_set_usage(id, VOLUME_ID_RAID);
 //     snprintf(id->type_version, sizeof(id->type_version)-1, "%u.%u",
-//              le16_to_cpu(sil->major_ver), le16_to_cpu(sil->minor_ver));
+//             le16_to_cpu(sil->major_ver), le16_to_cpu(sil->minor_ver));
 //     id->type = "silicon_medley_raid_member";
 
        return 0;
index 9f925d9..d33c10f 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_UFS) += ufs.o
+
+//config:
+//config:### config FEATURE_VOLUMEID_UFS
+//config:###   bool "ufs filesystem"
+//config:###   default y
+//config:###   depends on VOLUMEID
+//config:###   help
+//config:###     TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct ufs_super_block {
index a11eec1..258f93a 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_VIARAID) += via_raid.o
+
+//config:
+//config:### config FEATURE_VOLUMEID_VIARAID
+//config:###   bool "via raid"
+//config:###   default y
+//config:###   depends on VOLUMEID
+//config:###   help
+//config:###     TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct via_meta {
index dd75c7b..061545f 100644 (file)
@@ -31,25 +31,29 @@ void volume_id_set_unicode16(char *str, size_t len, const uint8_t *buf, enum end
                        c = (buf[i+1] << 8) | buf[i];
                else
                        c = (buf[i] << 8) | buf[i+1];
-               if (c == 0) {
-                       str[j] = '\0';
+               if (c == 0)
                        break;
-               } else if (c < 0x80) {
-                       if (j+1 >= len)
-                               break;
-                       str[j++] = (uint8_t) c;
-               } else if (c < 0x800) {
-                       if (j+2 >= len)
-                               break;
-                       str[j++] = (uint8_t) (0xc0 | (c >> 6));
-                       str[j++] = (uint8_t) (0x80 | (c & 0x3f));
+               if (j+1 >= len)
+                       break;
+               if (c < 0x80) {
+                       /* 0xxxxxxx */
                } else {
-                       if (j+3 >= len)
+                       uint8_t topbits = 0xc0;
+                       if (j+2 >= len)
                                break;
-                       str[j++] = (uint8_t) (0xe0 | (c >> 12));
-                       str[j++] = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
-                       str[j++] = (uint8_t) (0x80 | (c & 0x3f));
+                       if (c < 0x800) {
+                               /* 110yyyxx 10xxxxxx */
+                       } else {
+                               if (j+3 >= len)
+                                       break;
+                               /* 1110yyyy 10yyyyxx 10xxxxxx */
+                               str[j++] = (uint8_t) (0xe0 | (c >> 12));
+                               topbits = 0x80;
+                       }
+                       str[j++] = (uint8_t) (topbits | ((c >> 6) & 0x3f));
+                       c = 0x80 | (c & 0x3f);
                }
+               str[j++] = (uint8_t) c;
        }
        str[j] = '\0';
 }
@@ -125,30 +129,14 @@ void volume_id_set_label_string(struct volume_id *id, const uint8_t *buf, size_t
 
 void volume_id_set_label_unicode16(struct volume_id *id, const uint8_t *buf, enum endian endianess, size_t count)
 {
-        volume_id_set_unicode16(id->label, sizeof(id->label), buf, endianess, count);
+       volume_id_set_unicode16(id->label, sizeof(id->label), buf, endianess, count);
 }
 
 void volume_id_set_uuid(struct volume_id *id, const uint8_t *buf, enum uuid_format format)
 {
        unsigned i;
-       unsigned count = 0;
+       unsigned count = (format == UUID_DCE_STRING ? VOLUME_ID_UUID_SIZE : 4 << format);
 
-       switch (format) {
-       case UUID_DOS:
-               count = 4;
-               break;
-       case UUID_NTFS:
-       case UUID_HFS:
-               count = 8;
-               break;
-       case UUID_DCE:
-               count = 16;
-               break;
-       case UUID_DCE_STRING:
-               /* 36 is ok, id->uuid has one extra byte for NUL */
-               count = VOLUME_ID_UUID_SIZE;
-               break;
-       }
 //     memcpy(id->uuid_raw, buf, count);
 //     id->uuid_raw_len = count;
 
@@ -169,11 +157,6 @@ set:
                        buf[7], buf[6], buf[5], buf[4],
                        buf[3], buf[2], buf[1], buf[0]);
                break;
-       case UUID_HFS:
-               sprintf(id->uuid, "%02X%02X%02X%02X%02X%02X%02X%02X",
-                       buf[0], buf[1], buf[2], buf[3],
-                       buf[4], buf[5], buf[6], buf[7]);
-               break;
        case UUID_DCE:
                sprintf(id->uuid,
                        "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
index c8cf946..5c459a0 100644 (file)
@@ -18,6 +18,8 @@
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_VOLUMEID) += volume_id.o util.o
+
 #include "volume_id_internal.h"
 
 
@@ -93,9 +95,15 @@ static const probe_fptr fs1[] = {
 #if ENABLE_FEATURE_VOLUMEID_FAT
        volume_id_probe_vfat,
 #endif
+#if ENABLE_FEATURE_VOLUMEID_EXFAT
+       volume_id_probe_exfat,
+#endif
 #if ENABLE_FEATURE_VOLUMEID_MAC
        volume_id_probe_mac_partition_map,
 #endif
+#if ENABLE_FEATURE_VOLUMEID_SQUASHFS
+       volume_id_probe_squashfs,
+#endif
 #if ENABLE_FEATURE_VOLUMEID_XFS
        volume_id_probe_xfs,
 #endif
@@ -130,6 +138,12 @@ static const probe_fptr fs2[] = {
 #if ENABLE_FEATURE_VOLUMEID_UFS
        volume_id_probe_ufs,
 #endif
+#if ENABLE_FEATURE_VOLUMEID_F2FS
+       volume_id_probe_f2fs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_NILFS
+       volume_id_probe_nilfs,
+#endif
 #if ENABLE_FEATURE_VOLUMEID_NTFS
        volume_id_probe_ntfs,
 #endif
@@ -195,7 +209,6 @@ int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64
  ret:
        volume_id_free_buffer(id);
        return (- id->error); /* 0 or -1 */
-
 }
 
 /* open volume by device node */
index 9b808ff..6e2dbd7 100644 (file)
@@ -80,7 +80,9 @@ struct volume_id {
 //     char            type_version[VOLUME_ID_FORMAT_SIZE];
 //     smallint        usage_id;
 //     const char      *usage;
-//     const char      *type;
+#if ENABLE_FEATURE_BLKID_TYPE
+       const char      *type;
+#endif
 };
 
 struct volume_id* FAST_FUNC volume_id_open_node(int fd);
@@ -94,52 +96,35 @@ void FAST_FUNC free_volume_id(struct volume_id *id);
 /* size of seek buffer, FAT cluster is 32k max */
 #define SEEK_BUFFER_SIZE                       0x10000
 
-#define bswap16(x) (uint16_t)  ( \
-                               (((uint16_t)(x) & 0x00ffu) << 8) | \
-                               (((uint16_t)(x) & 0xff00u) >> 8))
-
-#define bswap32(x) (uint32_t)  ( \
-                               (((uint32_t)(x) & 0xff000000u) >> 24) | \
-                               (((uint32_t)(x) & 0x00ff0000u) >>  8) | \
-                               (((uint32_t)(x) & 0x0000ff00u) <<  8) | \
-                               (((uint32_t)(x) & 0x000000ffu) << 24))
-
-#define bswap64(x) (uint64_t)  ( \
-                               (((uint64_t)(x) & 0xff00000000000000ull) >> 56) | \
-                               (((uint64_t)(x) & 0x00ff000000000000ull) >> 40) | \
-                               (((uint64_t)(x) & 0x0000ff0000000000ull) >> 24) | \
-                               (((uint64_t)(x) & 0x000000ff00000000ull) >>  8) | \
-                               (((uint64_t)(x) & 0x00000000ff000000ull) <<  8) | \
-                               (((uint64_t)(x) & 0x0000000000ff0000ull) << 24) | \
-                               (((uint64_t)(x) & 0x000000000000ff00ull) << 40) | \
-                               (((uint64_t)(x) & 0x00000000000000ffull) << 56))
-
 #if BB_LITTLE_ENDIAN
-#define le16_to_cpu(x) (x)
-#define le32_to_cpu(x) (x)
-#define le64_to_cpu(x) (x)
-#define be16_to_cpu(x) bswap16(x)
-#define be32_to_cpu(x) bswap32(x)
-#define cpu_to_le16(x) (x)
-#define cpu_to_le32(x) (x)
-#define cpu_to_be32(x) bswap32(x)
+# define le16_to_cpu(x) (uint16_t)(x)
+# define le32_to_cpu(x) (uint32_t)(x)
+# define le64_to_cpu(x) (uint64_t)(x)
+# define be16_to_cpu(x) (uint16_t)(bswap_16(x))
+# define be32_to_cpu(x) (uint32_t)(bswap_32(x))
+# define cpu_to_le16(x) (uint16_t)(x)
+# define cpu_to_le32(x) (uint32_t)(x)
+# define cpu_to_be32(x) (uint32_t)(bswap_32(x))
 #else
-#define le16_to_cpu(x) bswap16(x)
-#define le32_to_cpu(x) bswap32(x)
-#define le64_to_cpu(x) bswap64(x)
-#define be16_to_cpu(x) (x)
-#define be32_to_cpu(x) (x)
-#define cpu_to_le16(x) bswap16(x)
-#define cpu_to_le32(x) bswap32(x)
-#define cpu_to_be32(x) (x)
+# define le16_to_cpu(x) (uint16_t)(bswap_16(x))
+# define le32_to_cpu(x) (uint32_t)(bswap_32(x))
+# define le64_to_cpu(x) (uint64_t)(bb_bswap_64(x))
+# define be16_to_cpu(x) (uint16_t)(x)
+# define be32_to_cpu(x) (uint32_t)(x)
+# define cpu_to_le16(x) (uint16_t)(bswap_16(x))
+# define cpu_to_le32(x) (uint32_t)(bswap_32(x))
+# define cpu_to_be32(x) (uint32_t)(x)
 #endif
 
+/* volume_id_set_uuid(id,buf,fmt) assumes size of uuid buf
+ * by shifting: 4 << fmt, except for fmt == UUID_DCE_STRING.
+ * The constants below should match sizes.
+ */
 enum uuid_format {
-       UUID_DCE_STRING,
-       UUID_DCE,
-       UUID_DOS,
-       UUID_NTFS,
-       UUID_HFS,
+       UUID_DOS = 0,           /* 4 bytes */
+       UUID_NTFS = 1,          /* 8 bytes */
+       UUID_DCE = 2,           /* 16 bytes */
+       UUID_DCE_STRING = 3,    /* 36 bytes (VOLUME_ID_UUID_SIZE) */
 };
 
 enum endian {
@@ -210,14 +195,22 @@ int FAST_FUNC volume_id_probe_luks(struct volume_id *id /*,uint64_t off*/);
 
 //int FAST_FUNC volume_id_probe_msdos_part_table(struct volume_id *id /*,uint64_t off*/);
 
+int FAST_FUNC volume_id_probe_f2fs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_nilfs(struct volume_id *id /*,uint64_t off*/);
+
 int FAST_FUNC volume_id_probe_ntfs(struct volume_id *id /*,uint64_t off*/);
 
+int FAST_FUNC volume_id_probe_exfat(struct volume_id *id /*,uint64_t off*/);
+
 int FAST_FUNC volume_id_probe_ocfs2(struct volume_id *id /*,uint64_t off*/);
 
 int FAST_FUNC volume_id_probe_reiserfs(struct volume_id *id /*,uint64_t off*/);
 
 int FAST_FUNC volume_id_probe_romfs(struct volume_id *id /*,uint64_t off*/);
 
+int FAST_FUNC volume_id_probe_squashfs(struct volume_id *id /*,uint64_t off*/);
+
 int FAST_FUNC volume_id_probe_sysv(struct volume_id *id /*,uint64_t off*/);
 
 int FAST_FUNC volume_id_probe_udf(struct volume_id *id /*,uint64_t off*/);
index 1017d07..5eefc20 100644 (file)
  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_XFS) += xfs.o
+
+//config:
+//config:config FEATURE_VOLUMEID_XFS
+//config:      bool "xfs filesystem"
+//config:      default y
+//config:      depends on VOLUMEID
+//config:      help
+//config:        TODO
+//config:
+
 #include "volume_id_internal.h"
 
 struct xfs_super_block {
@@ -54,7 +65,7 @@ int FAST_FUNC volume_id_probe_xfs(struct volume_id *id /*,uint64_t off*/)
        volume_id_set_uuid(id, xs->uuid, UUID_DCE);
 
 //     volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
-//     id->type = "xfs";
+       IF_FEATURE_BLKID_TYPE(id->type = "xfs";)
 
        return 0;
 }